1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.payments;
6 
7 import androidx.test.filters.MediumTest;
8 
9 import org.junit.Assert;
10 import org.junit.ClassRule;
11 import org.junit.Rule;
12 import org.junit.Test;
13 import org.junit.runner.RunWith;
14 
15 import org.chromium.base.metrics.RecordHistogram;
16 import org.chromium.base.test.util.CommandLineFlags;
17 import org.chromium.base.test.util.Feature;
18 import org.chromium.chrome.R;
19 import org.chromium.chrome.browser.autofill.AutofillTestHelper;
20 import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile;
21 import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard;
22 import org.chromium.chrome.browser.flags.ChromeSwitches;
23 import org.chromium.chrome.browser.payments.PaymentRequestTestRule.MainActivityStartCallback;
24 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
25 import org.chromium.ui.test.util.DisableAnimationsTestRule;
26 
27 import java.util.ArrayList;
28 import java.util.concurrent.TimeoutException;
29 
30 /**
31  * A payment integration test for a merchant that requires shipping address to calculate shipping
32  * and user that has 5 addresses stored in autofill settings.
33  */
34 @RunWith(ChromeJUnit4ClassRunner.class)
35 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
36 public class PaymentRequestMultiplePaymentInstrumentsTest implements MainActivityStartCallback {
37     // Disable animations to reduce flakiness.
38     @ClassRule
39     public static DisableAnimationsTestRule sNoAnimationsRule = new DisableAnimationsTestRule();
40 
41     @Rule
42     public PaymentRequestTestRule mPaymentRequestTestRule =
43             new PaymentRequestTestRule("payment_request_no_shipping_test.html", this);
44 
45     private static final AutofillProfile AUTOFILL_PROFILE = new AutofillProfile("" /* guid */,
46             "https://www.example.com" /* origin */, "" /* honorific prefix */, "Lisa Simpson",
47             "Acme Inc.", "123 Main", "California", "Los Angeles", "", "90210", "", "US",
48             "555 123-4567", "lisa@simpson.com", "" /* languageCode */);
49 
50     private static final CreditCard[] CREDIT_CARDS = {
51             // CARD_0 missing billing address.
52             new CreditCard("", "https://example.com", true /* isLocal */, true /* isCached */,
53                     "Jon Doe", "4111111111111111", "1111", "12", "2050", "visa",
54                     R.drawable.visa_card, "" /* billingAddressId */, "" /* serverId */),
55 
56             // For the rest of the cards billing address id will be added in
57             // onMainActivityStarted().
58             // CARD_1 complete card.
59             new CreditCard("", "https://example.com", true /* isLocal */, true /* isCached */,
60                     "John Smith", "4111111111113333", "3333", "10", "2050", "visa",
61                     R.drawable.amex_card, "" /* billingAddressId */, "" /* serverId */),
62 
63             // CARD_2 complete card, different from CARD_1.
64             new CreditCard("", "https://example.com", true /* isLocal */, true /* isCached */,
65                     "Jane Doe", "4111111111112222", "2222", "07", "2077", "visa",
66                     R.drawable.visa_card, "" /* billingAddressId */, "" /* serverId */),
67 
68             // CARD_3 expired card.
69             new CreditCard("", "https://example.com", true /* isLocal */, true /* isCached */,
70                     "Lisa Simpson", "4111111111111111", "1111", "12", "2010", "visa",
71                     R.drawable.visa_card, "" /* billingAddressId */, "" /* serverId */),
72 
73             // CARD_4 missing name.
74             new CreditCard("", "https://example.com", true /* isLocal */, true /* isCached */,
75                     "" /* name */, "4012888888881881", "1881", "06", "2049", "visa",
76                     R.drawable.visa_card, "" /* billingAddressId */, "" /* serverId */),
77     };
78 
79     private CreditCard[] mCreditCardsToAdd;
80     private int[] mCountsToSet;
81     private int[] mDatesToSet;
82 
83     @Override
onMainActivityStarted()84     public void onMainActivityStarted() throws TimeoutException {
85         AutofillTestHelper helper = new AutofillTestHelper();
86 
87         // The user has a complete autofill profile.
88         String billingAddressId = helper.setProfile(AUTOFILL_PROFILE);
89         // Add the autofill card.
90         ArrayList<String> guids = new ArrayList<>();
91         for (int i = 0; i < mCreditCardsToAdd.length; i++) {
92             // CREDIT_CARDS[0] has no billing address.
93             if (mCreditCardsToAdd[i] != CREDIT_CARDS[0]) {
94                 mCreditCardsToAdd[i].setBillingAddressId(billingAddressId);
95             }
96             String creditCardId = helper.setCreditCard(mCreditCardsToAdd[i]);
97             guids.add(creditCardId);
98         }
99 
100         // Set up the autofill card use stats.
101         for (int i = 0; i < guids.size(); i++) {
102             PaymentPreferencesUtil.setPaymentAppUseCountForTest(guids.get(i), mCountsToSet[i]);
103             PaymentPreferencesUtil.setPaymentAppLastUseDate(guids.get(i), mDatesToSet[i]);
104         }
105     }
106 
107     /**
108      * Make sure the address suggestions are in the correct order and that only the top 4
109      * suggestions are shown. They should be ordered by frecency and complete addresses should be
110      * suggested first.
111      */
112     @Test
113     @MediumTest
114     @Feature({"Payments"})
testCreditCardSuggestionOrdering()115     public void testCreditCardSuggestionOrdering() throws TimeoutException {
116         mCreditCardsToAdd = new CreditCard[] {CREDIT_CARDS[0], CREDIT_CARDS[3], CREDIT_CARDS[2],
117                 CREDIT_CARDS[1], CREDIT_CARDS[4]};
118         mCountsToSet = new int[] {20, 15, 10, 25, 30};
119         mDatesToSet = new int[] {5000, 5000, 5000, 5000, 5000};
120 
121         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
122         mPaymentRequestTestRule.clickInPaymentMethodAndWait(
123                 R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
124         Assert.assertEquals(5, mPaymentRequestTestRule.getNumberOfPaymentApps());
125         int i = 0;
126         // The two complete cards are sorted by frecency.
127         Assert.assertTrue(mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(i++).contains(
128                 "John Smith"));
129         Assert.assertTrue(
130                 mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(i++).contains("Jane Doe"));
131         // Incomplete cards have this order: 1-expired 2-missing name 3-missing address 4-missing
132         // card number.
133         Assert.assertTrue(mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(i++).contains(
134                 "Lisa Simpson"));
135         Assert.assertTrue(mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(i++).contains(
136                 "Cardholder name required"));
137         Assert.assertTrue(
138                 mPaymentRequestTestRule.getPaymentMethodSuggestionLabel(i++).contains("Jon Doe"));
139 
140         // Verify that no shipping fields is recorded since there is at least one complete
141         // suggestion.
142         Assert.assertEquals(0,
143                 RecordHistogram.getHistogramTotalCountForTesting(
144                         "PaymentRequest.MissingPaymentFields"));
145     }
146 
147     /**
148      * Make sure the name bit is recorded in missing fields when an incomplete card with missing
149      * name is the most complete one.
150      */
151     @Test
152     @MediumTest
153     @Feature({"Payments"})
testMissingNameFieldRecorded()154     public void testMissingNameFieldRecorded() throws TimeoutException {
155         // Add a card with invalid billing address and another one with missing name.
156         mCreditCardsToAdd = new CreditCard[] {CREDIT_CARDS[4], CREDIT_CARDS[0]};
157         mCountsToSet = new int[] {5, 5};
158         mDatesToSet = new int[] {5000, 5000};
159 
160         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
161         mPaymentRequestTestRule.clickInPaymentMethodAndWait(
162                 R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
163         Assert.assertEquals(2, mPaymentRequestTestRule.getNumberOfPaymentApps());
164 
165         // Verify that the missing fields of the most complete payment method has been recorded.
166         Assert.assertEquals(1,
167                 RecordHistogram.getHistogramValueCountForTesting(
168                         "PaymentRequest.MissingPaymentFields",
169                         AutofillPaymentInstrument.CompletionStatus.CREDIT_CARD_NO_CARDHOLDER));
170     }
171 
172     /**
173      * Make sure the billing address bit is recorded in missing fields when an incomplete card
174      * with missing address is the most complete one.
175      */
176     @Test
177     @MediumTest
178     @Feature({"Payments"})
testMissingBillingAddressFieldRecorded()179     public void testMissingBillingAddressFieldRecorded() throws TimeoutException {
180         // Add a card with invalid billing address.
181         mCreditCardsToAdd = new CreditCard[] {CREDIT_CARDS[0]};
182         mCountsToSet = new int[] {5};
183         mDatesToSet = new int[] {5000};
184 
185         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
186         mPaymentRequestTestRule.clickInPaymentMethodAndWait(
187                 R.id.payments_section, mPaymentRequestTestRule.getReadyForInput());
188         Assert.assertEquals(1, mPaymentRequestTestRule.getNumberOfPaymentApps());
189 
190         // Verify that the missing fields of the most complete payment method has been recorded.
191         Assert.assertEquals(1,
192                 RecordHistogram.getHistogramValueCountForTesting(
193                         "PaymentRequest.MissingPaymentFields",
194                         AutofillPaymentInstrument.CompletionStatus.CREDIT_CARD_NO_BILLING_ADDRESS));
195     }
196 
197     /**
198      * Make sure all fields are recorded when no card exists.
199      */
200     @Test
201     @MediumTest
202     @Feature({"Payments"})
203     @CommandLineFlags.Add("disable-features=StrictHasEnrolledAutofillInstrument")
testAllMissingFieldsRecorded()204     public void testAllMissingFieldsRecorded() throws TimeoutException {
205         // Don't add any cards
206         mCreditCardsToAdd = new CreditCard[] {};
207         mCountsToSet = new int[] {};
208         mDatesToSet = new int[] {};
209 
210         mPaymentRequestTestRule.triggerUIAndWait(mPaymentRequestTestRule.getReadyForInput());
211         Assert.assertEquals(0, mPaymentRequestTestRule.getNumberOfPaymentApps());
212 
213         // Verify that the missing fields of the most complete payment method has been recorded.
214         Assert.assertEquals(1,
215                 RecordHistogram.getHistogramValueCountForTesting(
216                         "PaymentRequest.MissingPaymentFields",
217                         AutofillPaymentInstrument.CompletionStatus.CREDIT_CARD_EXPIRED
218                                 | AutofillPaymentInstrument.CompletionStatus
219                                           .CREDIT_CARD_NO_CARDHOLDER
220                                 | AutofillPaymentInstrument.CompletionStatus
221                                           .CREDIT_CARD_NO_BILLING_ADDRESS
222                                 | AutofillPaymentInstrument.CompletionStatus
223                                           .CREDIT_CARD_NO_NUMBER));
224     }
225 }
226