chromium/chrome/browser/autofill/android/junit/src/org/chromium/chrome/browser/autofill/editors/AddressEditorTest.java

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser.autofill.editors;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.isEmptyString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import static org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.UserFlow.MIGRATE_EXISTING_ADDRESS_PROFILE;
import static org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.UserFlow.SAVE_NEW_ADDRESS_PROFILE;
import static org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.UserFlow.UPDATE_EXISTING_ADDRESS_PROFILE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.CANCEL_RUNNABLE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.CUSTOM_DONE_BUTTON_TEXT;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.DELETE_CONFIRMATION_TEXT;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.DELETE_CONFIRMATION_TITLE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.DONE_RUNNABLE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.DropdownFieldProperties.DROPDOWN_KEY_VALUE_LIST;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.EDITOR_FIELDS;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FOOTER_MESSAGE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.ERROR_MESSAGE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.IS_REQUIRED;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.LABEL;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldProperties.VALUE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.ItemType.DROPDOWN;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.ItemType.TEXT_INPUT;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.SHOW_REQUIRED_INDICATOR;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.TextFieldProperties.TEXT_FIELD_TYPE;
import static org.chromium.chrome.browser.autofill.editors.EditorProperties.setDropdownKey;

import android.app.Activity;

import androidx.annotation.Nullable;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.SmallTest;

import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.AutofillAddress;
import org.chromium.chrome.browser.autofill.AutofillProfileBridge;
import org.chromium.chrome.browser.autofill.AutofillProfileBridge.AutofillAddressUiComponent;
import org.chromium.chrome.browser.autofill.AutofillProfileBridgeJni;
import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.autofill.PersonalDataManagerFactory;
import org.chromium.chrome.browser.autofill.PhoneNumberUtil;
import org.chromium.chrome.browser.autofill.PhoneNumberUtilJni;
import org.chromium.chrome.browser.autofill.editors.AddressEditorCoordinator.Delegate;
import org.chromium.chrome.browser.autofill.editors.EditorProperties.DropdownKeyValue;
import org.chromium.chrome.browser.autofill.editors.EditorProperties.FieldItem;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.sync.SyncServiceFactory;
import org.chromium.components.autofill.AutofillProfile;
import org.chromium.components.autofill.FieldType;
import org.chromium.components.autofill.RecordType;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.sync.SyncService;
import org.chromium.components.sync.UserSelectableType;
import org.chromium.ui.base.TestActivity;
import org.chromium.ui.modelutil.ListModel;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

