Commit 9bf4c080 authored by Liquan (Max) Gu's avatar Liquan (Max) Gu Committed by Commit Bot

[PaymentHandler] Resolve test timeout by correcting PRUI onDismiss

Cause analysis:
We are trying to enable two test suites for BottomSheet(BS)
PaymentHandler (PH) UI - PaymentRequestServiceWorkerPaymentAppTest and
PaymentHandlerChangePaymentMethodTest. Currently the two tests would
fail (BS PH enabled) because they are both timeout on waiting for
PaymentRequestUI#onDismiss() to be called. The reason why it's never
invoked is because when mDialog.isShowing(), not only
mDialog.dismiss() is bypassed (in DimmingDialog), but mDialog's dismiss
listener callback is bypassed as well (in Android's Dialog.java).

Why dimming dialog is not showing in BS PH UI is
because of scrim. For CCT PH UI, the scrim is provided by PR UI; for
BottomSheet PH UI, it's by BottomSheet. As a result, DimmingDialog (the
scrim) is showing in CCT and not showing in BS PH UI.

Before change:
PaymentHandlerChangePaymentMethodTest and
PaymentRequestServiceWorkerPaymentAppTest fails on some tests.

After change:
All tests are green.

Change:
* Replace DialogInterface.onDismissListener with
DimmingDialog.onDismissListener.
* Add onDismissListener invocation for DimmingDialog's dismissal.

Bug: 1045088

