зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1429189 - Show shipping address errors on the summary screen. r=mattn
MozReview-Commit-ID: LaXrvWliWna --HG-- extra : rebase_source : 6b0639a39a92192dc0c4cbc9392d97e16717adfc
This commit is contained in:
Родитель
d14436478f
Коммит
66a1567f22
|
@ -108,6 +108,9 @@ var paymentDialogWrapper = {
|
|||
if (!requestId || typeof(requestId) != "string") {
|
||||
throw new Error("Invalid PaymentRequest ID");
|
||||
}
|
||||
|
||||
// The Request object returned by the Payment Service is live and
|
||||
// will automatically get updated if event.updateWith is used.
|
||||
this.request = paymentSrv.getPaymentRequestById(requestId);
|
||||
|
||||
if (!this.request) {
|
||||
|
@ -250,6 +253,19 @@ var paymentDialogWrapper = {
|
|||
});
|
||||
},
|
||||
|
||||
updateRequest() {
|
||||
// There is no need to update this.request since the object is live
|
||||
// and will automatically get updated if event.updateWith is used.
|
||||
let requestSerialized = this._serializeRequest(this.request);
|
||||
|
||||
this.mm.sendAsyncMessage("paymentChromeToContent", {
|
||||
messageType: "updateState",
|
||||
data: {
|
||||
request: requestSerialized,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Recursively convert and filter input to the subset of data types supported by JSON
|
||||
*
|
||||
|
|
|
@ -80,7 +80,13 @@ PaymentUIService.prototype = {
|
|||
},
|
||||
|
||||
updatePayment(requestId) {
|
||||
let dialog = this.findDialog(requestId);
|
||||
this.log.debug("updatePayment:", requestId);
|
||||
if (!dialog) {
|
||||
this.log.error("updatePayment: no dialog found");
|
||||
return;
|
||||
}
|
||||
dialog.paymentDialogWrapper.updateRequest();
|
||||
},
|
||||
|
||||
// other helper methods
|
||||
|
@ -90,17 +96,25 @@ PaymentUIService.prototype = {
|
|||
* @returns {boolean} whether the specified dialog was closed.
|
||||
*/
|
||||
closeDialog(requestId) {
|
||||
let win = this.findDialog(requestId);
|
||||
if (!win) {
|
||||
return false;
|
||||
}
|
||||
this.log.debug(`closing: ${win.name}`);
|
||||
win.close();
|
||||
return true;
|
||||
},
|
||||
|
||||
findDialog(requestId) {
|
||||
let enu = Services.wm.getEnumerator(null);
|
||||
let win;
|
||||
while ((win = enu.getNext())) {
|
||||
if (win.name == `${this.REQUEST_ID_PREFIX}${requestId}`) {
|
||||
this.log.debug(`closing: ${win.name}`);
|
||||
win.close();
|
||||
return true;
|
||||
return win;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return null;
|
||||
},
|
||||
|
||||
requestIdForWindow(window) {
|
||||
|
|
|
@ -32,6 +32,7 @@ class PaymentDialog extends PaymentStateSubscriberMixin(HTMLElement) {
|
|||
|
||||
this._orderDetailsOverlay = contents.querySelector("#order-details-overlay");
|
||||
this._shippingRequestedEls = contents.querySelectorAll(".shippingRequested");
|
||||
this._errorText = contents.querySelector("#error-text");
|
||||
|
||||
this._disabledOverlay = contents.getElementById("disabled-overlay");
|
||||
|
||||
|
@ -178,14 +179,16 @@ class PaymentDialog extends PaymentStateSubscriberMixin(HTMLElement) {
|
|||
|
||||
render(state) {
|
||||
let request = state.request;
|
||||
let paymentDetails = request.paymentDetails;
|
||||
this._hostNameEl.textContent = request.topLevelPrincipal.URI.displayHost;
|
||||
|
||||
let totalItem = request.paymentDetails.totalItem;
|
||||
let totalItem = paymentDetails.totalItem;
|
||||
let totalAmountEl = this.querySelector("#total > currency-amount");
|
||||
totalAmountEl.value = totalItem.amount.value;
|
||||
totalAmountEl.currency = totalItem.amount.currency;
|
||||
|
||||
this._orderDetailsOverlay.hidden = !state.orderDetailsShowing;
|
||||
this._errorText.textContent = paymentDetails.error;
|
||||
for (let element of this._shippingRequestedEls) {
|
||||
element.hidden = !request.paymentOptions.requestShipping;
|
||||
}
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
<shipping-option-picker class="shippingRequested"></shipping-option-picker>
|
||||
<div><label>&paymentMethodsLabel;</label></div>
|
||||
<payment-method-picker selected-state-key="selectedPaymentCard"></payment-method-picker>
|
||||
<div id="error-text"></div>
|
||||
</section>
|
||||
|
||||
<footer id="controls-container">
|
||||
|
|
|
@ -36,6 +36,25 @@ this.PaymentTestUtils = {
|
|||
return true;
|
||||
},
|
||||
|
||||
updateWith: async ({eventName, details}) => {
|
||||
/* globals ok */
|
||||
if (details.error &&
|
||||
(!details.shippingOptions || details.shippingOptions.length)) {
|
||||
ok(false, "Need to clear the shipping options to show error text");
|
||||
}
|
||||
if (!details.total) {
|
||||
ok(false, "`total: { label, amount: { value, currency } }` is required for updateWith");
|
||||
}
|
||||
|
||||
content[eventName + "Promise"] =
|
||||
new Promise(resolve => {
|
||||
content.rq.addEventListener(eventName, event => {
|
||||
event.updateWith(details);
|
||||
resolve();
|
||||
}, {once: true});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new payment request and cache it as `rq`.
|
||||
*
|
||||
|
@ -67,7 +86,7 @@ this.PaymentTestUtils = {
|
|||
DialogContentTasks: {
|
||||
isElementVisible: selector => {
|
||||
let element = content.document.querySelector(selector);
|
||||
return !element.hidden;
|
||||
return element.getBoundingClientRect().height > 0;
|
||||
},
|
||||
|
||||
getShippingOptions: () => {
|
||||
|
@ -87,6 +106,14 @@ this.PaymentTestUtils = {
|
|||
};
|
||||
},
|
||||
|
||||
getErrorDetails: () => {
|
||||
let doc = content.document;
|
||||
let errorText = doc.querySelector("#error-text");
|
||||
return {
|
||||
text: errorText.textContent,
|
||||
};
|
||||
},
|
||||
|
||||
selectShippingAddressByCountry: country => {
|
||||
let doc = content.document;
|
||||
let addressPicker =
|
||||
|
@ -268,4 +295,42 @@ this.PaymentTestUtils = {
|
|||
requestShipping: true,
|
||||
},
|
||||
},
|
||||
|
||||
Addresses: {
|
||||
TimBL: {
|
||||
"given-name": "Timothy",
|
||||
"additional-name": "John",
|
||||
"family-name": "Berners-Lee",
|
||||
organization: "World Wide Web Consortium",
|
||||
"street-address": "32 Vassar Street\nMIT Room 32-G524",
|
||||
"address-level2": "Cambridge",
|
||||
"address-level1": "MA",
|
||||
"postal-code": "02139",
|
||||
country: "US",
|
||||
tel: "+16172535702",
|
||||
email: "timbl@example.org",
|
||||
},
|
||||
TimBL2: {
|
||||
"given-name": "Timothy",
|
||||
"additional-name": "John",
|
||||
"family-name": "Berners-Lee",
|
||||
organization: "World Wide Web Consortium",
|
||||
"street-address": "1 Pommes Frittes Place",
|
||||
"address-level2": "Berlin",
|
||||
"address-level1": "BE",
|
||||
"postal-code": "02138",
|
||||
country: "DE",
|
||||
tel: "+16172535702",
|
||||
email: "timbl@example.org",
|
||||
},
|
||||
},
|
||||
|
||||
BasicCards: {
|
||||
JohnDoe: {
|
||||
"cc-exp-month": 1,
|
||||
"cc-exp-year": 9999,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "999999999999",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ support-files =
|
|||
[browser_request_serialization.js]
|
||||
[browser_request_shipping.js]
|
||||
[browser_request_summary.js]
|
||||
[browser_shippingaddresschange_error.js]
|
||||
[browser_show_dialog.js]
|
||||
skip-if = os == 'win' && debug # bug 1418385
|
||||
[browser_total.js]
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
"use strict";
|
||||
|
||||
add_task(addSampleAddressesAndBasicCard);
|
||||
|
||||
add_task(async function test_show_error_on_addresschange() {
|
||||
await BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: BLANK_PAGE_URL,
|
||||
}, async browser => {
|
||||
let dialogReadyPromise = waitForWidgetReady();
|
||||
// start by creating a PaymentRequest, and show it
|
||||
await ContentTask.spawn(browser,
|
||||
{
|
||||
methodData: [PTU.MethodData.basicCard],
|
||||
details: PTU.Details.twoShippingOptions,
|
||||
options: PTU.Options.requestShippingOption,
|
||||
},
|
||||
PTU.ContentTasks.createAndShowRequest);
|
||||
|
||||
// get a reference to the UI dialog and the requestId
|
||||
let [win] = await Promise.all([getPaymentWidget(), dialogReadyPromise]);
|
||||
ok(win, "Got payment widget");
|
||||
let requestId = paymentUISrv.requestIdForWindow(win);
|
||||
ok(requestId, "requestId should be defined");
|
||||
is(win.closed, false, "dialog should not be closed");
|
||||
|
||||
let frame = await getPaymentFrame(win);
|
||||
ok(frame, "Got payment frame");
|
||||
|
||||
info("setting up the event handler for shippingoptionchange");
|
||||
let EXPECTED_ERROR_TEXT = "Cannot ship with option 1 on days that end with Y";
|
||||
await ContentTask.spawn(browser, {
|
||||
eventName: "shippingoptionchange",
|
||||
details: {
|
||||
error: EXPECTED_ERROR_TEXT,
|
||||
shippingOptions: [],
|
||||
total: {
|
||||
label: "Grand total is!!!!!: ",
|
||||
amount: {
|
||||
value: "12",
|
||||
currency: "USD",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, PTU.ContentTasks.updateWith);
|
||||
await spawnPaymentDialogTask(frame,
|
||||
PTU.DialogContentTasks.selectShippingOptionById,
|
||||
"1");
|
||||
info("awaiting the shippingoptionchange event");
|
||||
await ContentTask.spawn(browser, {
|
||||
eventName: "shippingoptionchange",
|
||||
}, PTU.ContentTasks.awaitPaymentRequestEventPromise);
|
||||
|
||||
let errors = await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.getErrorDetails);
|
||||
is(errors.text, EXPECTED_ERROR_TEXT, "Error text should be present on dialog");
|
||||
let errorsVisible =
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.isElementVisible, "#error-text");
|
||||
ok(errorsVisible, "Error text should be visible");
|
||||
|
||||
info("setting up the event handler for shippingaddresschange");
|
||||
await ContentTask.spawn(browser, {
|
||||
eventName: "shippingaddresschange",
|
||||
details: {
|
||||
error: "",
|
||||
shippingOptions: PTU.Details.twoShippingOptions.shippingOptions,
|
||||
total: {
|
||||
label: "Grand total is now: ",
|
||||
amount: {
|
||||
value: "24",
|
||||
currency: "USD",
|
||||
},
|
||||
},
|
||||
},
|
||||
}, PTU.ContentTasks.updateWith);
|
||||
await spawnPaymentDialogTask(frame,
|
||||
PTU.DialogContentTasks.selectShippingAddressByCountry,
|
||||
"DE");
|
||||
info("awaiting the shippingaddresschange event");
|
||||
await ContentTask.spawn(browser, {
|
||||
eventName: "shippingaddresschange",
|
||||
}, PTU.ContentTasks.awaitPaymentRequestEventPromise);
|
||||
errors = await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.getErrorDetails);
|
||||
is(errors.text, "", "Error text should not be present on dialog");
|
||||
errorsVisible =
|
||||
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.isElementVisible, "#error-text");
|
||||
ok(!errorsVisible, "Error text should not be visible");
|
||||
|
||||
info("clicking cancel");
|
||||
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
|
||||
|
||||
await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
|
||||
});
|
||||
});
|
|
@ -153,7 +153,7 @@ add_task(async function test_show_completePayment() {
|
|||
|
||||
let frame = await getPaymentFrame(win);
|
||||
|
||||
ContentTask.spawn(browser, {
|
||||
await ContentTask.spawn(browser, {
|
||||
eventName: "shippingoptionchange",
|
||||
}, PTU.ContentTasks.promisePaymentRequestEvent);
|
||||
|
||||
|
|
|
@ -132,6 +132,23 @@ function spawnTaskInNewDialog(requestId, contentTaskFn, args = null) {
|
|||
});
|
||||
}
|
||||
|
||||
async function addSampleAddressesAndBasicCard() {
|
||||
let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
|
||||
(subject, data) => data == "add");
|
||||
profileStorage.addresses.add(PTU.Addresses.TimBL);
|
||||
await onChanged;
|
||||
|
||||
onChanged = TestUtils.topicObserved("formautofill-storage-changed",
|
||||
(subject, data) => data == "add");
|
||||
profileStorage.addresses.add(PTU.Addresses.TimBL2);
|
||||
await onChanged;
|
||||
|
||||
onChanged = TestUtils.topicObserved("formautofill-storage-changed",
|
||||
(subject, data) => data == "add");
|
||||
profileStorage.creditCards.add(PTU.BasicCards.JohnDoe);
|
||||
await onChanged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a merchant tab with the given merchantTaskFn to create a PaymentRequest
|
||||
* and then open the associated PaymentRequest dialog in a new tab and run the
|
||||
|
|
Загрузка…
Ссылка в новой задаче