/** Unit tests for autofill address editor. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@EnableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT})
public class AddressEditorTest {
    private static final String USER_EMAIL = "[email protected]";
    private static final Locale DEFAULT_LOCALE = Locale.getDefault();
    private static final List<AutofillAddressUiComponent> SUPPORTED_ADDRESS_FIELDS =
            List.of(
                    new AutofillAddressUiComponent(
                            FieldType.NAME_FULL, "full name label", true, true),
                    new AutofillAddressUiComponent(
                            FieldType.ADDRESS_HOME_STATE, "admin area label", false, true),
                    new AutofillAddressUiComponent(
                            FieldType.ADDRESS_HOME_CITY, "locality label", true, false),
                    new AutofillAddressUiComponent(
                            FieldType.ADDRESS_HOME_DEPENDENT_LOCALITY,
                            "dependent locality label",
                            true,
                            false),
                    new AutofillAddressUiComponent(
                            FieldType.COMPANY_NAME, "organization label", false, true),
                    new AutofillAddressUiComponent(
                            FieldType.ADDRESS_HOME_SORTING_CODE,
                            "sorting code label",
                            false,
                            false),
                    new AutofillAddressUiComponent(
                            FieldType.ADDRESS_HOME_ZIP, "postal code label", true, false),
                    new AutofillAddressUiComponent(
                            FieldType.ADDRESS_HOME_STREET_ADDRESS,
                            "street address label",
                            true,
                            true));

    private static final AutofillProfile sLocalProfile =
            AutofillProfile.builder()
                    .setFullName("Seb Doe")
                    .setCompanyName("Google")
                    .setStreetAddress("111 First St")
                    .setRegion("CA")
                    .setLocality("Los Angeles")
                    .setPostalCode("90291")
                    .setCountryCode("US")
                    .setPhoneNumber("650-253-0000")
                    .setEmailAddress("[email protected]")
                    .setLanguageCode("en-US")
                    .build();
    private static final AutofillProfile sAccountProfile =
            AutofillProfile.builder()
                    .setRecordType(RecordType.ACCOUNT)
                    .setFullName("Seb Doe")
                    .setCompanyName("Google")
                    .setStreetAddress("111 First St")
                    .setRegion("CA")
                    .setLocality("Los Angeles")
                    .setPostalCode("90291")
                    .setCountryCode("US")
                    .setPhoneNumber("650-253-0000")
                    .setEmailAddress("[email protected]")
                    .setLanguageCode("en-US")
                    .build();

    @Rule public JniMocker mJniMocker = new JniMocker();

    @Rule
    public ActivityScenarioRule<TestActivity> mActivityScenarioRule =
            new ActivityScenarioRule<>(TestActivity.class);

    @Mock private AutofillProfileBridge.Natives mAutofillProfileBridgeJni;
    @Mock private PhoneNumberUtil.Natives mPhoneNumberUtilJni;

    @Mock private EditorDialogView mEditorDialog;
    @Mock private SyncService mSyncService;
    @Mock private IdentityServicesProvider mIdentityServicesProvider;
    @Mock private IdentityManager mIdentityManager;
    @Mock private PersonalDataManager mPersonalDataManager;
    @Mock private Profile mProfile;
    @Mock private Delegate mDelegate;

    @Captor private ArgumentCaptor<AutofillAddress> mAddressCapture;

    private final CoreAccountInfo mAccountInfo =
            CoreAccountInfo.createFromEmailAndGaiaId(USER_EMAIL, "gaia_id");
    // Note: can't initialize this list statically because of how Robolectric
    // initializes Android library dependencies.
    private final List<DropdownKeyValue> mSupportedCountries =
            List.of(
                    new DropdownKeyValue("US", "United States"),
                    new DropdownKeyValue("DE", "Germany"),
                    new DropdownKeyValue("CU", "Cuba"));

    @Nullable private AutofillAddress mEditedAutofillAddress;

    private Activity mActivity;
    private AddressEditorCoordinator mAddressEditor;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        Locale.setDefault(Locale.US);
        mActivityScenarioRule.getScenario().onActivity(activity -> mActivity = activity);

        mJniMocker.mock(AutofillProfileBridgeJni.TEST_HOOKS, mAutofillProfileBridgeJni);
        doAnswer(
                        invocation -> {
                            List<Integer> requiredFields =
                                    (List<Integer>) invocation.getArguments()[1];
                            requiredFields.addAll(
                                    List.of(
                                            FieldType.NAME_FULL,
                                            FieldType.ADDRESS_HOME_CITY,
                                            FieldType.ADDRESS_HOME_DEPENDENT_LOCALITY,
                                            FieldType.ADDRESS_HOME_ZIP));
                            return null;
                        })
                .when(mAutofillProfileBridgeJni)
                .getRequiredFields(anyString(), anyList());
        mJniMocker.mock(PhoneNumberUtilJni.TEST_HOOKS, mPhoneNumberUtilJni);
        when(mPhoneNumberUtilJni.isPossibleNumber(anyString(), anyString())).thenReturn(true);

        IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
        when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager);
        when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(mAccountInfo);

        when(mSyncService.getSelectedTypes()).thenReturn(new HashSet());
        SyncServiceFactory.setInstanceForTesting(mSyncService);

        when(mPersonalDataManager.isCountryEligibleForAccountStorage(anyString())).thenReturn(true);
        when(mPersonalDataManager.getDefaultCountryCodeForNewAddress()).thenReturn("US");
        PersonalDataManagerFactory.setInstanceForTesting(mPersonalDataManager);

        setUpSupportedCountries(mSupportedCountries);

        when(mEditorDialog.getContext()).thenReturn(mActivity);
    }

    @After
    public void tearDown() {
        // Reset default locale to avoid changing it for other tests.
        Locale.setDefault(DEFAULT_LOCALE);
    }

    private void setUpSupportedCountries(List<DropdownKeyValue> supportedCountries) {
        doAnswer(
                        invocation -> {
                            List<String> contryCodes = (List<String>) invocation.getArguments()[0];
                            List<String> contryNames = (List<String>) invocation.getArguments()[1];

                            for (DropdownKeyValue keyValue : supportedCountries) {
                                contryCodes.add(keyValue.getKey());
                                contryNames.add(keyValue.getValue().toString());
                            }

                            return null;
                        })
                .when(mAutofillProfileBridgeJni)
                .getSupportedCountries(anyList(), anyList());
    }

    private void setUpAddressUiComponents(
            List<AutofillAddressUiComponent> addressUiComponents, String countryCode) {
        doAnswer(
                        invocation -> {
                            List<Integer> componentIds =
                                    (List<Integer>) invocation.getArguments()[3];
                            List<String> componentNames =
                                    (List<String>) invocation.getArguments()[4];
                            List<Integer> componentRequired =
                                    (List<Integer>) invocation.getArguments()[5];
                            List<Integer> componentLength =
                                    (List<Integer>) invocation.getArguments()[6];

                            for (AutofillAddressUiComponent component : addressUiComponents) {
                                componentIds.add(component.id);
                                componentNames.add(component.label);
                                componentRequired.add(component.isRequired ? 1 : 0);
                                componentLength.add(component.isFullLine ? 1 : 0);
                            }
                            return "EN";
                        })
                .when(mAutofillProfileBridgeJni)
                .getAddressUiComponents(
                        eq(countryCode),
                        anyString(),
                        anyInt(),
                        anyList(),
                        anyList(),
                        anyList(),
                        anyList());
    }

    private void setUpAddressUiComponents(List<AutofillAddressUiComponent> addressUiComponents) {
        setUpAddressUiComponents(addressUiComponents, "US");
    }

    private static void validateTextField(
            FieldItem fieldItem,
            String value,
            int textFieldType,
            String label,
            boolean isRequired,
            boolean isFullLine) {
        assertEquals(TEXT_INPUT, fieldItem.type);
        assertEquals(isFullLine, fieldItem.isFullLine);

        PropertyModel field = fieldItem.model;
        assertEquals(value, field.get(VALUE));
        assertEquals(textFieldType, field.get(TEXT_FIELD_TYPE));
        assertEquals(label, field.get(LABEL));
        assertEquals(isRequired, field.get(IS_REQUIRED));
    }

    private static void checkModelHasExpectedValues(
            PropertyModel editorModel,
            String expectedDeleteTitle,
            String expectedDeleteText,
            @Nullable String expectedSourceNotice) {
        assertNotNull(editorModel);

        assertFalse(editorModel.get(SHOW_REQUIRED_INDICATOR));
        assertEquals(expectedDeleteTitle, editorModel.get(DELETE_CONFIRMATION_TITLE));
        assertEquals(expectedDeleteText, editorModel.get(DELETE_CONFIRMATION_TEXT));
        assertEquals(expectedSourceNotice, editorModel.get(FOOTER_MESSAGE));
    }

    private void validateShownFields(
            PropertyModel editorModel, AutofillProfile profile, boolean shouldMarkFieldsRequired) {
        validateShownFields(
                editorModel,
                profile,
                shouldMarkFieldsRequired,
                /* shouldMarkFieldsRequiredWhenAddressFieldEmpty= */ false);
    }

    private void validateShownFields(
            PropertyModel editorModel,
            AutofillProfile profile,
            boolean shouldMarkFieldsRequired,
            boolean shouldMarkFieldsRequiredWhenAddressFieldEmpty) {
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        // editorFields[0] - country dropdown.
        // editorFields[1] - full name field.
        // editorFields[2] - admin area field.
        // editorFields[3] - locality field.
        // editorFields[4] - dependent locality field.
        // editorFields[5] - organization field.
        // editorFields[6] - sorting code field.
        // editorFields[7] - postal code field.
        // editorFields[8] - street address field.
        // editorFields[9] - phone number field.
        // editorFields[10] - email field.
        // editorFields[11] - nickname field.
        assertEquals(12, editorFields.size());

        // Fields obtained from backend must be placed after the country dropdown.
        validateTextField(
                editorFields.get(1),
                profile.getFullName(),
                FieldType.NAME_FULL,
                /* label= */ "full name label",
                shouldMarkFieldsRequired,
                /* isFullLine= */ true);
        validateTextField(
                editorFields.get(2),
                profile.getRegion(),
                FieldType.ADDRESS_HOME_STATE,
                /* label= */ "admin area label",
                /* isRequired= */ false,
                /* isFullLine= */ true);
        // Locality field is forced to occupy full line.
        validateTextField(
                editorFields.get(3),
                profile.getLocality(),
                FieldType.ADDRESS_HOME_CITY,
                /* label= */ "locality label",
                shouldMarkFieldsRequired,
                /* isFullLine= */ true);

        // Note: dependent locality is a required field for address profiles stored in Google
        // account, but it's still marked as optional by the editor when the corresponding field in
        // the existing address profile is empty. It is considered required for new address
        // profiles.
        validateTextField(
                editorFields.get(4),
                profile.getDependentLocality(),
                FieldType.ADDRESS_HOME_DEPENDENT_LOCALITY,
                /* label= */ "dependent locality label",
                shouldMarkFieldsRequiredWhenAddressFieldEmpty,
                /* isFullLine= */ true);

        validateTextField(
                editorFields.get(5),
                profile.getCompanyName(),
                FieldType.COMPANY_NAME,
                /* label= */ "organization label",
                /* isRequired= */ false,
                /* isFullLine= */ true);

        validateTextField(
                editorFields.get(6),
                profile.getSortingCode(),
                FieldType.ADDRESS_HOME_SORTING_CODE,
                /* label= */ "sorting code label",
                /* isRequired= */ false,
                /* isFullLine= */ false);
        validateTextField(
                editorFields.get(7),
                profile.getPostalCode(),
                FieldType.ADDRESS_HOME_ZIP,
                /* label= */ "postal code label",
                shouldMarkFieldsRequired,
                /* isFullLine= */ false);
        validateTextField(
                editorFields.get(8),
                profile.getStreetAddress(),
                FieldType.ADDRESS_HOME_STREET_ADDRESS,
                /* label= */ "street address label",
                shouldMarkFieldsRequired,
                /* isFullLine= */ true);
    }

    private void validateErrorMessages(PropertyModel editorModel, boolean errorsPresent) {
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        Matcher<String> requiredFieldMatcher =
                errorsPresent ? not(isEmptyString()) : anyOf(nullValue(), isEmptyString());
        assertThat(editorFields.get(0).model.get(ERROR_MESSAGE), requiredFieldMatcher);
        assertThat(editorFields.get(1).model.get(ERROR_MESSAGE), requiredFieldMatcher);
        assertThat(
                editorFields.get(2).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
        assertThat(editorFields.get(3).model.get(ERROR_MESSAGE), requiredFieldMatcher);
        assertThat(editorFields.get(4).model.get(ERROR_MESSAGE), requiredFieldMatcher);
        assertThat(
                editorFields.get(5).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
        assertThat(
                editorFields.get(6).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
        assertThat(editorFields.get(7).model.get(ERROR_MESSAGE), requiredFieldMatcher);
        assertThat(editorFields.get(8).model.get(ERROR_MESSAGE), requiredFieldMatcher);
        assertThat(
                editorFields.get(9).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
        assertThat(
                editorFields.get(10).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
        assertThat(
                editorFields.get(11).model.get(ERROR_MESSAGE), anyOf(nullValue(), isEmptyString()));
    }

    @Test
    @SmallTest
    public void validateCustomDoneButtonText() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.setCustomDoneButtonText("Custom done");
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);

        assertEquals("Custom done", editorModel.get(CUSTOM_DONE_BUTTON_TEXT));
    }

    @Test
    @SmallTest
    public void validateUIStrings_NewAddressProfile() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity.getString(R.string.autofill_delete_local_address_source_notice);
        final String sourceNotice = null;

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_NewAddressProfile_EligibleForAddressAccountStorage() {
        setUpAddressUiComponents(new ArrayList());
        when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity
                        .getString(R.string.autofill_delete_account_address_source_notice)
                        .replace("$1", USER_EMAIL);
        final String sourceNotice =
                mActivity
                        .getString(R.string.autofill_address_will_be_saved_in_account_source_notice)
                        .replace("$1", USER_EMAIL);

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_LocalOrSyncAddressProfile_AddressSyncDisabled() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity.getString(R.string.autofill_delete_local_address_source_notice);
        final String sourceNotice = null;

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_LocalOrSyncAddressProfile_AddressSyncEnabled() {
        setUpAddressUiComponents(new ArrayList());
        when(mSyncService.getSelectedTypes())
                .thenReturn(Collections.singleton(UserSelectableType.AUTOFILL));
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity.getString(R.string.autofill_delete_sync_address_source_notice);
        final String sourceNotice = null;

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_UpdateLocalOrSyncAddressProfile_AddressSyncDisabled() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity.getString(R.string.autofill_delete_local_address_source_notice);
        final String sourceNotice = null;

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_UpdateLocalOrSyncAddressProfile_AddressSyncEnabled() {
        setUpAddressUiComponents(new ArrayList());
        when(mSyncService.getSelectedTypes())
                .thenReturn(Collections.singleton(UserSelectableType.AUTOFILL));
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity.getString(R.string.autofill_delete_sync_address_source_notice);
        final String sourceNotice = null;

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_LocalAddressProfile_MigrationToAccount() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        MIGRATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity
                        .getString(R.string.autofill_delete_account_address_source_notice)
                        .replace("$1", USER_EMAIL);
        final String sourceNotice =
                mActivity
                        .getString(R.string.autofill_address_will_be_saved_in_account_source_notice)
                        .replace("$1", USER_EMAIL);

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_SyncAddressProfile_MigrationToAccount() {
        setUpAddressUiComponents(new ArrayList());
        when(mSyncService.getSelectedTypes())
                .thenReturn(Collections.singleton(UserSelectableType.AUTOFILL));
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        MIGRATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity
                        .getString(R.string.autofill_delete_account_address_source_notice)
                        .replace("$1", USER_EMAIL);
        final String sourceNotice =
                mActivity
                        .getString(R.string.autofill_address_will_be_saved_in_account_source_notice)
                        .replace("$1", USER_EMAIL);

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_AccountAddressProfile_SaveInAccountFlow() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sAccountProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity
                        .getString(R.string.autofill_delete_account_address_source_notice)
                        .replace("$1", USER_EMAIL);
        final String sourceNotice =
                mActivity
                        .getString(R.string.autofill_address_will_be_saved_in_account_source_notice)
                        .replace("$1", USER_EMAIL);

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    public void validateUIStrings_AccountAddressProfile_UpdateAccountProfileFlow() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sAccountProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        final String deleteTitle =
                mActivity.getString(R.string.autofill_delete_address_confirmation_dialog_title);
        final String deleteText =
                mActivity
                        .getString(R.string.autofill_delete_account_address_source_notice)
                        .replace("$1", USER_EMAIL);
        final String sourceNotice =
                mActivity
                        .getString(R.string.autofill_address_already_saved_in_account_source_notice)
                        .replace("$1", USER_EMAIL);

        checkModelHasExpectedValues(
                mAddressEditor.getEditorModelForTesting(), deleteTitle, deleteText, sourceNotice);
    }

    @Test
    @SmallTest
    @DisableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT})
    public void validateDefaultFields_NicknamesDisabled_HonorificDisabled() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        assertNotNull(mAddressEditor.getEditorModelForTesting());
        ListModel<FieldItem> editorFields =
                mAddressEditor.getEditorModelForTesting().get(EDITOR_FIELDS);
        // Following values are set regardless of the UI components list
        // received from backend when nicknames are disabled:
        // editorFields[0] - country dropdown.
        // editorFields[1] - phone field.
        // editorFields[2] - email field.
        assertEquals(3, editorFields.size());

        FieldItem countryDropdownItem = editorFields.get(0);
        assertEquals(DROPDOWN, countryDropdownItem.type);
        assertTrue(countryDropdownItem.isFullLine);

        PropertyModel countryDropdown = countryDropdownItem.model;
        assertEquals(
                countryDropdown.get(VALUE),
                AutofillAddress.getCountryCode(sLocalProfile, mPersonalDataManager));
        assertEquals(
                countryDropdown.get(LABEL),
                mActivity.getString(R.string.autofill_profile_editor_country));
        assertEquals(
                mSupportedCountries.size(), countryDropdown.get(DROPDOWN_KEY_VALUE_LIST).size());
        assertThat(
                mSupportedCountries,
                containsInAnyOrder(countryDropdown.get(DROPDOWN_KEY_VALUE_LIST).toArray()));

        validateTextField(
                editorFields.get(1),
                sLocalProfile.getPhoneNumber(),
                FieldType.PHONE_HOME_WHOLE_NUMBER,
                mActivity.getString(R.string.autofill_profile_editor_phone_number),
                false,
                true);
        validateTextField(
                editorFields.get(2),
                sLocalProfile.getEmailAddress(),
                FieldType.EMAIL_ADDRESS,
                mActivity.getString(R.string.autofill_profile_editor_email_address),
                false,
                true);
    }

    @Test
    @SmallTest
    public void validateDefaultFields() {
        setUpAddressUiComponents(new ArrayList());
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        assertNotNull(mAddressEditor.getEditorModelForTesting());
        ListModel<FieldItem> editorFields =
                mAddressEditor.getEditorModelForTesting().get(EDITOR_FIELDS);
        // Following values are set regardless of the UI components list
        // received from backend:
        // editorFields[0] - country dropdown.
        // editorFields[1] - phone field.
        // editorFields[2] - email field.
        // editorFields[3] - nickname field.
        assertEquals(4, editorFields.size());

        validateTextField(editorFields.get(3), null, FieldType.UNKNOWN_TYPE, "Label", false, true);
    }

    @Test
    @SmallTest
    public void validateShownFields_NewAddressProfile() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                AutofillProfile.builder().build(),
                /* shouldMarkFieldsRequired= */ false);
    }

    @Test
    @SmallTest
    public void validateShownFields_NewAddressProfile_EligibleForAddressAccountStorage() {
        when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                AutofillProfile.builder().build(),
                /* shouldMarkFieldsRequired= */ true,
                /* shouldMarkFieldsRequiredWhenAddressFieldEmpty= */ true);
    }

    @Test
    @SmallTest
    public void validateShownFields_LocalOrSyncAddressProfile_SaveLocally() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                sLocalProfile,
                /* shouldMarkFieldsRequired= */ false);
    }

    @Test
    @SmallTest
    public void validateShownFields_LocalOrSyncAddressProfile_UpdateLocally() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                sLocalProfile,
                /* shouldMarkFieldsRequired= */ false);
    }

    @Test
    @SmallTest
    public void validateShownFields_LocalOrSyncAddressProfile_MigrationToAccount() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        MIGRATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                sLocalProfile,
                /* shouldMarkFieldsRequired= */ true);
    }

    @Test
    @SmallTest
    public void validateShownFields_AccountProfile_SaveInAccountFlow() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sAccountProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                sAccountProfile,
                /* shouldMarkFieldsRequired= */ true);
    }

    @Test
    @SmallTest
    public void validateShownFields_AccountProfile_UpdateAlreadySaved() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sAccountProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();
        validateShownFields(
                mAddressEditor.getEditorModelForTesting(),
                sAccountProfile,
                /* shouldMarkFieldsRequired= */ true);
    }

    @Test
    @SmallTest
    public void edit_ChangeCountry_FieldsSetChanges() {
        setUpAddressUiComponents(
                List.of(
                        new AutofillAddressUiComponent(
                                FieldType.ADDRESS_HOME_SORTING_CODE,
                                "sorting code label",
                                false,
                                true)),
                "US");
        setUpAddressUiComponents(
                List.of(
                        new AutofillAddressUiComponent(
                                FieldType.ADDRESS_HOME_STREET_ADDRESS,
                                "street address label",
                                true,
                                true)),
                "DE");
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, sLocalProfile, mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        assertNotNull(mAddressEditor.getEditorModelForTesting());
        ListModel<FieldItem> editorFields =
                mAddressEditor.getEditorModelForTesting().get(EDITOR_FIELDS);

        // editorFields[0] - country dropdown.
        // editorFields[1] - sorting code field.
        // editorFields[2] - phone number field.
        // editorFields[3] - email field.
        // editorFields[4] - nickname field.
        assertEquals(5, editorFields.size());
        assertThat(
                StreamSupport.stream(
                                Spliterators.spliteratorUnknownSize(
                                        editorFields.iterator(), Spliterator.ORDERED),
                                false)
                        .skip(1)
                        .map(item -> item.model.get(TEXT_FIELD_TYPE))
                        .collect(Collectors.toList()),
                containsInAnyOrder(
                        FieldType.ADDRESS_HOME_SORTING_CODE,
                        FieldType.PHONE_HOME_WHOLE_NUMBER,
                        FieldType.EMAIL_ADDRESS,
                        FieldType.UNKNOWN_TYPE));
        PropertyModel countryDropdown = editorFields.get(0).model;

        setDropdownKey(countryDropdown, "DE");
        ListModel<FieldItem> editorFieldsGermany =
                mAddressEditor.getEditorModelForTesting().get(EDITOR_FIELDS);
        // editorFields[0] - country dropdown.
        // editorFields[1] - street address field.
        // editorFields[2] - phone number field.
        // editorFields[3] - email field.
        // editorFields[4] - nickname field.
        assertEquals(5, editorFieldsGermany.size());
        assertThat(
                StreamSupport.stream(
                                Spliterators.spliteratorUnknownSize(
                                        editorFieldsGermany.iterator(), Spliterator.ORDERED),
                                false)
                        .skip(1)
                        .map(item -> item.model.get(TEXT_FIELD_TYPE))
                        .collect(Collectors.toList()),
                containsInAnyOrder(
                        FieldType.ADDRESS_HOME_STREET_ADDRESS,
                        FieldType.PHONE_HOME_WHOLE_NUMBER,
                        FieldType.EMAIL_ADDRESS,
                        FieldType.UNKNOWN_TYPE));
    }

    @Test
    @SmallTest
    public void edit_NewAddressProfile_EligibleForAddressAccountStorage() {
        when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        // Set values of the required fields.
        editorFields.get(1).model.set(VALUE, "New Name");
        editorFields.get(3).model.set(VALUE, "Locality");
        editorFields.get(4).model.set(VALUE, "Dependent locality");
        editorFields.get(7).model.set(VALUE, "Postal code");
        editorFields.get(8).model.set(VALUE, "Street address");
        editorModel.get(DONE_RUNNABLE).run();

        verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
        verify(mDelegate, times(0)).onCancel();
        AutofillAddress address = mAddressCapture.getValue();
        assertNotNull(address);
        assertEquals(RecordType.ACCOUNT, address.getProfile().getRecordType());
    }

    @Test
    @SmallTest
    public void edit_AlterAddressProfile_Cancel() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(
                                mActivity,
                                new AutofillProfile(sLocalProfile),
                                mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        // Verify behaviour only on the relevant subset of fields.
        editorFields.get(1).model.set(VALUE, "New Name");
        editorFields.get(2).model.set(VALUE, "New admin area");
        editorModel.get(CANCEL_RUNNABLE).run();

        verify(mDelegate, times(0)).onDone(any());
        verify(mDelegate, times(1)).onCancel();
    }

    @Test
    @SmallTest
    public void edit_AlterAddressProfile_CommitChanges() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(
                                mActivity,
                                new AutofillProfile(sLocalProfile),
                                mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        assertNotNull(mAddressEditor.getEditorModelForTesting());
        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        // Verify behaviour only on the relevant subset of fields.
        editorFields.get(3).model.set(VALUE, "New locality");
        editorFields.get(4).model.set(VALUE, "New dependent locality");
        editorFields.get(5).model.set(VALUE, "New organization");
        editorModel.get(DONE_RUNNABLE).run();

        verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
        verify(mDelegate, times(0)).onCancel();
        AutofillAddress address = mAddressCapture.getValue();
        assertNotNull(address);
        assertEquals("New locality", address.getProfile().getLocality());
        assertEquals("New dependent locality", address.getProfile().getDependentLocality());
        assertEquals("New organization", address.getProfile().getCompanyName());
    }

    @Test
    @SmallTest
    public void edit_AlterAddressProfile_CommitChanges_InvisibleFieldsNotReset() {
        // Whitelist only full name, admin area and locality.
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS.subList(0, 3));
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(
                                mActivity,
                                new AutofillProfile(sLocalProfile),
                                mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);

        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        // editorFields[0] - country dropdown.
        // editorFields[1] - full name field.
        // editorFields[2] - admin area field.
        // editorFields[3] - locality field.
        // editorFields[4] - phone number field.
        // editorFields[5] - email field.
        // editorFields[6] - nickname field.
        assertEquals(7, editorFields.size());

        editorModel.get(DONE_RUNNABLE).run();
        verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
        verify(mDelegate, times(0)).onCancel();

        AutofillAddress address = mAddressCapture.getValue();
        assertNotNull(address);
        AutofillProfile profile = address.getProfile();
        assertEquals(profile.getStreetAddress(), "111 First St");
        assertEquals(profile.getDependentLocality(), "");
        assertEquals(profile.getCompanyName(), "Google");
        assertEquals(profile.getPostalCode(), "90291");
        assertEquals(profile.getSortingCode(), "");
    }

    @Test
    @SmallTest
    public void accountSavingDisallowedForUnsupportedCountry() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS, "US");
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS, "CU");
        when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
        when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        PropertyModel countryDropdown = editorFields.get(0).model;
        setDropdownKey(countryDropdown, "CU");

        // Set values of the required fields.
        editorFields.get(1).model.set(VALUE, "New Name");
        editorFields.get(3).model.set(VALUE, "Locality");
        editorFields.get(4).model.set(VALUE, "Dependent locality");
        editorFields.get(7).model.set(VALUE, "Postal code");
        editorFields.get(8).model.set(VALUE, "Street address");
        editorModel.get(DONE_RUNNABLE).run();

        verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
        verify(mDelegate, times(0)).onCancel();
        AutofillAddress address = mAddressCapture.getValue();
        assertNotNull(address);
        assertEquals(RecordType.LOCAL_OR_SYNCABLE, address.getProfile().getRecordType());
    }

    @Test
    @SmallTest
    public void countryDropDownExcludesUnsupportedCountries_saveInAccountFlow() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(
                                mActivity,
                                new AutofillProfile(sAccountProfile),
                                mPersonalDataManager),
                        SAVE_NEW_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        assertThat(
                editorFields.get(0).model.get(DROPDOWN_KEY_VALUE_LIST).stream()
                        .map(DropdownKeyValue::getKey)
                        .collect(Collectors.toList()),
                containsInAnyOrder("US", "DE"));
    }

    @Test
    @SmallTest
    public void countryDropDownExcludesUnsupportedCountries_MigrationFlow() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(
                                mActivity,
                                new AutofillProfile(sLocalProfile),
                                mPersonalDataManager),
                        MIGRATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        assertThat(
                editorFields.get(0).model.get(DROPDOWN_KEY_VALUE_LIST).stream()
                        .map(DropdownKeyValue::getKey)
                        .collect(Collectors.toList()),
                containsInAnyOrder("US", "DE"));
    }

    @Test
    @SmallTest
    public void countryDropDownExcludesUnsupportedCountries_editExistingAccountProfile() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(
                                mActivity,
                                new AutofillProfile(sAccountProfile),
                                mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        ListModel<FieldItem> editorFields = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, editorFields.size());

        assertThat(
                editorFields.get(0).model.get(DROPDOWN_KEY_VALUE_LIST).stream()
                        .map(DropdownKeyValue::getKey)
                        .collect(Collectors.toList()),
                containsInAnyOrder("US", "DE"));
    }

    @Test
    @SmallTest
    public void edit_NewAddressProfile_NoInitialValidation() {
        when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        validateErrorMessages(
                mAddressEditor.getEditorModelForTesting(), /* errorsPresent= */ false);
    }

    @Test
    @SmallTest
    public void edit_NewAddressProfile_FieldsAreValidatedAfterSave() {
        when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity, mDelegate, mProfile, /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        editorModel.get(DONE_RUNNABLE).run();
        validateErrorMessages(mAddressEditor.getEditorModelForTesting(), /* errorsPresent= */ true);
    }

    @Test
    @SmallTest
    public void edit_AccountAddressProfile_FieldsAreImmediatelyValidated() {
        AutofillProfile accountProfile = new AutofillProfile(sAccountProfile);
        accountProfile.setInfo(FieldType.NAME_FULL, "");
        accountProfile.setInfo(FieldType.ADDRESS_HOME_CITY, "");
        accountProfile.setInfo(FieldType.ADDRESS_HOME_ZIP, "");

        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, accountProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        validateErrorMessages(mAddressEditor.getEditorModelForTesting(), /* errorsPresent= */ true);
    }

    @Test
    @SmallTest
    public void edit_AccountAddressProfile_FieldsAreValidatedAfterSave() {
        AutofillProfile accountProfile = new AutofillProfile(sAccountProfile);
        accountProfile.setInfo(FieldType.NAME_FULL, "");
        accountProfile.setInfo(FieldType.ADDRESS_HOME_CITY, "");
        accountProfile.setInfo(FieldType.ADDRESS_HOME_ZIP, "");

        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, accountProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);
        editorModel.get(DONE_RUNNABLE).run();
        validateErrorMessages(mAddressEditor.getEditorModelForTesting(), /* errorsPresent= */ true);
    }

    @Test
    @SmallTest
    public void edit_AccountAddressProfile_EmptyFieldsAreValidatedAfterSave() {
        setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);

        AutofillProfile accountProfile = new AutofillProfile(sAccountProfile);
        mAddressEditor =
                new AddressEditorCoordinator(
                        mActivity,
                        mDelegate,
                        mProfile,
                        new AutofillAddress(mActivity, accountProfile, mPersonalDataManager),
                        UPDATE_EXISTING_ADDRESS_PROFILE,
                        /* saveToDisk= */ false);
        mAddressEditor.setEditorDialogForTesting(mEditorDialog);
        mAddressEditor.showEditorDialog();

        PropertyModel editorModel = mAddressEditor.getEditorModelForTesting();
        assertNotNull(editorModel);

        ListModel<FieldItem> model = editorModel.get(EDITOR_FIELDS);
        assertEquals(12, model.size());
        for (FieldItem item : model) {
            if (item.model.get(IS_REQUIRED)) {
                item.model.set(VALUE, "");
            }
        }

        editorModel.get(DONE_RUNNABLE).run();
        validateErrorMessages(mAddressEditor.getEditorModelForTesting(), /* errorsPresent= */ true);
    }
}