Change-Id: Iaedebfb1e9250eeb1974c63083c33cf8a89149df
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2047344
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#743142}
parent c7fec141
...@@ -311,6 +311,7 @@ chrome_test_java_sources = [ ...@@ -311,6 +311,7 @@ chrome_test_java_sources = [
"javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java", "javatests/src/org/chromium/chrome/browser/password_manager/PasswordManagerDialogTest.java",
"javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java", "javatests/src/org/chromium/chrome/browser/payments/AndroidPaymentAppFinderTest.java",
"javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java", "javatests/src/org/chromium/chrome/browser/payments/CurrencyFormatterTest.java",
"javatests/src/org/chromium/chrome/browser/payments/ExpandablePaymentHandlerChangePaymentMethodTest.java",
"javatests/src/org/chromium/chrome/browser/payments/MockPackageManagerDelegate.java", "javatests/src/org/chromium/chrome/browser/payments/MockPackageManagerDelegate.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentHandlerChangePaymentMethodTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentHandlerChangePaymentMethodTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentHandlerEnableDelegationsTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentHandlerEnableDelegationsTest.java",
...@@ -370,6 +371,7 @@ chrome_test_java_sources = [ ...@@ -370,6 +371,7 @@ chrome_test_java_sources = [
"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/PaymentRequestRetryTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestRetryTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServerCardTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServerCardTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerExpandablePaymentHandlerTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestServiceWorkerPaymentAppTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressAndOptionTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressAndOptionTest.java",
"javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressChangeTest.java", "javatests/src/org/chromium/chrome/browser/payments/PaymentRequestShippingAddressChangeTest.java",
......
...@@ -11,7 +11,6 @@ import android.animation.ObjectAnimator; ...@@ -11,7 +11,6 @@ import android.animation.ObjectAnimator;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Build; import android.os.Build;
...@@ -58,16 +57,25 @@ import java.util.Collection; ...@@ -58,16 +57,25 @@ import java.util.Collection;
private final Dialog mDialog; private final Dialog mDialog;
private final ViewGroup mFullContainer; private final ViewGroup mFullContainer;
private final int mAnimatorTranslation; private final int mAnimatorTranslation;
private OnDismissListener mDismissListener;
private boolean mIsAnimatingDisappearance; private boolean mIsAnimatingDisappearance;
/**
* Listener for the dismissal of the DimmingDialog.
*/
public interface OnDismissListener {
/** Called when the UI is dismissed. */
void onDismiss();
}
/** /**
* Builds the dimming dialog. * Builds the dimming dialog.
* *
* @param activity The activity on top of which the dialog should be displayed. * @param activity The activity on top of which the dialog should be displayed.
* @param dismissListener The listener for the dismissal of this dialog. * @param dismissListener The listener for the dismissal of this dialog.
*/ */
/* package */ DimmingDialog( /* package */ DimmingDialog(Activity activity, OnDismissListener dismissListener) {
Activity activity, DialogInterface.OnDismissListener dismissListener) { mDismissListener = dismissListener;
// To handle the specced animations, the dialog is entirely contained within a translucent // To handle the specced animations, the dialog is entirely contained within a translucent
// FrameLayout. This could eventually be converted to a real BottomSheetDialog, but that // FrameLayout. This could eventually be converted to a real BottomSheetDialog, but that
// requires exploration of how interactions would work when the dialog can be sent back and // requires exploration of how interactions would work when the dialog can be sent back and
...@@ -76,7 +84,7 @@ import java.util.Collection; ...@@ -76,7 +84,7 @@ import java.util.Collection;
mFullContainer.setBackgroundColor(ApiCompatibilityUtils.getColor( mFullContainer.setBackgroundColor(ApiCompatibilityUtils.getColor(
activity.getResources(), R.color.modal_dialog_scrim_color)); activity.getResources(), R.color.modal_dialog_scrim_color));
mDialog = new AlwaysDismissedDialog(activity, R.style.DimmingDialog); mDialog = new AlwaysDismissedDialog(activity, R.style.DimmingDialog);
mDialog.setOnDismissListener(dismissListener); mDialog.setOnDismissListener((v) -> notifyListenerDialogDismissed());
mDialog.addContentView(mFullContainer, mDialog.addContentView(mFullContainer,
new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)); new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
Window dialogWindow = mDialog.getWindow(); Window dialogWindow = mDialog.getWindow();
...@@ -124,14 +132,20 @@ import java.util.Collection; ...@@ -124,14 +132,20 @@ import java.util.Collection;
* @param isAnimated If true, the dialog dismissal is animated. * @param isAnimated If true, the dialog dismissal is animated.
*/ */
/* package */ void dismiss(boolean isAnimated) { /* package */ void dismiss(boolean isAnimated) {
if (!mDialog.isShowing()) return;
if (isAnimated) { if (isAnimated) {
new DisappearingAnimator(true); new DisappearingAnimator(true);
} else { } else {
mDialog.dismiss(); mDialog.dismiss();
notifyListenerDialogDismissed();
} }
} }
private void notifyListenerDialogDismissed() {
if (mDismissListener == null) return;
mDismissListener.onDismiss();
mDismissListener = null;
}
/** @param overlay The overlay to show. This can be an error dialog, for example. */ /** @param overlay The overlay to show. This can be an error dialog, for example. */
/* package */ void showOverlay(View overlay) { /* package */ void showOverlay(View overlay) {
// Animate the bottom sheet going away. // Animate the bottom sheet going away.
...@@ -232,7 +246,10 @@ import java.util.Collection; ...@@ -232,7 +246,10 @@ import java.util.Collection;
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
mIsAnimatingDisappearance = false; mIsAnimatingDisappearance = false;
mFullContainer.removeView(mFullContainer.getChildAt(0)); mFullContainer.removeView(mFullContainer.getChildAt(0));
if (mIsDialogClosing && mDialog.isShowing()) mDialog.dismiss(); if (mIsDialogClosing) {
if (mDialog.isShowing()) mDialog.dismiss();
notifyListenerDialogDismissed();
}
} }
} }
......
...@@ -14,7 +14,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; ...@@ -14,7 +14,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.app.Activity; import android.app.Activity;
import android.app.Dialog; import android.app.Dialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Handler; import android.os.Handler;
import android.support.v4.view.ViewCompat; import android.support.v4.view.ViewCompat;
...@@ -64,8 +63,8 @@ import java.util.List; ...@@ -64,8 +63,8 @@ import java.util.List;
/** /**
* The PaymentRequest UI. * The PaymentRequest UI.
*/ */
public class PaymentRequestUI implements DialogInterface.OnDismissListener, View.OnClickListener, public class PaymentRequestUI implements DimmingDialog.OnDismissListener, View.OnClickListener,
PaymentRequestSection.SectionDelegate { PaymentRequestSection.SectionDelegate {
@IntDef({DataType.SHIPPING_ADDRESSES, DataType.SHIPPING_OPTIONS, DataType.CONTACT_DETAILS, @IntDef({DataType.SHIPPING_ADDRESSES, DataType.SHIPPING_OPTIONS, DataType.CONTACT_DETAILS,
DataType.PAYMENT_METHODS}) DataType.PAYMENT_METHODS})
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
...@@ -876,6 +875,7 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View ...@@ -876,6 +875,7 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View
/** /**
* Called when user clicks anything in the dialog. * Called when user clicks anything in the dialog.
*/ */
// View.OnClickListener implementation.
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (!isAcceptingCloseButton()) return; if (!isAcceptingCloseButton()) return;
...@@ -1189,8 +1189,9 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View ...@@ -1189,8 +1189,9 @@ public class PaymentRequestUI implements DialogInterface.OnDismissListener, View
* window.</li> * window.</li>
* </ul> * </ul>
*/ */
// DimmingDialog.OnDismissListener implementation.
@Override @Override
public void onDismiss(DialogInterface dialog) { public void onDismiss() {
mIsClosing = true; mIsClosing = true;
if (mEditorDialog.isShowing()) mEditorDialog.dismiss(); if (mEditorDialog.isShowing()) mEditorDialog.dismiss();
if (mCardEditorDialog.isShowing()) mCardEditorDialog.dismiss(); if (mCardEditorDialog.isShowing()) mCardEditorDialog.dismiss();
......
// Copyright 2020 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.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.net.test.ServerCertificate;
import org.chromium.ui.test.util.DisableAnimationsTestRule;
/** An integration test for PaymentRequestEvent.changePaymentMethod(). */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
"enable-blink-features=PaymentMethodChangeEvent,PaymentHandlerChangePaymentMethod",
"enable-features=" + ChromeFeatureList.SCROLL_TO_EXPAND_PAYMENT_HANDLER})
@MediumTest
public class ExpandablePaymentHandlerChangePaymentMethodTest {
// Disable animations to reduce flakiness.
@ClassRule
public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
// Open a tab on the blank page first to initiate the native bindings required by the test
// server.
@Rule
public PaymentRequestTestRule mRule = new PaymentRequestTestRule("about:blank");
// Host the tests on https://127.0.0.1, because file:// URLs cannot have service workers.
private EmbeddedTestServer mServer;
@Before
public void setUp() throws Throwable {
mServer = EmbeddedTestServer.createAndStartHTTPSServer(
InstrumentationRegistry.getContext(), ServerCertificate.CERT_OK);
mRule.startMainActivityWithURL(
mServer.getURL("/components/test/data/payments/change_payment_method.html"));
// Find the web contents where JavaScript will be executed and instrument the browser
// payment sheet.
mRule.openPage();
}
private void installPaymentHandler() throws Throwable {
mRule.runJavaScriptCodeInCurrentTab("install();");
mRule.expectResultContains(new String[] {"instruments.set(): Payment handler installed."});
}
@After
public void tearDown() {
mServer.stopAndDestroyServer();
}
/**
* Verify that absence of the "paymentmethodchange" event handler in the merchant will cause
* PaymentRequestEvent.changePaymentMethod() to resolve with null.
*/
@Test
@Feature({"Payments"})
public void testNoEventHandler() throws Throwable {
installPaymentHandler();
mRule.clickNodeAndWait("testNoHandler", mRule.getDismissed());
mRule.expectResultContains(
new String[] {"PaymentRequest.show(): changePaymentMethod() returned: null"});
}
/**
* Verify that absence of the "paymentmethodchange" event handler in the merchant will cause
* PaymentRequestEvent.changePaymentMethod() to resolve with null.
*/
@Test
@Feature({"Payments"})
public void testNoEventHandlerBasicCard() throws Throwable {
mRule.clickNode("basicCardMethodName");
installPaymentHandler();
mRule.triggerUIAndWait("testNoHandler", mRule.getReadyToPay());
mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
mRule.expectResultContains(
new String[] {"PaymentRequest.show(): changePaymentMethod() returned: null"});
}
/**
* Verify that rejecting the promise passed into PaymentMethodChangeEvent.updateWith() will
* cause PaymentRequest.show() to reject and thus abort the transaction.
*/
@Test
@Feature({"Payments"})
public void testReject() throws Throwable {
installPaymentHandler();
mRule.clickNodeAndWait("testReject", mRule.getDismissed());
mRule.expectResultContains(
new String[] {"PaymentRequest.show() rejected with: Error for test"});
}
/**
* Verify that rejecting the promise passed into PaymentMethodChangeEvent.updateWith() will
* cause PaymentRequest.show() to reject and thus abort the transaction.
*/
@Test
@Feature({"Payments"})
public void testRejectBasicCard() throws Throwable {
mRule.clickNode("basicCardMethodName");
installPaymentHandler();
mRule.triggerUIAndWait("testReject", mRule.getReadyToPay());
mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
mRule.expectResultContains(
new String[] {"PaymentRequest.show() rejected with: Error for test"});
}
/**
* Verify that a JavaScript exception in the "paymentmethodchange" event handler will cause
* PaymentRequest.show() to reject and thus abort the transaction.
*/
@Test
@Feature({"Payments"})
public void testThrow() throws Throwable {
installPaymentHandler();
mRule.clickNodeAndWait("testThrow", mRule.getDismissed());
mRule.expectResultContains(
new String[] {"PaymentRequest.show() rejected with: Error: Error for test"});
}
/**
* Verify that a JavaScript exception in the "paymentmethodchange" event handler will cause
* PaymentRequest.show() to reject and thus abort the transaction.
*/
@Test
@Feature({"Payments"})
public void testThrowBasicCard() throws Throwable {
mRule.clickNode("basicCardMethodName");
installPaymentHandler();
mRule.triggerUIAndWait("testThrow", mRule.getReadyToPay());
mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
mRule.expectResultContains(
new String[] {"PaymentRequest.show() rejected with: Error: Error for test"});
}
/**
* Verify that the payment handler receives a subset of the payment details passed into
* PaymentMethodChangeEvent.updateWith() with URL-based payment method identifier.
*/
@Test
@Feature({"Payments"})
public void testDetails() throws Throwable {
installPaymentHandler();
mRule.clickNodeAndWait("testDetails", mRule.getDismissed());
// Look for the this exact return value to ensure that the browser redacts some details
// before forwarding them to the payment handler.
mRule.expectResultContains(
new String[] {"PaymentRequest.show(): changePaymentMethod() returned: "
+ "{\"error\":\"Error for test\",\"modifiers\":"
+ "[{\"data\":{\"soup\":\"potato\"},"
+ "\"supportedMethods\":\"https://127.0.0.1:",
// Port changes every time, so don't hardcode it here.
"/pay\",\"total\":{\"amount\":{\"currency\":\"EUR\",\"value\":\"0.03\"},"
+ "\"label\":\"\",\"pending\":false}}],"
+ "\"paymentMethodErrors\":{\"country\":\"Unsupported country\"},"
+ "\"total\":{\"currency\":\"GBP\",\"value\":\"0.02\"}}"});
}
/**
* Verify that the payment handler receives a subset of the payment details passed into
* PaymentMethodChangeEvent.updateWith() when basic-card payment method is used.
*/
@Test
@Feature({"Payments"})
public void testDetailsBasicCard() throws Throwable {
mRule.clickNode("basicCardMethodName");
installPaymentHandler();
mRule.triggerUIAndWait("testDetails", mRule.getReadyToPay());
mRule.clickAndWait(R.id.button_primary, mRule.getDismissed());
// Look for the this exact return value to ensure that the browser redacts some details
// before forwarding them to the payment handler.
mRule.expectResultContains(
new String[] {"PaymentRequest.show(): changePaymentMethod() returned: "
+ "{\"error\":\"Error for test\",\"modifiers\":"
+ "[{\"data\":{\"soup\":\"potato\"},"
+ "\"supportedMethods\":\"basic-card\","
+ "\"total\":{\"amount\":{\"currency\":\"EUR\",\"value\":\"0.03\"},"
+ "\"label\":\"\",\"pending\":false}}],"
+ "\"paymentMethodErrors\":{\"country\":\"Unsupported country\"},"
+ "\"total\":{\"currency\":\"GBP\",\"value\":\"0.02\"}}"});
}
}
...@@ -18,6 +18,7 @@ import org.chromium.base.test.util.CommandLineFlags; ...@@ -18,6 +18,7 @@ import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature; import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.net.test.EmbeddedTestServer; import org.chromium.net.test.EmbeddedTestServer;
import org.chromium.net.test.ServerCertificate; import org.chromium.net.test.ServerCertificate;
...@@ -27,7 +28,7 @@ import org.chromium.ui.test.util.DisableAnimationsTestRule; ...@@ -27,7 +28,7 @@ import org.chromium.ui.test.util.DisableAnimationsTestRule;
@RunWith(ChromeJUnit4ClassRunner.class) @RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
"enable-blink-features=PaymentMethodChangeEvent,PaymentHandlerChangePaymentMethod", "enable-blink-features=PaymentMethodChangeEvent,PaymentHandlerChangePaymentMethod",
"disable-field-trial-config"}) "disable-features=" + ChromeFeatureList.SCROLL_TO_EXPAND_PAYMENT_HANDLER})
@MediumTest @MediumTest
public class PaymentHandlerChangePaymentMethodTest { public class PaymentHandlerChangePaymentMethodTest {
// Disable animations to reduce flakiness. // Disable animations to reduce flakiness.
......
// Copyright 2020 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.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.support.test.filters.MediumTest;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.R;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.browser.autofill.AutofillTestHelper;
import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.WebContents;
import org.chromium.payments.mojom.BasicCardNetwork;
import org.chromium.ui.test.util.DisableAnimationsTestRule;
import java.util.concurrent.TimeoutException;
/**
* A payment integration test for service worker based payment apps.
*/
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE,
// For all the tests in this file, we expect abort exception when there is no supported
// payment apps instead of showing payment request UI.
"enable-features=" + ChromeFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT + ","
+ ChromeFeatureList.SCROLL_TO_EXPAND_PAYMENT_HANDLER,
// Prevent crawling the web for real payment apps.
"disable-features=" + ChromeFeatureList.SERVICE_WORKER_PAYMENT_APPS})
public class PaymentRequestServiceWorkerExpandablePaymentHandlerTest {
// Disable animations to reduce flakiness.
@ClassRule
public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
@Rule
public PaymentRequestTestRule mPaymentRequestTestRule = new PaymentRequestTestRule(
"payment_request_bobpay_and_basic_card_with_modifier_optional_data_test.html");
/**
* Installs a mock service worker based payment app with given supported delegations for
* testing.
*
* @param scope Service worker scope that identifies the payment app. Must be
* unique.
* @param supportedMethodNames The supported payment methods of the mock payment app.
* @param capabilities The capabilities of the mocked payment app.
* @param name The name of the mocked payment app.
* @param withIcon Whether provide payment app icon.
* @param supportedDelegations The supported delegations of the mock payment app.
*/
private void installMockServiceWorkerPaymentApp(String scope,
final String[] supportedMethodNames,
final ServiceWorkerPaymentApp.Capabilities[] capabilities, final String name,
final boolean withIcon, SupportedDelegations supportedDelegations) {
PaymentAppService.getInstance().addFactory(new PaymentAppFactoryInterface() {
@Override
public void create(PaymentAppFactoryDelegate delegate) {
WebContents webContents = delegate.getParams().getWebContents();
ChromeActivity activity = ChromeActivity.fromWebContents(webContents);
BitmapDrawable icon = withIcon
? new BitmapDrawable(activity.getResources(),
Bitmap.createBitmap(new int[] {Color.RED}, 1 /* width */,
1 /* height */, Bitmap.Config.ARGB_8888))
: null;
delegate.onPaymentAppCreated(new ServiceWorkerPaymentApp(webContents,
0 /* registrationId */, UriUtils.parseUriFromString(scope) /*scope*/, name,
"test@bobpay.com" /* userHint */, "https://bobpay.com" /* origin */,
icon /* icon */, supportedMethodNames /* methodNames */,
capabilities /* capabilities */,
new String[0] /* preferredRelatedApplicationIds */, supportedDelegations));
delegate.onDoneCreatingPaymentApps(this);
}
});
}
/**
* Installs a mock service worker based payment app with no supported delegations for testing.
*
* @param scope The service worker scope that identifies this payment app. Must
* be unique.
* @param supportedMethodNames The supported payment methods of the mock payment app.
* @param capabilities The capabilities of the mocked payment app.
* @param withName Whether provide payment app name.
* @param withIcon Whether provide payment app icon.
*/
private void installMockServiceWorkerPaymentApp(String scope,
final String[] supportedMethodNames,
final ServiceWorkerPaymentApp.Capabilities[] capabilities, final boolean withName,
final boolean withIcon) {
installMockServiceWorkerPaymentApp(scope, supportedMethodNames, capabilities,
withName ? "BobPay" : null, withIcon, new SupportedDelegations());
}
/**
* Installs a mock service worker based payment app for bobpay with given supported delegations
* for testing.
*
* @param scope The service worker scope that identifies this payment app. Must be
* unique.
* @param shippingAddress Whether or not the mock payment app provides shipping address.
* @param payerName Whether or not the mock payment app provides payer's name.
* @param payerPhone Whether or not the mock payment app provides payer's phone number.
* @param payerEmail Whether or not the mock payment app provides payer's email address.
* @param name The name of the mocked payment app.
*/
private void installMockServiceWorkerPaymentAppWithDelegations(String scope,
final boolean shippingAddress, final boolean payerName, final boolean payerPhone,
final boolean payerEmail, final String name) {
String[] supportedMethodNames = {"https://bobpay.xyz"};
installMockServiceWorkerPaymentApp(scope, supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], name, true /*withIcon*/,
new SupportedDelegations(shippingAddress, payerName, payerPhone, payerEmail));
}
/**
* Adds a credit cart to ensure that autofill app is available.
*/
public void addCreditCard() throws TimeoutException {
AutofillTestHelper helper = new AutofillTestHelper();
String billingAddressId = helper.setProfile(new AutofillProfile("", "https://example.com",
true, "John Smith", "Google", "340 Main St", "CA", "Los Angeles", "", "90291", "",
"US", "310-310-6000", "john.smith@gmail.com", "en-US"));
helper.setCreditCard(new CreditCard("", "https://example.com", true, true, "Jon Doe",
"4111111111111111", "1111", "12", "2050", "visa", R.drawable.visa_card,
billingAddressId, "" /* serverId */));
}
@Test
@MediumTest
@Feature({"Payments"})
public void testNoSupportedPaymentMethods() throws TimeoutException {
mPaymentRequestTestRule.openPageAndClickBuyAndWait(mPaymentRequestTestRule.getShowFailed());
mPaymentRequestTestRule.expectResultContains(
new String[] {"show() rejected", "Payment method not supported"});
}
@Test
@MediumTest
@Feature({"Payments"})
public void testHasSupportedPaymentMethods() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com"};
installMockServiceWorkerPaymentApp("https://bobpay.com", supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], true, true);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
// Payment sheet skips to the app since it is the only available app.
mPaymentRequestTestRule.openPageAndClickBuyAndWait(mPaymentRequestTestRule.getDismissed());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testNoCapabilities() throws TimeoutException {
// Add a credit card to force showing payment sheet UI.
addCreditCard();
String[] supportedMethodNames = {"https://bobpay.com", "basic-card"};
ServiceWorkerPaymentApp.Capabilities[] capabilities = {};
installMockServiceWorkerPaymentApp(
"https://bobpay.com", supportedMethodNames, capabilities, true, true);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The Bob Pay modifier should apply.
Assert.assertEquals("USD $4.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_all_cards_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should apply.
Assert.assertEquals("USD $4.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_visa_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should not apply.
Assert.assertEquals("USD $5.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_visa_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should not apply.
Assert.assertEquals("USD $5.00", mPaymentRequestTestRule.getOrderSummaryTotal());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testHasVisaCapabilities() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com", "basic-card"};
int[] networks = {BasicCardNetwork.VISA};
ServiceWorkerPaymentApp.Capabilities[] capabilities = {
new ServiceWorkerPaymentApp.Capabilities(networks)};
installMockServiceWorkerPaymentApp(
"https://bobpay.com", supportedMethodNames, capabilities, true, true);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_all_cards_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
Assert.assertEquals("USD $4.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_visa_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should apply.
Assert.assertEquals("USD $4.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_visa_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should apply.
Assert.assertEquals("USD $4.00", mPaymentRequestTestRule.getOrderSummaryTotal());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testHasMastercardCapabilities() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com", "basic-card"};
int[] networks = {BasicCardNetwork.MASTERCARD};
ServiceWorkerPaymentApp.Capabilities[] capabilities = {
new ServiceWorkerPaymentApp.Capabilities(networks)};
installMockServiceWorkerPaymentApp(
"https://bobpay.com", supportedMethodNames, capabilities, true, true);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_all_cards_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should apply.
Assert.assertEquals("USD $4.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_visa_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should not apply.
Assert.assertEquals("USD $5.00", mPaymentRequestTestRule.getOrderSummaryTotal());
mPaymentRequestTestRule.clickAndWait(
R.id.close_button, mPaymentRequestTestRule.getDismissed());
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_visa_modifier", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The modifier should not apply.
Assert.assertEquals("USD $5.00", mPaymentRequestTestRule.getOrderSummaryTotal());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testDoNotCallCanMakePayment() throws TimeoutException {
// Add a credit card to force showing payment sheet UI.
addCreditCard();
String[] supportedMethodNames = {"basic-card"};
installMockServiceWorkerPaymentApp("https://bobpay.com", supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], true, true);
// Sets setCanMakePaymentForTesting(false) to return false for CanMakePayment since there is
// no real sw payment app, so if CanMakePayment is called then no payment apps will be
// available, otherwise CanMakePayment is not called.
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(false);
mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfPaymentApps());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testCanPreselect() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com"};
installMockServiceWorkerPaymentApp("https://bobpay.com", supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], true, true);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
// Payment sheet skips to the app since it is the only available app.
mPaymentRequestTestRule.openPageAndClickBuyAndWait(mPaymentRequestTestRule.getDismissed());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testCanNotPreselectWithoutName() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com"};
installMockServiceWorkerPaymentApp("https://bobpay.com", supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], false, true);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
Assert.assertNull(mPaymentRequestTestRule.getSelectedPaymentAppLabel());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testCanNotPreselectWithoutIcon() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com"};
installMockServiceWorkerPaymentApp("https://bobpay.com", supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], true, false);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
Assert.assertNull(mPaymentRequestTestRule.getSelectedPaymentAppLabel());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testCanNotPreselectWithoutNameAndIcon() throws TimeoutException {
String[] supportedMethodNames = {"https://bobpay.com"};
installMockServiceWorkerPaymentApp("https://bobpay.com", supportedMethodNames,
new ServiceWorkerPaymentApp.Capabilities[0], false, false);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
Assert.assertNull(mPaymentRequestTestRule.getSelectedPaymentAppLabel());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testPaymentAppProvidingShippingComesFirst() throws TimeoutException {
installMockServiceWorkerPaymentAppWithDelegations("https://alicepay.com" /*scope*/,
false /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "noSupportedDelegation" /*name*/);
installMockServiceWorkerPaymentAppWithDelegations("https://bobpay.com" /*scope*/,
true /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "shippingSupported1" /*name */);
// Install the second app supporting shipping delegation to force showing payment sheet.
installMockServiceWorkerPaymentAppWithDelegations("https://charliepay.com" /*scope*/,
true /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "shippingSupported2" /*name */);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_shipping_requested", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(3, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The payment app which provides shipping address must be preselected.
Assert.assertTrue(
mPaymentRequestTestRule.getSelectedPaymentAppLabel().contains("shippingSupported"));
}
@Test
@MediumTest
@Feature({"Payments"})
public void testPaymentAppProvidingContactComesFirst() throws TimeoutException {
installMockServiceWorkerPaymentAppWithDelegations("https://alicepay.com" /*scope*/,
false /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "noSupportedDelegation" /*name*/);
installMockServiceWorkerPaymentAppWithDelegations("https://bobpay.com" /*scope*/,
false /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "contactSupported" /*name */);
installMockServiceWorkerPaymentAppWithDelegations("https://charliepay.com" /*scope*/,
false /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
true /*payerEmail*/, "emailOnlySupported" /*name */);
// Install the second app supporting contact delegation to force showing payment sheet.
installMockServiceWorkerPaymentAppWithDelegations("https://davepay.com" /*scope*/,
false /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "contactSupported2" /*name */);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait(
"buy_with_contact_requested", mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(4, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The payment app which provides full contact details must be preselected.
Assert.assertTrue(
mPaymentRequestTestRule.getSelectedPaymentAppLabel().contains("contactSupported"));
// The payment app which partially provides the required contact details comes before the
// one that provides no contact information.
Assert.assertTrue(mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(2).contains(
"emailOnlySupported"));
}
@Test
@MediumTest
@Feature({"Payments"})
public void testPaymentAppProvidingAllRequiredInfoComesFirst() throws TimeoutException {
installMockServiceWorkerPaymentAppWithDelegations("https://alicepay.com" /*scope*/,
true /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "shippingSupported" /*name */);
installMockServiceWorkerPaymentAppWithDelegations("https://bobpay.com" /*scope*/,
false /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "contactSupported" /*name */);
installMockServiceWorkerPaymentAppWithDelegations("https://charliepay.com" /*scope*/,
true /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "shippingAndContactSupported" /*name*/);
// Install the second app supporting both shipping and contact delegations to force showing
// payment sheet.
installMockServiceWorkerPaymentAppWithDelegations("https://davepay.com" /*scope*/,
true /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "shippingAndContactSupported2" /*name*/);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.triggerUIAndWait("buy_with_shipping_and_contact_requested",
mPaymentRequestTestRule.getReadyForInput());
Assert.assertEquals(4, mPaymentRequestTestRule.getNumberOfPaymentApps());
// The payment app which provides all required information must be preselected.
Assert.assertTrue(mPaymentRequestTestRule.getSelectedPaymentAppLabel().contains(
"shippingAndContactSupported"));
// The payment app which provides shipping comes before the one which provides contact
// details when both required by merchant.
Assert.assertTrue(mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(2).contains(
"shippingSupported"));
}
@Test
@MediumTest
@Feature({"Payments"})
public void testSkipsToSinglePaymentAppProvidingShipping() throws TimeoutException {
installMockServiceWorkerPaymentAppWithDelegations("https://alicepay.com" /*scope*/,
false /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "noSupportedDelegation" /*name*/);
installMockServiceWorkerPaymentAppWithDelegations("https://bobpay.com" /*scope*/,
true /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "shippingSupported" /*name */);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.openPageAndClickNodeAndWait(
"buy_with_shipping_requested", mPaymentRequestTestRule.getDismissed());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testSkipsToSinglePaymentAppProvidingContact() throws TimeoutException {
installMockServiceWorkerPaymentAppWithDelegations("https://alicepay.com" /*scope*/,
false /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "noSupportedDelegation" /*name*/);
installMockServiceWorkerPaymentAppWithDelegations("https://bobpay.com" /*scope*/,
false /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "contactSupported" /*name */);
installMockServiceWorkerPaymentAppWithDelegations("https://charliepay.com" /*scope*/,
false /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
true /*payerEmail*/, "emailOnlySupported" /*name */);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.openPageAndClickNodeAndWait(
"buy_with_contact_requested", mPaymentRequestTestRule.getDismissed());
}
@Test
@MediumTest
@Feature({"Payments"})
public void testSkipsToSinglePaymentAppProvidingAllRequiredInfo() throws TimeoutException {
installMockServiceWorkerPaymentAppWithDelegations("https://alicepay.com" /*scope*/,
true /*shippingAddress*/, false /*payerName*/, false /*payerPhone*/,
false /*payerEmail*/, "shippingSupported" /*name */);
installMockServiceWorkerPaymentAppWithDelegations("https://bobpay.com" /*scope*/,
false /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "contactSupported" /*name */);
installMockServiceWorkerPaymentAppWithDelegations("https://charliepay.com" /*scope*/,
true /*shippingAddress*/, true /*payerName*/, true /*payerPhone*/,
true /*payerEmail*/, "shippingAndContactSupported" /*name*/);
ServiceWorkerPaymentAppBridge.setCanMakePaymentForTesting(true);
mPaymentRequestTestRule.openPageAndClickNodeAndWait(
"buy_with_shipping_and_contact_requested", mPaymentRequestTestRule.getDismissed());
}
}
...@@ -39,9 +39,9 @@ import java.util.concurrent.TimeoutException; ...@@ -39,9 +39,9 @@ import java.util.concurrent.TimeoutException;
// For all the tests in this file, we expect abort exception when there is no supported // For all the tests in this file, we expect abort exception when there is no supported
// payment apps instead of showing payment request UI. // payment apps instead of showing payment request UI.
"enable-features=" + ChromeFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT, "enable-features=" + ChromeFeatureList.STRICT_HAS_ENROLLED_AUTOFILL_INSTRUMENT,
"disable-field-trial-config",
// Prevent crawling the web for real payment apps. // Prevent crawling the web for real payment apps.
"disable-features=" + ChromeFeatureList.SERVICE_WORKER_PAYMENT_APPS}) "disable-features=" + ChromeFeatureList.SERVICE_WORKER_PAYMENT_APPS + ","
+ ChromeFeatureList.SCROLL_TO_EXPAND_PAYMENT_HANDLER})
public class PaymentRequestServiceWorkerPaymentAppTest { public class PaymentRequestServiceWorkerPaymentAppTest {
// Disable animations to reduce flakiness. // Disable animations to reduce flakiness.
@ClassRule @ClassRule
......
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