blob: 86e6c772c1cf99345472708ebcd04ca2e912413c [file] [log] [blame]
// 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.settings;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.isEmptyString;
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.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.annotation.Config;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.PersonalDataManager;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.Source;
import org.chromium.chrome.browser.autofill.prefeditor.EditorDialog;
import org.chromium.chrome.browser.autofill.prefeditor.EditorModel;
import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.AddressField;
import org.chromium.chrome.browser.autofill.settings.AutofillProfileBridge.AddressUiComponent;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.payments.AutofillAddress;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.signin.services.IdentityServicesProvider;
import org.chromium.chrome.browser.sync.SyncService;
import org.chromium.chrome.test.util.browser.Features;
import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
import org.chromium.components.autofill.prefeditor.EditorFieldModel;
import org.chromium.components.autofill.prefeditor.EditorFieldModel.DropdownKeyValue;
import org.chromium.components.signin.base.CoreAccountInfo;
import org.chromium.components.signin.identitymanager.IdentityManager;
import org.chromium.components.sync.UserSelectableType;
import org.chromium.ui.base.TestActivity;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
/** Unit tests for {@link AddressEditor}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@EnableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT,
ChromeFeatureList.AUTOFILL_ENABLE_SUPPORT_FOR_HONORIFIC_PREFIXES})
public class AddressEditorTest {
private static final String USER_EMAIL = "example@gmail.com";
private static final Locale DEFAULT_LOCALTE = Locale.getDefault();
private static final List<AddressUiComponent> SUPPORTED_ADDRESS_FIELDS = List.of(
new AddressUiComponent(AddressField.RECIPIENT, "full name label", true, true),
new AddressUiComponent(AddressField.ADMIN_AREA, "admin area label", false, true),
new AddressUiComponent(AddressField.LOCALITY, "locality label", true, false),
new AddressUiComponent(
AddressField.DEPENDENT_LOCALITY, "dependent locality label", true, false),
new AddressUiComponent(AddressField.ORGANIZATION, "organization label", false, true),
new AddressUiComponent(AddressField.SORTING_CODE, "sorting code label", false, false),
new AddressUiComponent(AddressField.POSTAL_CODE, "postal code label", true, false),
new AddressUiComponent(
AddressField.STREET_ADDRESS, "street address label", true, true));
private static final AutofillProfile sLocalProfile = new AutofillProfile("",
true, Source.LOCAL_OR_SYNCABLE, "" /* honorific prefix */,
"Seb Doe", "Google", "111 First St", "CA", "Los Angeles", "", "90291", "", "US",
"650-253-0000", "first@gmail.com", "en-US");
private static final AutofillProfile sAccountProfile = new AutofillProfile("",
true, Source.ACCOUNT, "" /* honorific prefix */, "Seb Doe",
"Google", "111 First St", "CA", "Los Angeles", "", "90291", "", "US", "650-253-0000",
"first@gmail.com", "en-US");
@Rule
public TestRule mProcessor = new Features.JUnitProcessor();
@Rule
public JniMocker mJniMocker = new JniMocker();
@Mock
private AutofillProfileBridge.Natives mAutofillProfileBridgeJni;
@Mock
private EditorDialog mEditorDialog;
@Mock
private SyncService mSyncService;
@Mock
private IdentityServicesProvider mIdentityServicesProvider;
@Mock
private IdentityManager mIdentityManager;
@Mock
private PersonalDataManager mPersonalDataManager;
@Mock
private Profile mProfile;
@Mock
private AddressEditor.Delegate mDelegate;
@Captor
private ArgumentCaptor<EditorModel> mEditorModelCapture;
@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 AddressEditor mAddressEditor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Locale.setDefault(Locale.US);
mJniMocker.mock(AutofillProfileBridgeJni.TEST_HOOKS, mAutofillProfileBridgeJni);
doAnswer(invocation -> {
List<Integer> requiredFields = (List<Integer>) invocation.getArguments()[1];
requiredFields.addAll(List.of(AddressField.RECIPIENT, AddressField.LOCALITY,
AddressField.DEPENDENT_LOCALITY, AddressField.POSTAL_CODE));
return null;
})
.when(mAutofillProfileBridgeJni)
.getRequiredFields(anyString(), anyList());
mActivity = Robolectric.setupActivity(TestActivity.class);
Profile.setLastUsedProfileForTesting(mProfile);
IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider);
when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager);
when(mIdentityManager.getPrimaryAccountInfo(anyInt())).thenReturn(mAccountInfo);
when(mSyncService.isSyncFeatureEnabled()).thenReturn(false);
when(mSyncService.getSelectedTypes()).thenReturn(new HashSet());
SyncService.overrideForTests(mSyncService);
when(mPersonalDataManager.isCountryEligibleForAccountStorage(anyString())).thenReturn(true);
PersonalDataManager.setInstanceForTesting(mPersonalDataManager);
setUpSupportedCountries(mSupportedCountries);
when(mEditorDialog.getContext()).thenReturn(mActivity);
doNothing().when(mEditorDialog).show(mEditorModelCapture.capture());
}
@After
public void tearDown() {
// Reset default locale to avoid changing it for other tests.
Locale.setDefault(DEFAULT_LOCALTE);
}
private void setUpSupportedCountries(List<DropdownKeyValue> supportedContries) {
doAnswer(invocation -> {
List<String> contryCodes = (List<String>) invocation.getArguments()[0];
List<String> contryNames = (List<String>) invocation.getArguments()[1];
for (DropdownKeyValue keyValue : supportedContries) {
contryCodes.add(keyValue.getKey());
contryNames.add(keyValue.getValue().toString());
}
return null;
})
.when(mAutofillProfileBridgeJni)
.getSupportedCountries(anyList(), anyList());
}
private void setUpAddressUiComponents(
List<AddressUiComponent> 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 (AddressUiComponent 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<AddressUiComponent> addressUiComponents) {
setUpAddressUiComponents(addressUiComponents, "US");
}
private static void validateTextField(EditorFieldModel field, String value, int inputTypeHint,
String label, boolean isRequired, boolean isFullLine, boolean hasLengthCounter) {
Assert.assertTrue(field.isTextField());
Assert.assertEquals(field.getValue(), value);
Assert.assertEquals(inputTypeHint, field.getInputTypeHint());
Assert.assertEquals(label, field.getLabel());
Assert.assertEquals(isRequired, field.isRequired());
Assert.assertEquals(isFullLine, field.isFullLine());
Assert.assertEquals(hasLengthCounter, field.hasLengthCounter());
}
private static void checkUiStringsHaveExpectedValues(EditorModel editorModel,
String expectedDeleteTitle, String expectedDeleteText,
@Nullable String expectedSourceNotice) {
Assert.assertNotNull(editorModel);
Assert.assertEquals(expectedDeleteTitle, editorModel.getDeleteConfirmationTitle());
Assert.assertEquals(expectedDeleteText, editorModel.getDeleteConfirmationText());
Assert.assertEquals(expectedSourceNotice, editorModel.getFooterMessageText());
}
private void validateShownFields(
EditorModel editorModel, AutofillProfile profile, boolean shouldMarkFieldsRequired) {
validateShownFields(editorModel, profile, shouldMarkFieldsRequired,
/*shouldMarkFieldsRequiredWhenAddressFieldEmpty=*/false);
}
private void validateShownFields(EditorModel editorModel, AutofillProfile profile,
boolean shouldMarkFieldsRequired,
boolean shouldMarkFieldsRequiredWhenAddressFieldEmpty) {
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
// editorFields[0] - country dropdown.
// editorFields[1] - honorific field.
// editorFields[2] - full name field.
// editorFields[3] - admin area field.
// editorFields[4] - locality field.
// editorFields[5] - dependent locality field.
// editorFields[6] - organization field.
// editorFields[7] - sorting code field.
// editorFields[8] - postal code field.
// editorFields[9] - street address field.
// editorFields[10] - phone number field.
// editorFields[11] - email field.
// editorFields[12] - nickname field.
Assert.assertEquals(13, editorFields.size());
// Fields obtained from backend must be placed after the country dropdown.
// Note: honorific prefix always comes before the full name field.
validateTextField(editorFields.get(1), profile.getHonorificPrefix(),
EditorFieldModel.INPUT_TYPE_HINT_NONE,
mActivity.getString(R.string.autofill_profile_editor_honorific_prefix), false, true,
false);
validateTextField(editorFields.get(2), profile.getFullName(),
EditorFieldModel.INPUT_TYPE_HINT_PERSON_NAME, "full name label",
shouldMarkFieldsRequired, true, false);
validateTextField(editorFields.get(3), profile.getRegion(),
EditorFieldModel.INPUT_TYPE_HINT_NONE, "admin area label", false, true, false);
// Locality field is forced to occupy full line.
validateTextField(editorFields.get(4), profile.getLocality(),
EditorFieldModel.INPUT_TYPE_HINT_NONE, "locality label", shouldMarkFieldsRequired,
true, false);
// 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(5), profile.getDependentLocality(),
EditorFieldModel.INPUT_TYPE_HINT_NONE, "dependent locality label",
shouldMarkFieldsRequiredWhenAddressFieldEmpty, true, false);
validateTextField(editorFields.get(6), profile.getCompanyName(),
EditorFieldModel.INPUT_TYPE_HINT_NONE, "organization label", false, true, false);
validateTextField(editorFields.get(7), profile.getSortingCode(),
EditorFieldModel.INPUT_TYPE_HINT_ALPHA_NUMERIC, "sorting code label", false, false,
false);
validateTextField(editorFields.get(8), profile.getPostalCode(),
EditorFieldModel.INPUT_TYPE_HINT_ALPHA_NUMERIC, "postal code label",
shouldMarkFieldsRequired, false, false);
validateTextField(editorFields.get(9), profile.getStreetAddress(),
EditorFieldModel.INPUT_TYPE_HINT_STREET_LINES, "street address label",
shouldMarkFieldsRequired, true, false);
}
@Test
@SmallTest
public void validateCustomDoneButtonText() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
mAddressEditor.setCustomDoneButtonText("Custom done");
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
Assert.assertEquals("Custom done", editorModel.getCustomDoneButtonText());
}
@Test
@SmallTest
public void validateUIStrings_NewAddressProfile() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
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;
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_NewAddressProfile_EligibleForAddressAccountStorage() {
when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
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);
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_LocalOrSyncAddressProfile_AddressSyncDisabled() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
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;
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_LocalOrSyncAddressProfile_AddressSyncEnabled() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
when(mSyncService.isSyncFeatureEnabled()).thenReturn(true);
when(mSyncService.getSelectedTypes())
.thenReturn(Collections.singleton(UserSelectableType.AUTOFILL));
setUpAddressUiComponents(new ArrayList());
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;
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_UpdateLocalOrSyncAddressProfile_AddressSyncDisabled() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/true,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
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;
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_UpdateLocalOrSyncAddressProfile_AddressSyncEnabled() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/true,
/*isMigrationToAccount=*/false);
when(mSyncService.isSyncFeatureEnabled()).thenReturn(true);
when(mSyncService.getSelectedTypes())
.thenReturn(Collections.singleton(UserSelectableType.AUTOFILL));
setUpAddressUiComponents(new ArrayList());
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;
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_LocalAddressProfile_MigrationToAccount() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/true);
setUpAddressUiComponents(new ArrayList());
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);
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_SyncAddressProfile_MigrationToAccount() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/true);
when(mSyncService.isSyncFeatureEnabled()).thenReturn(true);
when(mSyncService.getSelectedTypes())
.thenReturn(Collections.singleton(UserSelectableType.AUTOFILL));
setUpAddressUiComponents(new ArrayList());
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);
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_AccountAddressProfile_SaveInAccountFlow() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sAccountProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
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);
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
public void validateUIStrings_AccountAddressProfile_UpdateAccountProfileFlow() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sAccountProfile), /*saveToDisk=*/false,
/*isUpdate=*/true,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
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);
checkUiStringsHaveExpectedValues(
mEditorModelCapture.getValue(), deleteTitle, deleteText, sourceNotice);
}
@Test
@SmallTest
@DisableFeatures({ChromeFeatureList.AUTOFILL_ADDRESS_PROFILE_SAVE_PROMPT_NICKNAME_SUPPORT,
ChromeFeatureList.AUTOFILL_ENABLE_SUPPORT_FOR_HONORIFIC_PREFIXES})
public void
validateDefaultFields_NicknamesDisabled_HonorificDisabled() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
mAddressEditor.showEditorDialog();
Assert.assertNotNull(mEditorModelCapture.getValue());
List<EditorFieldModel> editorFields = mEditorModelCapture.getValue().getFields();
// 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.
Assert.assertEquals(3, editorFields.size());
EditorFieldModel countryDropdown = editorFields.get(0);
Assert.assertTrue(countryDropdown.isDropdownField());
Assert.assertTrue(countryDropdown.isFullLine());
Assert.assertEquals(
countryDropdown.getValue(), AutofillAddress.getCountryCode(sLocalProfile));
Assert.assertEquals(countryDropdown.getLabel(),
mActivity.getString(R.string.autofill_profile_editor_country));
Assert.assertEquals(
mSupportedCountries.size(), countryDropdown.getDropdownKeyValues().size());
assertThat(mSupportedCountries,
containsInAnyOrder(countryDropdown.getDropdownKeyValues().toArray()));
validateTextField(editorFields.get(1), sLocalProfile.getPhoneNumber(),
EditorFieldModel.INPUT_TYPE_HINT_PHONE,
mActivity.getString(R.string.autofill_profile_editor_phone_number), false, true,
false);
validateTextField(editorFields.get(2), sLocalProfile.getEmailAddress(),
EditorFieldModel.INPUT_TYPE_HINT_EMAIL,
mActivity.getString(R.string.autofill_profile_editor_email_address), false, true,
false);
}
@Test
@SmallTest
public void validateDefaultFields() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(new ArrayList());
mAddressEditor.showEditorDialog();
Assert.assertNotNull(mEditorModelCapture.getValue());
List<EditorFieldModel> editorFields = mEditorModelCapture.getValue().getFields();
// 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.
Assert.assertEquals(4, editorFields.size());
validateTextField(editorFields.get(3), null, EditorFieldModel.INPUT_TYPE_HINT_NONE, "Label",
false, true, false);
}
@Test
@SmallTest
public void validateShownFields_NewAddressProfile() {
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
mAddressEditor.showEditorDialog();
validateShownFields(mEditorModelCapture.getValue(), new AutofillProfile(),
/*shouldMarkFieldsRequired=*/false);
}
@Test
@SmallTest
public void validateShownFields_NewAddressProfile_EligibleForAddressAccountStorage() {
when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
mAddressEditor.showEditorDialog();
validateShownFields(mEditorModelCapture.getValue(), new AutofillProfile(),
/*shouldMarkFieldsRequired=*/true,
/*shouldMarkFieldsRequiredWhenAddressFieldEmpty=*/true);
}
@Test
@SmallTest
public void validateShownFields_LocalOrSyncAddressProfile_SaveLocally() {
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
mAddressEditor.showEditorDialog();
validateShownFields(
mEditorModelCapture.getValue(), sLocalProfile, /*shouldMarkFieldsRequired=*/false);
}
@Test
@SmallTest
public void validateShownFields_LocalOrSyncAddressProfile_UpdateLocally() {
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/true,
/*isMigrationToAccount=*/false);
mAddressEditor.showEditorDialog();
validateShownFields(
mEditorModelCapture.getValue(), sLocalProfile, /*shouldMarkFieldsRequired=*/false);
}
@Test
@SmallTest
public void validateShownFields_LocalOrSyncAddressProfile_MigrationToAccount() {
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/true);
mAddressEditor.showEditorDialog();
validateShownFields(
mEditorModelCapture.getValue(), sLocalProfile, /*shouldMarkFieldsRequired=*/true);
}
@Test
@SmallTest
public void validateShownFields_AccountProfile_SaveInAccountFlow() {
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sAccountProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
mAddressEditor.showEditorDialog();
validateShownFields(
mEditorModelCapture.getValue(), sAccountProfile, /*shouldMarkFieldsRequired=*/true);
}
@Test
@SmallTest
public void validateShownFields_AccountProfile_UpdateAlreadySaved() {
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sAccountProfile), /*saveToDisk=*/false,
/*isUpdate=*/true,
/*isMigrationToAccount=*/false);
mAddressEditor.showEditorDialog();
validateShownFields(
mEditorModelCapture.getValue(), sAccountProfile, /*shouldMarkFieldsRequired=*/true);
}
@Test
@SmallTest
public void edit_ChangeCountry_FieldsSetChanges() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, sLocalProfile), /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(List.of(new AddressUiComponent(AddressField.SORTING_CODE,
"sorting code label", false, true)),
"US");
setUpAddressUiComponents(List.of(new AddressUiComponent(AddressField.STREET_ADDRESS,
"street address label", true, true)),
"DE");
mAddressEditor.showEditorDialog();
Assert.assertNotNull(mEditorModelCapture.getValue());
List<EditorFieldModel> editorFields = mEditorModelCapture.getValue().getFields();
// editorFields[0] - country dropdown.
// editorFields[1] - sorting code field.
// editorFields[2] - phone number field.
// editorFields[3] - email field.
// editorFields[4] - nickname field.
Assert.assertEquals(5, editorFields.size());
assertThat(editorFields.stream()
.map(EditorFieldModel::getInputTypeHint)
.collect(Collectors.toList()),
containsInAnyOrder(EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN,
EditorFieldModel.INPUT_TYPE_HINT_ALPHA_NUMERIC,
EditorFieldModel.INPUT_TYPE_HINT_PHONE,
EditorFieldModel.INPUT_TYPE_HINT_EMAIL,
EditorFieldModel.INPUT_TYPE_HINT_NONE));
EditorFieldModel countryDropdown = editorFields.get(0);
countryDropdown.setDropdownKey("DE", () -> {});
// editorFields[0] - country dropdown.
// editorFields[1] - street address field.
// editorFields[2] - phone number field.
// editorFields[3] - email field.
// editorFields[4] - nickname field.
Assert.assertEquals(5, editorFields.size());
assertThat(editorFields.stream()
.map(EditorFieldModel::getInputTypeHint)
.collect(Collectors.toList()),
containsInAnyOrder(EditorFieldModel.INPUT_TYPE_HINT_DROPDOWN,
EditorFieldModel.INPUT_TYPE_HINT_STREET_LINES,
EditorFieldModel.INPUT_TYPE_HINT_PHONE,
EditorFieldModel.INPUT_TYPE_HINT_EMAIL,
EditorFieldModel.INPUT_TYPE_HINT_NONE));
}
@Test
@SmallTest
public void edit_NewAddressProfile_EligibleForAddressAccountStorage() {
when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
// Set values of the required fields.
editorFields.get(2).setValue("New Name");
editorFields.get(4).setValue("Locality");
editorFields.get(5).setValue("Dependent locality");
editorFields.get(8).setValue("Postal code");
editorFields.get(9).setValue("Street address");
editorModel.done();
verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
verify(mDelegate, times(0)).onCancel();
AutofillAddress address = mAddressCapture.getValue();
Assert.assertNotNull(address);
Assert.assertEquals(Source.ACCOUNT, address.getProfile().getSource());
}
@Test
@SmallTest
public void edit_AlterAddressProfile_Cancel() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, new AutofillProfile(sLocalProfile)),
/*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
// Verify behaviour only on the relevant subset of fields.
editorFields.get(1).setValue("New honorific prefix");
editorFields.get(2).setValue("New Name");
editorFields.get(3).setValue("New admin area");
editorModel.cancel();
verify(mDelegate, times(0)).onDone(any());
verify(mDelegate, times(1)).onCancel();
}
@Test
@SmallTest
public void edit_AlterAddressProfile_CommitChanges() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, new AutofillProfile(sLocalProfile)),
/*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor.showEditorDialog();
Assert.assertNotNull(mEditorModelCapture.getValue());
EditorModel editorModel = mEditorModelCapture.getValue();
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
// Verify behaviour only on the relevant subset of fields.
editorFields.get(4).setValue("New locality");
editorFields.get(5).setValue("New dependent locality");
editorFields.get(6).setValue("New organization");
editorModel.done();
verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
verify(mDelegate, times(0)).onCancel();
AutofillAddress address = mAddressCapture.getValue();
Assert.assertNotNull(address);
Assert.assertEquals("New locality", address.getProfile().getLocality());
Assert.assertEquals("New dependent locality", address.getProfile().getDependentLocality());
Assert.assertEquals("New organization", address.getProfile().getCompanyName());
}
@Test
@SmallTest
public void edit_AlterAddressProfile_CommitChanges_InvisibleFieldsGetReset() {
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, new AutofillProfile(sLocalProfile)),
/*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
// Whitelist only full name, admin area and locality.
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS.subList(0, 3));
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
// editorFields[0] - country dropdown.
// editorFields[1] - honorific prefix field.
// editorFields[2] - full name field.
// editorFields[3] - admin area field.
// editorFields[4] - locality field.
// editorFields[5] - phone number field.
// editorFields[6] - email field.
// editorFields[7] - nickname field.
Assert.assertEquals(8, editorFields.size());
editorModel.done();
verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
verify(mDelegate, times(0)).onCancel();
AutofillAddress address = mAddressCapture.getValue();
Assert.assertNotNull(address);
AutofillProfile profile = address.getProfile();
assertThat(profile.getStreetAddress(), isEmptyString());
assertThat(profile.getDependentLocality(), isEmptyString());
assertThat(profile.getCompanyName(), isEmptyString());
assertThat(profile.getPostalCode(), isEmptyString());
assertThat(profile.getSortingCode(), isEmptyString());
}
@Test
@SmallTest
public void accountSavingDisallowedForUnsupportedCountry() {
when(mPersonalDataManager.isEligibleForAddressAccountStorage()).thenReturn(true);
when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate, /*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS, "US");
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS, "CU");
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
EditorFieldModel countryDropdown = editorFields.get(0);
countryDropdown.setDropdownKey("CU", () -> {});
// Set values of the required fields.
editorFields.get(2).setValue("New Name");
editorFields.get(4).setValue("Locality");
editorFields.get(5).setValue("Dependent locality");
editorFields.get(8).setValue("Postal code");
editorFields.get(9).setValue("Street address");
editorModel.done();
verify(mDelegate, times(1)).onDone(mAddressCapture.capture());
verify(mDelegate, times(0)).onCancel();
AutofillAddress address = mAddressCapture.getValue();
Assert.assertNotNull(address);
Assert.assertEquals(Source.LOCAL_OR_SYNCABLE, address.getProfile().getSource());
}
@Test
@SmallTest
public void countryDropDownExcludesUnsupportedCountries_saveInAccountFlow() {
when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, new AutofillProfile(sAccountProfile)),
/*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
assertThat(editorFields.get(0).getDropdownKeys(), containsInAnyOrder("US", "DE"));
}
@Test
@SmallTest
public void countryDropDownExcludesUnsupportedCountries_MigrationFlow() {
when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, new AutofillProfile(sLocalProfile)),
/*saveToDisk=*/false,
/*isUpdate=*/false,
/*isMigrationToAccount=*/true);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
assertThat(editorFields.get(0).getDropdownKeys(), containsInAnyOrder("US", "DE"));
}
@Test
@SmallTest
public void countryDropDownExcludesUnsupportedCountries_editExistingAccountProfile() {
when(mPersonalDataManager.isCountryEligibleForAccountStorage(eq("CU"))).thenReturn(false);
mAddressEditor = new AddressEditor(mEditorDialog, mDelegate,
new AutofillAddress(mActivity, new AutofillProfile(sAccountProfile)),
/*saveToDisk=*/false, /*isUpdate=*/true,
/*isMigrationToAccount=*/false);
setUpAddressUiComponents(SUPPORTED_ADDRESS_FIELDS);
mAddressEditor.showEditorDialog();
EditorModel editorModel = mEditorModelCapture.getValue();
Assert.assertNotNull(editorModel);
List<EditorFieldModel> editorFields = editorModel.getFields();
Assert.assertEquals(13, editorFields.size());
assertThat(editorFields.get(0).getDropdownKeys(), containsInAnyOrder("US", "DE"));
}
}