Bug 1495549 - Clear paymentMethod errors when the paymentMethod changes. r=jaws

Differential Revision: https://phabricator.services.mozilla.com/D13451

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Matthew Noorenberghe 2018-12-11 18:18:15 +00:00
Родитель b82e3514a4
Коммит 865adcf2a7
3 изменённых файлов: 258 добавлений и 6 удалений

Просмотреть файл

@ -154,6 +154,20 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
});
}
/**
* Called when the selectedPaymentCard or its relevant properties or billingAddress are changed.
* @param {string} selectedPaymentCardGUID
*/
changePaymentMethod(selectedPaymentCardGUID) {
// Clear paymentMethod merchant errors when the paymentMethod or billingAddress changes.
let request = Object.assign({}, this.requestStore.getState().request);
request.paymentDetails = Object.assign({}, request.paymentDetails);
request.paymentDetails.paymentMethodErrors = null;
this.requestStore.setState({request});
// TODO: Bug 1477113 - Dispatch paymentmethodchange
}
/**
* Called when the selectedPayerAddress or its relevant properties are changed.
* @param {string} payerAddressGUID
@ -216,6 +230,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
*/
async setStateFromParent(state) { // eslint-disable-line complexity
let oldAddresses = paymentRequest.getAddresses(this.requestStore.getState());
let oldBasicCards = paymentRequest.getBasicCards(this.requestStore.getState());
if (state.request) {
state = this._updateCompleteStatus(state);
}
@ -269,9 +284,28 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
}
}
let basicCards = paymentRequest.getBasicCards(state);
let oldPaymentMethod = selectedPaymentCard && oldBasicCards[selectedPaymentCard];
let paymentMethod = selectedPaymentCard && basicCards[selectedPaymentCard];
if (oldPaymentMethod && paymentMethod.guid == oldPaymentMethod.guid &&
paymentMethod.timeLastModified != oldPaymentMethod.timeLastModified) {
delete this._cachedState.selectedPaymentCard;
} else {
// Changes to the billing address record don't change the `timeLastModified`
// on the card record so we have to check for changes to the address separately.
let billingAddressGUID = paymentMethod && paymentMethod.billingAddressGUID;
let billingAddress = billingAddressGUID && addresses[billingAddressGUID];
let oldBillingAddress = billingAddressGUID && oldAddresses[billingAddressGUID];
if (oldBillingAddress && billingAddress &&
billingAddress.timeLastModified != oldBillingAddress.timeLastModified) {
delete this._cachedState.selectedPaymentCard;
}
}
// Ensure `selectedPaymentCard` never refers to a deleted payment card and refers
// to a payment card if one exists.
let basicCards = paymentRequest.getBasicCards(state);
if (!basicCards[selectedPaymentCard]) {
// Determining the initial selection is tracked in bug 1455789
this.requestStore.setState({
@ -398,6 +432,10 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
}
}
if (state.selectedPaymentCard != this._cachedState.selectedPaymentCard) {
this.changePaymentMethod(state.selectedPaymentCard);
}
if (this._isPayerRequested(state.request.paymentOptions)) {
if (state.selectedPayerAddress != this._cachedState.selectedPayerAddress) {
this.changePayerAddress(state.selectedPayerAddress);
@ -406,6 +444,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
this._cachedState.selectedShippingAddress = state.selectedShippingAddress;
this._cachedState.selectedShippingOption = state.selectedShippingOption;
this._cachedState.selectedPaymentCard = state.selectedPaymentCard;
this._cachedState.selectedPayerAddress = state.selectedPayerAddress;
}

Просмотреть файл

@ -236,7 +236,7 @@ var PaymentTestUtils = {
let {requestStore} = Cu.waiveXrays(content.document.querySelector("payment-dialog"));
let {page} = requestStore.getState();
let button = content.document.querySelector(`#${page.id} button.primary`);
ok(!button.disabled, "Primary button should not be disabled when clicking it");
ok(!button.disabled, `#${page.id} primary button should not be disabled when clicking it`);
button.click();
},
@ -260,7 +260,15 @@ var PaymentTestUtils = {
*
* @returns {undefined}
*/
completePayment: () => {
completePayment: async () => {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "payment-summary";
}, "Wait for change to payment-summary before clicking Pay");
let button = content.document.getElementById("pay");
ok(!button.disabled, "Pay button should not be disabled when clicking it");
button.click();

Просмотреть файл

@ -7,15 +7,19 @@
async function setup() {
await setupFormAutofillStorage();
await cleanupFormAutofillStorage();
// add 2 addresses and a card to avoid the FTU sequence and test address errors
// 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.JohnDoe]);
[PTU.BasicCards.JaneMasterCard, PTU.BasicCards.JohnDoe]);
info("associating the card with a billing address");
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;
}
@ -336,3 +340,204 @@ add_task(async function test_retry_with_payerErrors() {
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, 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 = ContentTask.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 = ContentTask.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 ContentTask.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.
ContentTask.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 ContentTask.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");
});
});