fune/browser/components/payments/test/browser/browser_retry_fieldErrors.js

882 lines
26 KiB
JavaScript

"use strict";
/**
* Test the merchant calling .retry() with field-specific errors.
*/
async function setup() {
await setupFormAutofillStorage();
await cleanupFormAutofillStorage();
// add 2 addresses and 2 cards to avoid the FTU sequence and test address errors
let prefilledGuids = await addSampleAddressesAndBasicCard(
[PTU.Addresses.TimBL, PTU.Addresses.TimBL2],
[PTU.BasicCards.JaneMasterCard, PTU.BasicCards.JohnDoe]
);
info("associating card1 with a billing address");
await formAutofillStorage.creditCards.update(
prefilledGuids.card1GUID,
{
billingAddressGUID: prefilledGuids.address1GUID,
},
true
);
info("associating card2 with a billing address");
await formAutofillStorage.creditCards.update(
prefilledGuids.card2GUID,
{
billingAddressGUID: prefilledGuids.address1GUID,
},
true
);
return prefilledGuids;
}
add_task(async function test_retry_with_shippingAddressErrors() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(false, "Cannot test OS key store login on official builds.");
return;
}
let prefilledGuids = await setup();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE_URL,
},
async browser => {
let { win, frame } = await setupPaymentDialog(browser, {
methodData: [PTU.MethodData.basicCard],
details: Object.assign(
{},
PTU.Details.twoShippingOptions,
PTU.Details.total60USD
),
options: PTU.Options.requestShippingOption,
merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
});
await selectPaymentDialogShippingAddressByCountry(frame, "DE");
await spawnPaymentDialogTask(
frame,
async ({ prefilledGuids: guids }) => {
let paymentMethodPicker = content.document.querySelector(
"payment-method-picker"
);
content.fillField(
Cu.waiveXrays(paymentMethodPicker).dropdown.popupBox,
guids.card2GUID
);
},
{ prefilledGuids }
);
await spawnPaymentDialogTask(
frame,
PTU.DialogContentTasks.setSecurityCode,
{
securityCode: "123",
}
);
info("clicking the button to try pay the 1st time");
await loginAndCompletePayment(frame);
let retryUpdatePromise = spawnPaymentDialogTask(
frame,
async function checkDialog() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
let state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "processing";
},
"Wait for completeStatus from pay button click"
);
is(
state.request.completeStatus,
"processing",
"Check completeStatus is processing"
);
is(
state.request.paymentDetails.shippingAddressErrors.country,
undefined,
"Check country error string is empty"
);
ok(state.changesPrevented, "Changes prevented");
state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "";
},
"Wait for completeStatus from DOM update"
);
is(state.request.completeStatus, "", "Check completeStatus");
is(
state.request.paymentDetails.shippingAddressErrors.country,
"Can only ship to USA",
"Check country error string in state"
);
ok(!state.changesPrevented, "Changes no longer prevented");
is(
state.page.id,
"payment-summary",
"Check still on payment-summary"
);
ok(
content.document
.querySelector("#payment-summary")
.innerText.includes("Can only ship to USA"),
"Check error visibility on summary page"
);
ok(
content.document.getElementById("pay").disabled,
"Pay button should be disabled until the field error is addressed"
);
}
);
// Add a handler to retry the payment above.
info("Tell merchant page to retry with a country error string");
let retryPromise = SpecialPowers.spawn(
browser,
[
{
delayMs: 1000,
validationErrors: {
shippingAddress: {
country: "Can only ship to USA",
},
},
},
],
PTU.ContentTasks.addRetryHandler
);
await retryUpdatePromise;
info("Changing to a US address to clear the error");
await selectPaymentDialogShippingAddressByCountry(frame, "US");
info("Tell merchant page to retry with a regionCode error string");
let retryPromise2 = SpecialPowers.spawn(
browser,
[
{
delayMs: 1000,
validationErrors: {
shippingAddress: {
regionCode: "Can only ship to California",
},
},
},
],
PTU.ContentTasks.addRetryHandler
);
await loginAndCompletePayment(frame);
await spawnPaymentDialogTask(frame, async function checkRegionError() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
let state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "";
},
"Wait for completeStatus from DOM update"
);
is(state.request.completeStatus, "", "Check completeStatus");
is(
state.request.paymentDetails.shippingAddressErrors.regionCode,
"Can only ship to California",
"Check regionCode error string in state"
);
ok(!state.changesPrevented, "Changes no longer prevented");
is(state.page.id, "payment-summary", "Check still on payment-summary");
ok(
content.document
.querySelector("#payment-summary")
.innerText.includes("Can only ship to California"),
"Check error visibility on summary page"
);
ok(
content.document.getElementById("pay").disabled,
"Pay button should be disabled until the field error is addressed"
);
});
info(
"Changing the shipping state to CA without changing selectedShippingAddress"
);
await navigateToAddShippingAddressPage(frame, {
addLinkSelector:
'address-picker[selected-state-key="selectedShippingAddress"] .edit-link',
});
await fillInShippingAddressForm(frame, { "address-level1": "CA" });
await submitAddressForm(frame, null, { isEditing: true });
await loginAndCompletePayment(frame);
// We can only check the retry response after the closing as it only resolves upon complete.
let { retryException } = await retryPromise;
ok(
!retryException,
"Expect no exception to be thrown when calling retry()"
);
let { retryException2 } = await retryPromise2;
ok(
!retryException2,
"Expect no exception to be thrown when calling retry()"
);
// Add a handler to complete the payment above.
info("acknowledging the completion from the merchant page");
let result = await SpecialPowers.spawn(
browser,
[],
PTU.ContentTasks.addCompletionHandler
);
// Verify response has the expected properties
let expectedDetails = Object.assign(
{
"cc-security-code": "123",
},
PTU.BasicCards.JohnDoe
);
checkPaymentMethodDetailsMatchesCard(
result.response.details,
expectedDetails,
"Check response payment details"
);
checkPaymentAddressMatchesStorageAddress(
result.response.shippingAddress,
{ ...PTU.Addresses.TimBL, ...{ "address-level1": "CA" } },
"Check response shipping address"
);
await BrowserTestUtils.waitForCondition(
() => win.closed,
"dialog should be closed"
);
}
);
});
add_task(async function test_retry_with_payerErrors() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(false, "Cannot test OS key store login on official builds.");
return;
}
let prefilledGuids = await setup();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE_URL,
},
async browser => {
let { win, frame } = await setupPaymentDialog(browser, {
methodData: [PTU.MethodData.basicCard],
details: PTU.Details.total60USD,
options: PTU.Options.requestPayerNameEmailAndPhone,
merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
});
await spawnPaymentDialogTask(
frame,
async ({ prefilledGuids: guids }) => {
let paymentMethodPicker = content.document.querySelector(
"payment-method-picker"
);
content.fillField(
Cu.waiveXrays(paymentMethodPicker).dropdown.popupBox,
guids.card2GUID
);
},
{ prefilledGuids }
);
await spawnPaymentDialogTask(
frame,
PTU.DialogContentTasks.setSecurityCode,
{
securityCode: "123",
}
);
info("clicking the button to try pay the 1st time");
await loginAndCompletePayment(frame);
let retryUpdatePromise = spawnPaymentDialogTask(
frame,
async function checkDialog() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
let state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "processing";
},
"Wait for completeStatus from pay button click"
);
is(
state.request.completeStatus,
"processing",
"Check completeStatus is processing"
);
is(
state.request.paymentDetails.payerErrors.email,
undefined,
"Check email error isn't present"
);
ok(state.changesPrevented, "Changes prevented");
state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "";
},
"Wait for completeStatus from DOM update"
);
is(state.request.completeStatus, "", "Check completeStatus");
is(
state.request.paymentDetails.payerErrors.email,
"You must use your employee email address",
"Check email error string in state"
);
ok(!state.changesPrevented, "Changes no longer prevented");
is(
state.page.id,
"payment-summary",
"Check still on payment-summary"
);
ok(
content.document
.querySelector("#payment-summary")
.innerText.includes("You must use your employee email address"),
"Check error visibility on summary page"
);
ok(
content.document.getElementById("pay").disabled,
"Pay button should be disabled until the field error is addressed"
);
}
);
// Add a handler to retry the payment above.
info("Tell merchant page to retry with a country error string");
let retryPromise = SpecialPowers.spawn(
browser,
[
{
delayMs: 1000,
validationErrors: {
payer: {
email: "You must use your employee email address",
},
},
},
],
PTU.ContentTasks.addRetryHandler
);
await retryUpdatePromise;
info("Changing to a different email address to clear the error");
await spawnPaymentDialogTask(
frame,
PTU.DialogContentTasks.selectPayerAddressByGuid,
prefilledGuids.address1GUID
);
info("Tell merchant page to retry with a phone error string");
let retryPromise2 = SpecialPowers.spawn(
browser,
[
{
delayMs: 1000,
validationErrors: {
payer: {
phone: "Your phone number isn't valid",
},
},
},
],
PTU.ContentTasks.addRetryHandler
);
await loginAndCompletePayment(frame);
await spawnPaymentDialogTask(frame, async function checkRegionError() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
let state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "";
},
"Wait for completeStatus from DOM update"
);
is(state.request.completeStatus, "", "Check completeStatus");
is(
state.request.paymentDetails.payerErrors.phone,
"Your phone number isn't valid",
"Check regionCode error string in state"
);
ok(!state.changesPrevented, "Changes no longer prevented");
is(state.page.id, "payment-summary", "Check still on payment-summary");
ok(
content.document
.querySelector("#payment-summary")
.innerText.includes("Your phone number isn't valid"),
"Check error visibility on summary page"
);
ok(
content.document.getElementById("pay").disabled,
"Pay button should be disabled until the field error is addressed"
);
});
info(
"Changing the payer phone to be valid without changing selectedPayerAddress"
);
await navigateToAddAddressPage(frame, {
addLinkSelector:
'address-picker[selected-state-key="selectedPayerAddress"] .edit-link',
initialPageId: "payment-summary",
addressPageId: "payer-address-page",
});
let newPhoneNumber = "+16175555555";
await fillInPayerAddressForm(frame, { tel: newPhoneNumber });
await SpecialPowers.spawn(
browser,
[
{
eventName: "payerdetailchange",
},
],
PTU.ContentTasks.promisePaymentResponseEvent
);
await submitAddressForm(frame, null, { isEditing: true });
await SpecialPowers.spawn(
browser,
[
{
eventName: "payerdetailchange",
},
],
PTU.ContentTasks.awaitPaymentEventPromise
);
await loginAndCompletePayment(frame);
// We can only check the retry response after the closing as it only resolves upon complete.
let { retryException } = await retryPromise;
ok(
!retryException,
"Expect no exception to be thrown when calling retry()"
);
let { retryException2 } = await retryPromise2;
ok(
!retryException2,
"Expect no exception to be thrown when calling retry()"
);
// Add a handler to complete the payment above.
info("acknowledging the completion from the merchant page");
let result = await SpecialPowers.spawn(
browser,
[],
PTU.ContentTasks.addCompletionHandler
);
// Verify response has the expected properties
let expectedDetails = Object.assign(
{
"cc-security-code": "123",
},
PTU.BasicCards.JohnDoe
);
checkPaymentMethodDetailsMatchesCard(
result.response.details,
expectedDetails,
"Check response payment details"
);
let {
"given-name": givenName,
"additional-name": additionalName,
"family-name": familyName,
email,
} = PTU.Addresses.TimBL;
is(
result.response.payerName,
`${givenName} ${additionalName} ${familyName}`,
"Check payer name"
);
is(result.response.payerEmail, email, "Check payer email");
is(result.response.payerPhone, newPhoneNumber, "Check payer phone");
await BrowserTestUtils.waitForCondition(
() => win.closed,
"dialog should be closed"
);
}
);
});
add_task(async function test_retry_with_paymentMethodErrors() {
if (!OSKeyStoreTestUtils.canTestOSKeyStoreLogin()) {
todo(false, "Cannot test OS key store login on official builds.");
return;
}
let prefilledGuids = await setup();
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: BLANK_PAGE_URL,
},
async browser => {
let { win, frame } = await setupPaymentDialog(browser, {
methodData: [PTU.MethodData.basicCard],
details: PTU.Details.total60USD,
merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
});
await spawnPaymentDialogTask(
frame,
async ({ prefilledGuids: guids }) => {
let paymentMethodPicker = content.document.querySelector(
"payment-method-picker"
);
content.fillField(
Cu.waiveXrays(paymentMethodPicker).dropdown.popupBox,
guids.card1GUID
);
},
{ prefilledGuids }
);
await spawnPaymentDialogTask(
frame,
PTU.DialogContentTasks.setSecurityCode,
{
securityCode: "123",
}
);
info("clicking the button to try pay the 1st time");
await loginAndCompletePayment(frame);
let retryUpdatePromise = spawnPaymentDialogTask(
frame,
async function checkDialog() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
let state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "processing";
},
"Wait for completeStatus from pay button click"
);
is(
state.request.completeStatus,
"processing",
"Check completeStatus is processing"
);
is(
state.request.paymentDetails.paymentMethodErrors,
null,
"Check no paymentMethod errors are present"
);
ok(state.changesPrevented, "Changes prevented");
state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "";
},
"Wait for completeStatus from DOM update"
);
is(state.request.completeStatus, "", "Check completeStatus");
is(
state.request.paymentDetails.paymentMethodErrors.cardSecurityCode,
"Your CVV is incorrect",
"Check cardSecurityCode error string in state"
);
ok(!state.changesPrevented, "Changes no longer prevented");
is(
state.page.id,
"payment-summary",
"Check still on payment-summary"
);
todo(
content.document
.querySelector("#payment-summary")
.innerText.includes("Your CVV is incorrect"),
"Bug 1491815: Check error visibility on summary page"
);
todo(
content.document.getElementById("pay").disabled,
"Bug 1491815: Pay button should be disabled until the field error is addressed"
);
}
);
// Add a handler to retry the payment above.
info("Tell merchant page to retry with a cardSecurityCode error string");
let retryPromise = SpecialPowers.spawn(
browser,
[
{
delayMs: 1000,
validationErrors: {
paymentMethod: {
cardSecurityCode: "Your CVV is incorrect",
},
},
},
],
PTU.ContentTasks.addRetryHandler
);
await retryUpdatePromise;
info("Changing to a different card to clear the error");
await spawnPaymentDialogTask(
frame,
PTU.DialogContentTasks.selectPaymentOptionByGuid,
prefilledGuids.card1GUID
);
info(
"Tell merchant page to retry with a billing postalCode error string"
);
let retryPromise2 = SpecialPowers.spawn(
browser,
[
{
delayMs: 1000,
validationErrors: {
paymentMethod: {
billingAddress: {
postalCode: "Your postal code isn't valid",
},
},
},
},
],
PTU.ContentTasks.addRetryHandler
);
await loginAndCompletePayment(frame);
await spawnPaymentDialogTask(
frame,
async function checkPostalCodeError() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
let state = await PTU.DialogContentUtils.waitForState(
content,
({ request }) => {
return request.completeStatus === "";
},
"Wait for completeStatus from DOM update"
);
is(state.request.completeStatus, "", "Check completeStatus");
is(
state.request.paymentDetails.paymentMethodErrors.billingAddress
.postalCode,
"Your postal code isn't valid",
"Check postalCode error string in state"
);
ok(!state.changesPrevented, "Changes no longer prevented");
is(
state.page.id,
"payment-summary",
"Check still on payment-summary"
);
todo(
content.document
.querySelector("#payment-summary")
.innerText.includes("Your postal code isn't valid"),
"Bug 1491815: Check error visibility on summary page"
);
todo(
content.document.getElementById("pay").disabled,
"Bug 1491815: Pay button should be disabled until the field error is addressed"
);
}
);
info(
"Changing the billingAddress postalCode to be valid without changing selectedPaymentCard"
);
await navigateToAddCardPage(frame, {
addLinkSelector: "payment-method-picker .edit-link",
});
await navigateToAddAddressPage(frame, {
addLinkSelector: ".billingAddressRow .edit-link",
initialPageId: "basic-card-page",
addressPageId: "billing-address-page",
});
let newPostalCode = "90210";
await fillInBillingAddressForm(frame, { "postal-code": newPostalCode });
await SpecialPowers.spawn(
browser,
[
{
eventName: "paymentmethodchange",
},
],
PTU.ContentTasks.promisePaymentResponseEvent
);
await submitAddressForm(frame, null, {
isEditing: true,
nextPageId: "basic-card-page",
});
await spawnPaymentDialogTask(frame, async function checkErrorsCleared() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
await PTU.DialogContentUtils.waitForState(
content,
state => {
return state.request.paymentDetails.paymentMethodErrors == null;
},
"Check no paymentMethod errors are present"
);
});
await spawnPaymentDialogTask(
frame,
PTU.DialogContentTasks.clickPrimaryButton
);
await spawnPaymentDialogTask(frame, async function checkErrorsCleared() {
let { PaymentTestUtils: PTU } = ChromeUtils.import(
"resource://testing-common/PaymentTestUtils.jsm"
);
await PTU.DialogContentUtils.waitForState(
content,
state => {
return state.request.paymentDetails.paymentMethodErrors == null;
},
"Check no card errors are present after save"
);
});
// TODO: Add an `await` here after bug 1477113.
SpecialPowers.spawn(
browser,
[
{
eventName: "paymentmethodchange",
},
],
PTU.ContentTasks.awaitPaymentEventPromise
);
await loginAndCompletePayment(frame);
// We can only check the retry response after the closing as it only resolves upon complete.
let { retryException } = await retryPromise;
ok(
!retryException,
"Expect no exception to be thrown when calling retry()"
);
let { retryException2 } = await retryPromise2;
ok(
!retryException2,
"Expect no exception to be thrown when calling retry()"
);
// Add a handler to complete the payment above.
info("acknowledging the completion from the merchant page");
let result = await SpecialPowers.spawn(
browser,
[],
PTU.ContentTasks.addCompletionHandler
);
// Verify response has the expected properties
let expectedDetails = Object.assign(
{
"cc-security-code": "123",
},
PTU.BasicCards.JaneMasterCard
);
let expectedBillingAddress = Object.assign({}, PTU.Addresses.TimBL, {
"postal-code": newPostalCode,
});
checkPaymentMethodDetailsMatchesCard(
result.response.details,
expectedDetails,
"Check response payment details"
);
checkPaymentAddressMatchesStorageAddress(
result.response.details.billingAddress,
expectedBillingAddress,
"Check response billing address"
);
await BrowserTestUtils.waitForCondition(
() => win.closed,
"dialog should be closed"
);
}
);
});