Commit 84204fda authored by rouslan's avatar rouslan Committed by Commit bot

Autofill cards at the bottom of the PaymentRequest UI

This patch arranges the payment instruments in the following order:
> Non-autofill payment apps
> Complete credit cards from Autofill.
> Incomplete credit card from Autofill (need editing before sending to
  the merchant).

BUG=629882

Review-Url: https://codereview.chromium.org/2165163002
Cr-Commit-Position: refs/heads/master@{#407179}
parent d2a8f72e
...@@ -53,6 +53,7 @@ import java.util.HashMap; ...@@ -53,6 +53,7 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.Set; import java.util.Set;
/** /**
...@@ -89,8 +90,14 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -89,8 +90,14 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
} }
private static final String TAG = "cr_PaymentRequest"; private static final String TAG = "cr_PaymentRequest";
private static final int SUGGESTIONS_LIMIT = 4; private static final int SUGGESTIONS_LIMIT = 4;
private static final Comparator<Completable> COMPLETENESS_COMPARATOR =
new Comparator<Completable>() {
@Override
public int compare(Completable a, Completable b) {
return (b.isComplete() ? 1 : 0) - (a.isComplete() ? 1 : 0);
}
};
private static PaymentRequestServiceObserverForTest sObserverForTest; private static PaymentRequestServiceObserverForTest sObserverForTest;
...@@ -127,12 +134,12 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -127,12 +134,12 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
*/ */
private SectionInformation mUiShippingOptions; private SectionInformation mUiShippingOptions;
private HashMap<String, JSONObject> mMethodData; private Map<String, JSONObject> mMethodData;
private SectionInformation mShippingAddressesSection; private SectionInformation mShippingAddressesSection;
private SectionInformation mContactSection; private SectionInformation mContactSection;
private List<PaymentApp> mPendingApps; private List<PaymentApp> mPendingApps;
private int mFirstCompletePendingInstrument;
private List<PaymentInstrument> mPendingInstruments; private List<PaymentInstrument> mPendingInstruments;
private List<PaymentInstrument> mPendingAutofillInstruments;
private SectionInformation mPaymentMethodsSection; private SectionInformation mPaymentMethodsSection;
private PaymentRequestUI mUI; private PaymentRequestUI mUI;
private Callback<PaymentInformation> mPaymentInformationCallback; private Callback<PaymentInformation> mPaymentInformationCallback;
...@@ -237,20 +244,6 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -237,20 +244,6 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
return; return;
} }
// Create a comparator to sort the suggestions by completeness.
Comparator<Completable> completenessComparator = new Comparator<Completable>() {
@Override
public int compare(Completable a, Completable b) {
if (a.isComplete() == b.isComplete()) {
return 0;
} else if (a.isComplete()) {
return -1;
} else {
return 1;
}
}
};
// If the merchant requests shipping and does not provide a selected shipping option, then // If the merchant requests shipping and does not provide a selected shipping option, then
// the merchant needs the shipping address to calculate the shipping price and availability. // the merchant needs the shipping address to calculate the shipping price and availability.
boolean requestShipping = options != null && options.requestShipping; boolean requestShipping = options != null && options.requestShipping;
...@@ -277,7 +270,7 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -277,7 +270,7 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
} }
// Suggest complete addresses first. // Suggest complete addresses first.
Collections.sort(addresses, completenessComparator); Collections.sort(addresses, COMPLETENESS_COMPARATOR);
// Limit the number of suggestions. // Limit the number of suggestions.
addresses = addresses.subList(0, Math.min(addresses.size(), SUGGESTIONS_LIMIT)); addresses = addresses.subList(0, Math.min(addresses.size(), SUGGESTIONS_LIMIT));
...@@ -324,7 +317,7 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -324,7 +317,7 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
} }
// Suggest complete contact infos first. // Suggest complete contact infos first.
Collections.sort(contacts, completenessComparator); Collections.sort(contacts, COMPLETENESS_COMPARATOR);
// Limit the number of suggestions. // Limit the number of suggestions.
contacts = contacts.subList(0, Math.min(contacts.size(), SUGGESTIONS_LIMIT)); contacts = contacts.subList(0, Math.min(contacts.size(), SUGGESTIONS_LIMIT));
...@@ -351,11 +344,11 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -351,11 +344,11 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
if (mContactEditor != null) mContactEditor.setEditorView(mUI.getEditorView()); if (mContactEditor != null) mContactEditor.setEditorView(mUI.getEditorView());
} }
private static HashMap<String, JSONObject> getValidatedMethodData( private static Map<String, JSONObject> getValidatedMethodData(
PaymentMethodData[] methodData, CardEditor paymentMethodsCollector) { PaymentMethodData[] methodData, CardEditor paymentMethodsCollector) {
// Payment methodData are required. // Payment methodData are required.
if (methodData == null || methodData.length == 0) return null; if (methodData == null || methodData.length == 0) return null;
HashMap<String, JSONObject> result = new HashMap<>(); Map<String, JSONObject> result = new HashMap<>();
for (int i = 0; i < methodData.length; i++) { for (int i = 0; i < methodData.length; i++) {
JSONObject data = null; JSONObject data = null;
if (!TextUtils.isEmpty(methodData[i].stringifiedData)) { if (!TextUtils.isEmpty(methodData[i].stringifiedData)) {
...@@ -395,10 +388,11 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -395,10 +388,11 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
*/ */
private boolean getMatchingPaymentInstruments() { private boolean getMatchingPaymentInstruments() {
mPendingApps = new ArrayList<>(mApps); mPendingApps = new ArrayList<>(mApps);
mFirstCompletePendingInstrument = SectionInformation.NO_SELECTION;
mPendingInstruments = new ArrayList<>(); mPendingInstruments = new ArrayList<>();
mPendingAutofillInstruments = new ArrayList<>();
boolean arePaymentMethodsSupported = false; boolean arePaymentMethodsSupported = false;
Map<PaymentApp, JSONObject> queryApps = new HashMap<>();
for (int i = 0; i < mApps.size(); i++) { for (int i = 0; i < mApps.size(); i++) {
PaymentApp app = mApps.get(i); PaymentApp app = mApps.get(i);
Set<String> appMethods = app.getSupportedMethodNames(); Set<String> appMethods = app.getSupportedMethodNames();
...@@ -407,11 +401,18 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -407,11 +401,18 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
mPendingApps.remove(app); mPendingApps.remove(app);
} else { } else {
arePaymentMethodsSupported = true; arePaymentMethodsSupported = true;
mMerchantSupportsAutofillPaymentInstruments = app instanceof AutofillPaymentApp; mMerchantSupportsAutofillPaymentInstruments |= app instanceof AutofillPaymentApp;
app.getInstruments(mMethodData.get(appMethods.iterator().next()), this); queryApps.put(app, mMethodData.get(appMethods.iterator().next()));
} }
} }
// Query instruments after mMerchantSupportsAutofillPaymentInstruments has been initialized,
// so a fast response from a non-autofill payment app at the front of the app list does not
// cause NOT_SUPPORTED payment rejection.
for (Map.Entry<PaymentApp, JSONObject> q : queryApps.entrySet()) {
q.getKey().getInstruments(q.getValue(), this);
}
return arePaymentMethodsSupported; return arePaymentMethodsSupported;
} }
...@@ -868,12 +869,13 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -868,12 +869,13 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instruments) { public void onInstrumentsReady(PaymentApp app, List<PaymentInstrument> instruments) {
mPendingApps.remove(app); mPendingApps.remove(app);
// Place the instruments into either "autofill" or "non-autofill" list to be displayed when
// all apps have responded.
if (instruments != null) { if (instruments != null) {
for (int i = 0; i < instruments.size(); i++) { for (int i = 0; i < instruments.size(); i++) {
PaymentInstrument instrument = instruments.get(i); PaymentInstrument instrument = instruments.get(i);
if (mMethodData.containsKey(instrument.getMethodName())) { if (mMethodData.containsKey(instrument.getMethodName())) {
checkForCompletePaymentInstrument(instrument, mPendingInstruments.size()); addPendingInstrument(instrument);
mPendingInstruments.add(instrument);
} else { } else {
instrument.dismiss(); instrument.dismiss();
} }
...@@ -893,25 +895,56 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie ...@@ -893,25 +895,56 @@ public class PaymentRequestImpl implements PaymentRequest, PaymentRequestUI.Clie
return; return;
} }
// List order:
// > Non-autofill instruments.
// > Complete autofill instruments.
// > Incomplete autofill instruments.
Collections.sort(mPendingAutofillInstruments, COMPLETENESS_COMPARATOR);
mPendingInstruments.addAll(mPendingAutofillInstruments);
mPendingAutofillInstruments.clear();
mPendingAutofillInstruments = null;
// Pre-select the first instrument on the list, if it is complete.
int selection = SectionInformation.NO_SELECTION;
if (!mPendingInstruments.isEmpty()) {
PaymentInstrument first = mPendingInstruments.get(0);
if (!(first instanceof AutofillPaymentInstrument)
|| ((AutofillPaymentInstrument) first).isComplete()) {
selection = 0;
}
}
// The list of payment instruments is ready to display. // The list of payment instruments is ready to display.
mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYPE_PAYMENT_METHODS, mPaymentMethodsSection = new SectionInformation(PaymentRequestUI.TYPE_PAYMENT_METHODS,
mFirstCompletePendingInstrument, mPendingInstruments); selection, mPendingInstruments);
mPendingInstruments.clear(); mPendingInstruments.clear();
mPendingInstruments = null;
// UI has requested the full list of payment instruments. Provide it now. // UI has requested the full list of payment instruments. Provide it now.
if (mPaymentInformationCallback != null) providePaymentInformation(); if (mPaymentInformationCallback != null) providePaymentInformation();
} }
private void checkForCompletePaymentInstrument(PaymentInstrument instrument, int index) { /**
boolean isComplete = true; * Saves the given instrument in either "autofill" or "non-autofill" list. The separation
* enables placing autofill instruments on the bottom of the list.
*
* Autofill instruments are also checked for completeness. A complete autofill instrument can be
* sent to the merchant as-is, without editing first. Such instruments should be displayed
* higher in the list.
*
* @param instrument The instrument to add to either "autofill" or "non-autofill" list.
*/
private void addPendingInstrument(PaymentInstrument instrument) {
if (instrument instanceof AutofillPaymentInstrument) { if (instrument instanceof AutofillPaymentInstrument) {
AutofillPaymentInstrument autofillInstrument = (AutofillPaymentInstrument) instrument; AutofillPaymentInstrument autofillInstrument = (AutofillPaymentInstrument) instrument;
isComplete = mCardEditor.isCardComplete(autofillInstrument.getCard()); if (mCardEditor.isCardComplete(autofillInstrument.getCard())) {
if (isComplete) autofillInstrument.setIsComplete(); autofillInstrument.setIsComplete();
} }
mPendingAutofillInstruments.add(instrument);
if (isComplete && mFirstCompletePendingInstrument == SectionInformation.NO_SELECTION) { } else {
mFirstCompletePendingInstrument = index; mPendingInstruments.add(instrument);
} }
} }
......
...@@ -1198,6 +1198,7 @@ chrome_test_java_sources = [ ...@@ -1198,6 +1198,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompletePhoneTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestIncompletePhoneTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestNoShippingTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPaymentAppAndCardsTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestPhoneTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRemoveBillingAddressTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestBase.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestTestBase.java",
......
// Copyright 2016 The Chromium Authors. All rights reserved.
// 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.payments;
import android.content.DialogInterface;
import android.test.suitebuilder.annotation.MediumTest;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.autofill.AutofillTestHelper;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
/**
* A payment integration test for a merchant that requests payment via Bob Pay or cards.
*/
public class PaymentRequestPaymentAppAndCardsTest extends PaymentRequestTestBase {
public PaymentRequestPaymentAppAndCardsTest() {
super("payment_request_bobpay_and_cards_test.html");
}
@Override
public void onMainActivityStarted() throws InterruptedException, ExecutionException,
TimeoutException {
AutofillTestHelper helper = new AutofillTestHelper();
String billingAddressId = helper.setProfile(new AutofillProfile("", "https://example.com",
true, "Jon Doe", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
"US", "310-310-6000", "jon.doe@gmail.com", "en-US"));
// Mastercard card without a billing address.
helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
"5454545454545454", "", "12", "2050", "mastercard", R.drawable.pr_mc,
""));
// Visa card with complete set of information.
helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
"4111111111111111", "", "12", "2050", "visa", R.drawable.pr_visa,
billingAddressId));
}
/**
* If Bob Pay does not have any instruments, show [visa, mastercard]. Here the payment app
* responds quickly.
*/
@MediumTest
public void testNoInstrumentsInFastBobPay() throws InterruptedException, ExecutionException,
TimeoutException {
runTest(NO_INSTRUMENTS, IMMEDIATE_RESPONSE);
}
/**
* If Bob Pay does not have any instruments, show [visa, mastercard]. Here the payment app
* responds slowly.
*/
@MediumTest
public void testNoInstrumentsInSlowBobPay() throws InterruptedException, ExecutionException,
TimeoutException {
runTest(NO_INSTRUMENTS, DELAYED_RESPONSE);
}
/**
* If Bob Pay has instruments, show [bobpay, visa, mastercard]. Here the payment app responds
* quickly.
*/
@MediumTest
public void testHaveInstrumentsInFastBobPay() throws InterruptedException, ExecutionException,
TimeoutException {
runTest(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
}
/**
* If Bob Pay has instruments, show [bobpay, visa, mastercard]. Here the payment app responds
* slowly.
*/
@MediumTest
public void testHaveInstrumentsInSlowBobPay() throws InterruptedException, ExecutionException,
TimeoutException {
runTest(HAVE_INSTRUMENTS, DELAYED_RESPONSE);
}
private void runTest(int instrumentPresence, int responseSpeed) throws InterruptedException,
ExecutionException, TimeoutException {
installPaymentApp(instrumentPresence, responseSpeed);
triggerUIAndWait(mReadyToPay);
clickInPaymentMethodAndWait(R.id.payments_section, mReadyForInput);
// Check the number of instruments.
assertEquals(
instrumentPresence == HAVE_INSTRUMENTS ? 3 : 2, getNumberOfPaymentInstruments());
// Check the labesl of the instruments.
int i = 0;
if (instrumentPresence == HAVE_INSTRUMENTS) {
assertEquals("Bob Pay", getPaymentInstrumentLabel(i++));
}
// \u00A0\u22EF is a non-breaking space followed by a midline ellipsis.
assertEquals("Visa\u00A0\u22EF1111\nJon Doe", getPaymentInstrumentLabel(i++));
assertEquals("MasterCard\u00A0\u22EF5454\nJon Doe", getPaymentInstrumentLabel(i++));
// Check the output of the selected instrument.
if (instrumentPresence == HAVE_INSTRUMENTS) {
clickAndWait(R.id.button_primary, mDismissed);
expectResultContains(new String[]{"https://bobpay.com", "\"transaction\"", "1337"});
} else {
clickAndWait(R.id.button_primary, mReadyForUnmaskInput);
setTextInCardUnmaskDialogAndWait(R.id.card_unmask_input, "123", mReadyToUnmask);
clickCardUnmaskButtonAndWait(DialogInterface.BUTTON_POSITIVE, mDismissed);
expectResultContains(new String[] {"Jon Doe", "4111111111111111", "12", "2050", "visa",
"123"});
}
}
}
...@@ -4,20 +4,10 @@ ...@@ -4,20 +4,10 @@
package org.chromium.chrome.browser.payments; package org.chromium.chrome.browser.payments;
import android.os.Handler;
import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.MediumTest;
import org.chromium.base.ThreadUtils;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
import org.chromium.content_public.browser.WebContents;
import org.chromium.mojom.payments.PaymentItem;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
...@@ -25,12 +15,6 @@ import java.util.concurrent.TimeoutException; ...@@ -25,12 +15,6 @@ import java.util.concurrent.TimeoutException;
* A payment integration test for a merchant that requests payment via Bob Pay. * A payment integration test for a merchant that requests payment via Bob Pay.
*/ */
public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase { public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase {
private static final int NO_INSTRUMENTS = 0;
private static final int HAVE_INSTRUMENTS = 1;
private static final int IMMEDIATE_RESPONSE = 0;
private static final int DELAYED_RESPONSE = 1;
public PaymentRequestPaymentAppTest() { public PaymentRequestPaymentAppTest() {
super("payment_request_bobpay_test.html"); super("payment_request_bobpay_test.html");
} }
...@@ -55,7 +39,7 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase { ...@@ -55,7 +39,7 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase {
@MediumTest @MediumTest
public void testNoInstrumentsInFastBobPay() throws InterruptedException, ExecutionException, public void testNoInstrumentsInFastBobPay() throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
installBobPay(NO_INSTRUMENTS, IMMEDIATE_RESPONSE); installPaymentApp(NO_INSTRUMENTS, IMMEDIATE_RESPONSE);
triggerUIAndWait(mShowFailed); triggerUIAndWait(mShowFailed);
expectResultContains( expectResultContains(
new String[]{"show() rejected", "The payment method is not supported"}); new String[]{"show() rejected", "The payment method is not supported"});
...@@ -68,7 +52,7 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase { ...@@ -68,7 +52,7 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase {
@MediumTest @MediumTest
public void testNoInstrumentsInSlowBobPay() throws InterruptedException, ExecutionException, public void testNoInstrumentsInSlowBobPay() throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
installBobPay(NO_INSTRUMENTS, DELAYED_RESPONSE); installPaymentApp(NO_INSTRUMENTS, DELAYED_RESPONSE);
triggerUIAndWait(mShowFailed); triggerUIAndWait(mShowFailed);
expectResultContains( expectResultContains(
new String[]{"show() rejected", "The payment method is not supported"}); new String[]{"show() rejected", "The payment method is not supported"});
...@@ -81,7 +65,7 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase { ...@@ -81,7 +65,7 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase {
@MediumTest @MediumTest
public void testPayViaFastBobPay() throws InterruptedException, ExecutionException, public void testPayViaFastBobPay() throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
installBobPay(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE); installPaymentApp(HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE);
triggerUIAndWait(mReadyToPay); triggerUIAndWait(mReadyToPay);
clickAndWait(R.id.button_primary, mDismissed); clickAndWait(R.id.button_primary, mDismissed);
expectResultContains(new String[]{"https://bobpay.com", "\"transaction\"", "1337"}); expectResultContains(new String[]{"https://bobpay.com", "\"transaction\"", "1337"});
...@@ -94,92 +78,9 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase { ...@@ -94,92 +78,9 @@ public class PaymentRequestPaymentAppTest extends PaymentRequestTestBase {
@MediumTest @MediumTest
public void testPayViaSlowBobPay() throws InterruptedException, ExecutionException, public void testPayViaSlowBobPay() throws InterruptedException, ExecutionException,
TimeoutException { TimeoutException {
installBobPay(HAVE_INSTRUMENTS, DELAYED_RESPONSE); installPaymentApp(HAVE_INSTRUMENTS, DELAYED_RESPONSE);
triggerUIAndWait(mReadyToPay); triggerUIAndWait(mReadyToPay);
clickAndWait(R.id.button_primary, mDismissed); clickAndWait(R.id.button_primary, mDismissed);
expectResultContains(new String[]{"https://bobpay.com", "\"transaction\"", "1337"}); expectResultContains(new String[]{"https://bobpay.com", "\"transaction\"", "1337"});
} }
/**
* Installs a payment app for testing.
*
* @param instrumentPresence Whether Bob Pay has any payment instruments. Either NO_INSTRUMENTS
* or HAVE_INSTRUMENTS.
* @param responseSpeed How quickly Bob Pay will respond to "get instruments" query. Either
* IMMEDIATE_RESPONSE or DELAYED_RESPONSE.
*/
private void installBobPay(final int instrumentPresence, final int responseSpeed) {
PaymentAppFactory.setAdditionalFactory(new PaymentAppFactoryAddition() {
@Override
public List<PaymentApp> create(WebContents webContents) {
List<PaymentApp> additionalApps = new ArrayList<>();
additionalApps.add(new BobPay(instrumentPresence, responseSpeed));
return additionalApps;
}
});
}
/** A payment app implementation for test. */
private static class BobPay implements PaymentApp {
private final int mInstrumentPresence;
private final int mResponseSpeed;
BobPay(int instrumentPresence, int responseSpeed) {
mInstrumentPresence = instrumentPresence;
mResponseSpeed = responseSpeed;
}
@Override
public void getInstruments(JSONObject details, final InstrumentsCallback
instrumentsCallback) {
final List<PaymentInstrument> instruments = new ArrayList<>();
if (mInstrumentPresence == HAVE_INSTRUMENTS) instruments.add(new BobPayInstrument());
Runnable instrumentsReady = new Runnable() {
@Override
public void run() {
ThreadUtils.assertOnUiThread();
instrumentsCallback.onInstrumentsReady(BobPay.this, instruments);
}
};
if (mResponseSpeed == IMMEDIATE_RESPONSE) {
instrumentsReady.run();
} else {
new Handler().postDelayed(instrumentsReady, 100);
}
}
@Override
public Set<String> getSupportedMethodNames() {
Set<String> methodNames = new HashSet<>();
methodNames.add("https://bobpay.com");
return methodNames;
}
@Override
public String getIdentifier() {
return "https://bobpay.com";
}
}
/** A payment instrument implementation for test. */
private static class BobPayInstrument extends PaymentInstrument {
BobPayInstrument() {
super("https://bobpay.com", "Bob Pay", null, NO_ICON);
}
@Override
public String getMethodName() {
return "https://bobpay.com";
}
@Override
public void getDetails(String merchantName, String origin, PaymentItem total,
List<PaymentItem> cart, JSONObject details, DetailsCallback detailsCallback) {
detailsCallback.onInstrumentDetailsReady(
"https://bobpay.com", "{\"transaction\": 1337}");
}
@Override
public void dismiss() {}
}
} }
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
package org.chromium.chrome.browser.payments; package org.chromium.chrome.browser.payments;
import android.os.Handler;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
...@@ -17,6 +18,7 @@ import org.chromium.chrome.R; ...@@ -17,6 +18,7 @@ import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.autofill.CardUnmaskPrompt; import org.chromium.chrome.browser.autofill.CardUnmaskPrompt;
import org.chromium.chrome.browser.autofill.CardUnmaskPrompt.CardUnmaskObserverForTest; import org.chromium.chrome.browser.autofill.CardUnmaskPrompt.CardUnmaskObserverForTest;
import org.chromium.chrome.browser.payments.PaymentAppFactory.PaymentAppFactoryAddition;
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;
...@@ -29,8 +31,13 @@ import org.chromium.content.browser.test.util.Criteria; ...@@ -29,8 +31,13 @@ import org.chromium.content.browser.test.util.Criteria;
import org.chromium.content.browser.test.util.CriteriaHelper; import org.chromium.content.browser.test.util.CriteriaHelper;
import org.chromium.content.browser.test.util.DOMUtils; import org.chromium.content.browser.test.util.DOMUtils;
import org.chromium.content_public.browser.WebContents; import org.chromium.content_public.browser.WebContents;
import org.chromium.mojom.payments.PaymentItem;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
...@@ -42,6 +49,18 @@ import java.util.concurrent.atomic.AtomicReference; ...@@ -42,6 +49,18 @@ import java.util.concurrent.atomic.AtomicReference;
abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeActivity> abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeActivity>
implements PaymentRequestObserverForTest, PaymentRequestServiceObserverForTest, implements PaymentRequestObserverForTest, PaymentRequestServiceObserverForTest,
CardUnmaskObserverForTest { CardUnmaskObserverForTest {
/** Flag for installing a payment app without instruments. */
protected static final int NO_INSTRUMENTS = 0;
/** Flag for installing a payment app with instruments. */
protected static final int HAVE_INSTRUMENTS = 1;
/** Flag for installing a fast payment app. */
protected static final int IMMEDIATE_RESPONSE = 0;
/** Flag for installing a slow payment app. */
protected static final int DELAYED_RESPONSE = 1;
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;
...@@ -240,14 +259,23 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -240,14 +259,23 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
}); });
} }
/** Returns the label corresponding to the payment instrument at the specified |index|. */
protected String getPaymentInstrumentLabel(final int index) throws ExecutionException {
return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
@Override
public String call() {
return ((OptionSection) mUI.getPaymentMethodSectionForTest())
.getOptionLabelsForTest(index).getText().toString();
}
});
}
/** /**
* Returns the label corresponding to the contact detail suggestion at the specified * Returns the label corresponding to the contact detail suggestion at the specified
* |suggestionIndex|. * |suggestionIndex|.
*/ */
protected String getContactDetailsSuggestionLabel(final int suggestionIndex) protected String getContactDetailsSuggestionLabel(final int suggestionIndex)
throws ExecutionException { throws ExecutionException {
assert (suggestionIndex < getNumberOfContactDetailSuggestions());
return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() { return ThreadUtils.runOnUiThreadBlocking(new Callable<String>() {
@Override @Override
public String call() { public String call() {
...@@ -257,9 +285,18 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -257,9 +285,18 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
}); });
} }
/** /** Returns the the number of payment instruments. */
* Returns the the number of contact detail suggestions, protected int getNumberOfPaymentInstruments() throws ExecutionException {
*/ return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
@Override
public Integer call() {
return ((OptionSection) mUI.getPaymentMethodSectionForTest())
.getNumberOfOptionLabelsForTest();
}
});
}
/** Returns the the number of contact detail suggestions. */
protected int getNumberOfContactDetailSuggestions() throws ExecutionException { protected int getNumberOfContactDetailSuggestions() throws ExecutionException {
return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() { return ThreadUtils.runOnUiThreadBlocking(new Callable<Integer>() {
@Override @Override
...@@ -528,4 +565,87 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA ...@@ -528,4 +565,87 @@ abstract class PaymentRequestTestBase extends ChromeActivityTestCaseBase<ChromeA
notifyCalled(); notifyCalled();
} }
} }
/**
* Installs a payment app for testing.
*
* @param instrumentPresence Whether the app has any payment instruments. Either NO_INSTRUMENTS
* or HAVE_INSTRUMENTS.
* @param responseSpeed How quickly the app will respond to "get instruments" query. Either
* IMMEDIATE_RESPONSE or DELAYED_RESPONSE.
*/
protected void installPaymentApp(final int instrumentPresence, final int responseSpeed) {
PaymentAppFactory.setAdditionalFactory(new PaymentAppFactoryAddition() {
@Override
public List<PaymentApp> create(WebContents webContents) {
List<PaymentApp> additionalApps = new ArrayList<>();
additionalApps.add(new BobPay(instrumentPresence, responseSpeed));
return additionalApps;
}
});
}
/** A payment app implementation for test. */
private static class BobPay implements PaymentApp {
private final int mInstrumentPresence;
private final int mResponseSpeed;
BobPay(int instrumentPresence, int responseSpeed) {
mInstrumentPresence = instrumentPresence;
mResponseSpeed = responseSpeed;
}
@Override
public void getInstruments(JSONObject details, final InstrumentsCallback
instrumentsCallback) {
final List<PaymentInstrument> instruments = new ArrayList<>();
if (mInstrumentPresence == HAVE_INSTRUMENTS) instruments.add(new BobPayInstrument());
Runnable instrumentsReady = new Runnable() {
@Override
public void run() {
ThreadUtils.assertOnUiThread();
instrumentsCallback.onInstrumentsReady(BobPay.this, instruments);
}
};
if (mResponseSpeed == IMMEDIATE_RESPONSE) {
instrumentsReady.run();
} else {
new Handler().postDelayed(instrumentsReady, 100);
}
}
@Override
public Set<String> getSupportedMethodNames() {
Set<String> methodNames = new HashSet<>();
methodNames.add("https://bobpay.com");
return methodNames;
}
@Override
public String getIdentifier() {
return "https://bobpay.com";
}
}
/** A payment instrument implementation for test. */
private static class BobPayInstrument extends PaymentInstrument {
BobPayInstrument() {
super("https://bobpay.com", "Bob Pay", null, NO_ICON);
}
@Override
public String getMethodName() {
return "https://bobpay.com";
}
@Override
public void getDetails(String merchantName, String origin, PaymentItem total,
List<PaymentItem> cart, JSONObject details, DetailsCallback detailsCallback) {
detailsCallback.onInstrumentDetailsReady(
"https://bobpay.com", "{\"transaction\": 1337}");
}
@Override
public void dismiss() {}
}
} }
/*
* Copyright 2016 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
/* global PaymentRequest:false */
/**
* Launches the PaymentRequest UI with Bob Pay and credit cards as payment
* methods.
*/
function buy() { // eslint-disable-line no-unused-vars
try {
new PaymentRequest(
[{supportedMethods: ['https://bobpay.com', 'visa', 'mastercard']}],
{total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}})
.show()
.then(function(resp) {
resp.complete('success')
.then(function() {
print(resp.methodName + '<br>' +
JSON.stringify(resp.details, undefined, 2));
})
.catch(function(error) {
print(error.message);
});
})
.catch(function(error) {
print(error.message);
});
} catch (error) {
print(error.message);
}
}
<!DOCTYPE html>
<!--
Copyright 2016 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->
<html>
<head>
<title>Bob Pay and Cards Test</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<button onclick="buy()" id="buy">Bob Pay and Cards Test</button><br>
<pre id="result"></pre>
<script src="util.js"></script>
<script src="bobpay_and_cards.js"></script>
</body>
</html>
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