Commit 80155b30 authored by dfalcantara's avatar dfalcantara Committed by Commit bot

[Payments] Use the correct index when checking addresses

We have an off-by-one error now that the mOptionLayout can have multiple children in it above the actual options.

* Use the correct index when indexing into the options layout.

* Add a test that checks for the crash and additionally checks that the "merchant doesn't accept address" string is
correctly displayed.

* Plumbs a new callback that indicates that the client has finished
checking the selection.

BUG=630348

Review-Url: https://codereview.chromium.org/2170603006
Cr-Commit-Position: refs/heads/master@{#407197}
parent d577494f
...@@ -540,7 +540,7 @@ public abstract class PaymentRequestSection extends LinearLayout { ...@@ -540,7 +540,7 @@ public abstract class PaymentRequestSection extends LinearLayout {
* + The "label" is text describing the row. * + The "label" is text describing the row.
* + The "icon" is a logo representing the option, like a credit card. * + The "icon" is a logo representing the option, like a credit card.
*/ */
private class OptionRow { public class OptionRow {
private static final int OPTION_ROW_TYPE_OPTION = 0; private static final int OPTION_ROW_TYPE_OPTION = 0;
private static final int OPTION_ROW_TYPE_ADD = 1; private static final int OPTION_ROW_TYPE_ADD = 1;
private static final int OPTION_ROW_TYPE_DESCRIPTION = 2; private static final int OPTION_ROW_TYPE_DESCRIPTION = 2;
...@@ -589,6 +589,12 @@ public abstract class PaymentRequestSection extends LinearLayout { ...@@ -589,6 +589,12 @@ public abstract class PaymentRequestSection extends LinearLayout {
mButton.setId(id); mButton.setId(id);
} }
/** @return the label for the row. */
@VisibleForTesting
public CharSequence getLabelText() {
return mLabel.getText();
}
private View createButton( private View createButton(
GridLayout parent, int rowIndex, boolean isSelected, boolean isEnabled) { GridLayout parent, int rowIndex, boolean isSelected, boolean isEnabled) {
if (mRowType == OPTION_ROW_TYPE_DESCRIPTION) return null; if (mRowType == OPTION_ROW_TYPE_DESCRIPTION) return null;
...@@ -881,21 +887,22 @@ public abstract class PaymentRequestSection extends LinearLayout { ...@@ -881,21 +887,22 @@ public abstract class PaymentRequestSection extends LinearLayout {
mLabelsForTest.clear(); mLabelsForTest.clear();
// Show any additional text requested by the layout. // Show any additional text requested by the layout.
if (!TextUtils.isEmpty(mDelegate.getAdditionalText(this))) { String additionalText = mDelegate.getAdditionalText(this);
if (!TextUtils.isEmpty(additionalText)) {
OptionRow descriptionRow = new OptionRow(mOptionLayout, OptionRow descriptionRow = new OptionRow(mOptionLayout,
mOptionLayout.getChildCount(), mOptionRows.size(),
mDelegate.isAdditionalTextDisplayingWarning(this) mDelegate.isAdditionalTextDisplayingWarning(this)
? OptionRow.OPTION_ROW_TYPE_WARNING ? OptionRow.OPTION_ROW_TYPE_WARNING
: OptionRow.OPTION_ROW_TYPE_DESCRIPTION, : OptionRow.OPTION_ROW_TYPE_DESCRIPTION,
null, false); null, false);
mOptionRows.add(descriptionRow); mOptionRows.add(descriptionRow);
descriptionRow.setLabel(mDelegate.getAdditionalText(this)); descriptionRow.setLabel(additionalText);
} }
// List out known payment options. // List out known payment options.
int firstOptionIndex = INVALID_OPTION_INDEX; int firstOptionIndex = INVALID_OPTION_INDEX;
for (int i = 0; i < information.getSize(); i++) { for (int i = 0; i < information.getSize(); i++) {
int currentRow = mOptionLayout.getChildCount(); int currentRow = mOptionRows.size();
if (firstOptionIndex == INVALID_OPTION_INDEX) firstOptionIndex = currentRow; if (firstOptionIndex == INVALID_OPTION_INDEX) firstOptionIndex = currentRow;
PaymentOption item = information.getItem(i); PaymentOption item = information.getItem(i);
...@@ -957,6 +964,12 @@ public abstract class PaymentRequestSection extends LinearLayout { ...@@ -957,6 +964,12 @@ public abstract class PaymentRequestSection extends LinearLayout {
public int getNumberOfOptionLabelsForTest() { public int getNumberOfOptionLabelsForTest() {
return mLabelsForTest.size(); return mLabelsForTest.size();
} }
/** Returns the OptionRow at the specified |index|. */
@VisibleForTesting
public OptionRow getOptionRowAtIndex(int index) {
return mOptionRows.get(index);
}
} }
/** /**
......
...@@ -193,6 +193,11 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View ...@@ -193,6 +193,11 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View
*/ */
void onPaymentRequestReadyToPay(PaymentRequestUI ui); void onPaymentRequestReadyToPay(PaymentRequestUI ui);
/**
* Called when the UI has been updated to reflect checking a selected option.
*/
void onPaymentRequestSelectionChecked(PaymentRequestUI ui);
/** /**
* Called when edit dialog is showing. * Called when edit dialog is showing.
*/ */
...@@ -329,6 +334,7 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View ...@@ -329,6 +334,7 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View
expand(null); expand(null);
} }
updatePayButtonEnabled(); updatePayButtonEnabled();
notifySelectionChecked();
} }
}; };
...@@ -1168,4 +1174,10 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View ...@@ -1168,4 +1174,10 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View
sObserverForTest.onPaymentRequestReadyToClose(this); sObserverForTest.onPaymentRequestReadyToClose(this);
} }
} }
private void notifySelectionChecked() {
if (sObserverForTest != null) {
sObserverForTest.onPaymentRequestSelectionChecked(this);
}
}
} }
...@@ -9,8 +9,8 @@ import android.test.suitebuilder.annotation.MediumTest; ...@@ -9,8 +9,8 @@ import android.test.suitebuilder.annotation.MediumTest;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.AutofillTestHelper; import org.chromium.chrome.browser.autofill.AutofillTestHelper;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
...@@ -19,57 +19,57 @@ import java.util.concurrent.TimeoutException; ...@@ -19,57 +19,57 @@ import java.util.concurrent.TimeoutException;
* and user that has 5 addresses stored in autofill settings. * and user that has 5 addresses stored in autofill settings.
*/ */
public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentRequestTestBase { public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentRequestTestBase {
public PaymentRequestDynamicShippingMultipleAddressesTest() { private static final AutofillProfile[] AUTOFILL_PROFILES = {
// This merchant requests the shipping address first before providing any shipping options. // Incomplete profile (missing phone number)
super("payment_request_dynamic_shipping_test.html");
}
@Override
public void onMainActivityStarted()
throws InterruptedException, ExecutionException, TimeoutException {
AutofillTestHelper helper = new AutofillTestHelper();
// Create an incomplete (no phone) profile with the highest frecency score.
String guid1 = helper.setProfile(
new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */, new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
"Bart Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "", "Bart Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "",
"90210", "", "US", "", "bart@simpson.com", "")); "90210", "", "US", "", "bart@simpson.com", ""),
// Create an incomplete (no phone) profile with a the second highest frecency score. // Incomplete profile.
String guid2 = helper.setProfile(
new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */, new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
"Homer Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "", "Homer Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "",
"90210", "", "US", "", "homer@simpson.com", "")); "90210", "", "US", "", "homer@simpson.com", ""),
// Create a complete profile with a middle frecency score. // Complete profile.
String guid3 = helper.setProfile(
new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */, new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
"Lisa Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "", "Lisa Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "",
"90210", "", "US", "555 123-4567", "lisa@simpson.com", "")); "90210", "", "US", "555 123-4567", "lisa@simpson.com", ""),
// Create a complete profile with the second lowest frecency score. // Complete profile in another country.
String guid4 = helper.setProfile(
new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */, new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
"Maggie Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "", "Maggie Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "",
"90210", "", "US", "555 123-4567", "maggie@simpson.com", "")); "90210", "", "Uzbekistan", "555 123-4567", "maggie@simpson.com", ""),
// Create an incomplete profile with the lowest frecency score. // Incomplete profile.
String guid5 = helper.setProfile(
new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */, new AutofillProfile("" /* guid */, "https://www.example.com" /* origin */,
"Marge Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "", "Marge Simpson", "Acme Inc.", "123 Main", "California", "Los Angeles", "",
"90210", "", "US", "", "marge@simpson.com", "")); "90210", "", "US", "", "marge@simpson.com", "")
};
// Create a credit card associated witht the fourth profile.
helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe", private AutofillProfile[] mProfilesToAdd;
"4111111111111111", "1111", "12", "2050", "visa", R.drawable.pr_visa, private int[] mCountsToSet;
guid4)); private int[] mDatesToSet;
// Set the use stats so that profile1 has the highest frecency score, profile2 the second public PaymentRequestDynamicShippingMultipleAddressesTest() {
// highest, profile 3 the second lowest and profile4 the lowest. // This merchant requests the shipping address first before providing any shipping options.
helper.setProfileUseStatsForTesting(guid1, 20, 5000); super("payment_request_dynamic_shipping_test.html");
helper.setProfileUseStatsForTesting(guid2, 15, 5000); }
helper.setProfileUseStatsForTesting(guid3, 10, 5000);
helper.setProfileUseStatsForTesting(guid4, 5, 5000); @Override
helper.setProfileUseStatsForTesting(guid5, 1, 1); public void onMainActivityStarted()
throws InterruptedException, ExecutionException, TimeoutException {
AutofillTestHelper helper = new AutofillTestHelper();
// Add the profiles.
ArrayList<String> guids = new ArrayList<>();
for (int i = 0; i < mProfilesToAdd.length; i++) {
guids.add(helper.setProfile(mProfilesToAdd[i]));
}
// Set up the profile use stats.
for (int i = 0; i < guids.size(); i++) {
helper.setProfileUseStatsForTesting(guids.get(i), mCountsToSet[i], mDatesToSet[i]);
}
} }
/** /**
...@@ -80,6 +80,14 @@ public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentR ...@@ -80,6 +80,14 @@ public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentR
@MediumTest @MediumTest
public void testShippingAddressSuggestionOrdering() public void testShippingAddressSuggestionOrdering()
throws InterruptedException, ExecutionException, TimeoutException { throws InterruptedException, ExecutionException, TimeoutException {
// Create a bunch of profiles, some complete, some incomplete. Values are set so that the
// profiles are ordered by frecency.
mProfilesToAdd = new AutofillProfile[] {
AUTOFILL_PROFILES[0], AUTOFILL_PROFILES[1], AUTOFILL_PROFILES[2],
AUTOFILL_PROFILES[3], AUTOFILL_PROFILES[4]};
mCountsToSet = new int[] {20, 15, 10, 5, 1};
mDatesToSet = new int[] {5000, 5000, 5000, 5000, 1};
triggerUIAndWait(mReadyForInput); triggerUIAndWait(mReadyForInput);
clickInShippingSummaryAndWait(R.id.payments_section, mReadyForInput); clickInShippingSummaryAndWait(R.id.payments_section, mReadyForInput);
assertEquals(4, getNumberOfShippingAddressSuggestions()); assertEquals(4, getNumberOfShippingAddressSuggestions());
...@@ -88,4 +96,30 @@ public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentR ...@@ -88,4 +96,30 @@ public class PaymentRequestDynamicShippingMultipleAddressesTest extends PaymentR
assertTrue(getShippingAddressSuggestionLabel(2).contains("Bart Simpson")); assertTrue(getShippingAddressSuggestionLabel(2).contains("Bart Simpson"));
assertTrue(getShippingAddressSuggestionLabel(3).contains("Homer Simpson")); assertTrue(getShippingAddressSuggestionLabel(3).contains("Homer Simpson"));
} }
/**
* Select a shipping address that the website refuses to accept, which should force the dialog
* to show an error.
*/
@MediumTest
public void testShippingAddresNotAcceptedByMerchant()
throws InterruptedException, ExecutionException, TimeoutException {
// Add a profile that is not accepted by the website.
mProfilesToAdd = new AutofillProfile[] {AUTOFILL_PROFILES[3]};
mCountsToSet = new int[] {5};
mDatesToSet = new int[] {5000};
// Click on the unacceptable shipping address.
triggerUIAndWait(mReadyForInput);
clickInShippingSummaryAndWait(R.id.payments_section, mReadyForInput);
assertTrue(getShippingAddressSuggestionLabel(0).contains(
AUTOFILL_PROFILES[3].getFullName()));
clickOnShippingAddressSuggestionOptionAndWait(0, mSelectionChecked);
// The string should indicate that the shipping address isn't valid.
CharSequence actualString = getShippingAddressOptionRowAtIndex(0).getLabelText();
CharSequence expectedString = getInstrumentation().getTargetContext().getString(
R.string.payments_unsupported_shipping_address);
assertEquals(expectedString, actualString);
}
} }
...@@ -22,6 +22,7 @@ import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryA ...@@ -22,6 +22,7 @@ import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryA
import org.chromium.chrome.browser.payments.PaymentRequestImpl.PaymentRequestServiceObserverForTest; import org.chromium.chrome.browser.payments.PaymentRequestImpl.PaymentRequestServiceObserverForTest;
import org.chromium.chrome.browser.payments.ui.EditorTextField; import org.chromium.chrome.browser.payments.ui.EditorTextField;
import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection; import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection;
import org.chromium.chrome.browser.payments.ui.PaymentRequestSection.OptionSection.OptionRow;
import org.chromium.chrome.browser.payments.ui.PaymentRequestUI; import org.chromium.chrome.browser.payments.ui.PaymentRequestUI;
import org.chromium.chrome.browser.payments.ui.PaymentRequestUI.PaymentRequestObserverForTest; import org.chromium.chrome.browser.payments.ui.PaymentRequestUI.PaymentRequestObserverForTest;
import org.chromium.chrome.test.ChromeActivityTestCaseBase; import org.chromium.chrome.test.ChromeActivityTestCaseBase;
...@@ -64,6 +65,7 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -64,6 +65,7 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
protected final PaymentsCallbackHelper<PaymentRequestUI> mReadyForInput; protected final PaymentsCallbackHelper<PaymentRequestUI> mReadyForInput;
protected final PaymentsCallbackHelper<PaymentRequestUI> mReadyToPay; protected final PaymentsCallbackHelper<PaymentRequestUI> mReadyToPay;
protected final PaymentsCallbackHelper<PaymentRequestUI> mReadyToClose; protected final PaymentsCallbackHelper<PaymentRequestUI> mReadyToClose;
protected final PaymentsCallbackHelper<PaymentRequestUI> mSelectionChecked;
protected final PaymentsCallbackHelper<PaymentRequestUI> mResultReady; protected final PaymentsCallbackHelper<PaymentRequestUI> mResultReady;
protected final PaymentsCallbackHelper<CardUnmaskPrompt> mReadyForUnmaskInput; protected final PaymentsCallbackHelper<CardUnmaskPrompt> mReadyForUnmaskInput;
protected final PaymentsCallbackHelper<CardUnmaskPrompt> mReadyToUnmask; protected final PaymentsCallbackHelper<CardUnmaskPrompt> mReadyToUnmask;
...@@ -86,6 +88,7 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -86,6 +88,7 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
mReadyForInput = new PaymentsCallbackHelper<>(); mReadyForInput = new PaymentsCallbackHelper<>();
mReadyToPay = new PaymentsCallbackHelper<>(); mReadyToPay = new PaymentsCallbackHelper<>();
mReadyToClose = new PaymentsCallbackHelper<>(); mReadyToClose = new PaymentsCallbackHelper<>();
mSelectionChecked = new PaymentsCallbackHelper<>();
mResultReady = new PaymentsCallbackHelper<>(); mResultReady = new PaymentsCallbackHelper<>();
mReadyForUnmaskInput = new PaymentsCallbackHelper<>(); mReadyForUnmaskInput = new PaymentsCallbackHelper<>();
mReadyToUnmask = new PaymentsCallbackHelper<>(); mReadyToUnmask = new PaymentsCallbackHelper<>();
...@@ -325,7 +328,28 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -325,7 +328,28 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
} }
/** /**
* Returns the the number of shipping address suggestions, * Clicks on the label corresponding to the shipping address suggestion at the specified
* |suggestionIndex|.
* @throws InterruptedException
*/
protected void clickOnShippingAddressSuggestionOptionAndWait(
final int suggestionIndex, CallbackHelper helper)
throws ExecutionException, TimeoutException, InterruptedException {
assert (suggestionIndex < getNumberOfShippingAddressSuggestions());
int callCount = helper.getCallCount();
ThreadUtils.runOnUiThreadBlocking(new Runnable() {
@Override
public void run() {
((OptionSection) mUI.getShippingAddressSectionForTest())
.getOptionLabelsForTest(suggestionIndex).performClick();
}
});
helper.waitForCallback(callCount);
}
/**
* Returns the the number of shipping address suggestions.
*/ */
protected int getNumberOfShippingAddressSuggestions() throws ExecutionException { protected int getNumberOfShippingAddressSuggestions() throws ExecutionException {
return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() { return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
...@@ -337,6 +361,18 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -337,6 +361,18 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
}); });
} }
/** Returns the {@link OptionRow} at the given index for the shipping address section. */
protected OptionRow getShippingAddressOptionRowAtIndex(final int index)
throws ExecutionException {
return ThreadUtils.runOnUiThreadBlocking(new Callable<OptionRow>() {
@Override
public OptionRow call() {
return ((OptionSection) mUI.getShippingAddressSectionForTest())
.getOptionRowAtIndex(index);
}
});
}
/** Selects the spinner value in the editor UI for credit cards. */ /** Selects the spinner value in the editor UI for credit cards. */
protected void setSpinnerSelectionsInCardEditorAndWait(final int[] selections, protected void setSpinnerSelectionsInCardEditorAndWait(final int[] selections,
CallbackHelper helper) throws InterruptedException, TimeoutException { CallbackHelper helper) throws InterruptedException, TimeoutException {
...@@ -496,6 +532,12 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -496,6 +532,12 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
mReadyToClose.notifyCalled(ui); mReadyToClose.notifyCalled(ui);
} }
@Override
public void onPaymentRequestSelectionChecked(PaymentRequestUI ui) {
ThreadUtils.assertOnUiThread();
mSelectionChecked.notifyCalled(ui);
}
@Override @Override
public void onPaymentRequestResultReady(PaymentRequestUI ui) { public void onPaymentRequestResultReady(PaymentRequestUI ui) {
ThreadUtils.assertOnUiThread(); ThreadUtils.assertOnUiThread();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment