summaryrefslogtreecommitdiffstats
path: root/dom/payments/test
diff options
context:
space:
mode:
Diffstat (limited to 'dom/payments/test')
-rw-r--r--dom/payments/test/BasicCardErrorsChromeScript.js133
-rw-r--r--dom/payments/test/BasiccardChromeScript.js372
-rw-r--r--dom/payments/test/Bug1478740ChromeScript.js90
-rw-r--r--dom/payments/test/Bug1490698ChromeScript.js226
-rw-r--r--dom/payments/test/ClosePaymentChromeScript.js160
-rw-r--r--dom/payments/test/ConstructorChromeScript.js490
-rw-r--r--dom/payments/test/CurrencyAmountValidationChromeScript.js67
-rw-r--r--dom/payments/test/DefaultData.js59
-rw-r--r--dom/payments/test/GeneralChromeScript.js20
-rw-r--r--dom/payments/test/PMIValidationChromeScript.js82
-rw-r--r--dom/payments/test/PayerDetailsChromeScript.js83
-rw-r--r--dom/payments/test/RequestShippingChromeScript.js116
-rw-r--r--dom/payments/test/RetryPaymentChromeScript.js238
-rw-r--r--dom/payments/test/ShippingOptionsChromeScript.js120
-rw-r--r--dom/payments/test/ShowPaymentChromeScript.js393
-rw-r--r--dom/payments/test/UpdateErrorsChromeScript.js214
-rw-r--r--dom/payments/test/blank_page.html16
-rw-r--r--dom/payments/test/browser.ini10
-rw-r--r--dom/payments/test/browser_payment_in_different_tabs.js37
-rw-r--r--dom/payments/test/bug1478740.html44
-rw-r--r--dom/payments/test/echo_payment_request.html37
-rw-r--r--dom/payments/test/head.js127
-rw-r--r--dom/payments/test/mochitest.ini53
-rw-r--r--dom/payments/test/simple_payment_request.html81
-rw-r--r--dom/payments/test/test_abortPayment.html95
-rw-r--r--dom/payments/test/test_basiccard.html371
-rw-r--r--dom/payments/test/test_basiccarderrors.html85
-rw-r--r--dom/payments/test/test_block_none10s.html58
-rw-r--r--dom/payments/test/test_bug1478740.html140
-rw-r--r--dom/payments/test/test_bug1490698.html119
-rw-r--r--dom/payments/test/test_canMakePayment.html164
-rw-r--r--dom/payments/test/test_closePayment.html284
-rw-r--r--dom/payments/test/test_constructor.html351
-rw-r--r--dom/payments/test/test_currency_amount_validation.html353
-rw-r--r--dom/payments/test/test_payerDetails.html107
-rw-r--r--dom/payments/test/test_payment-request-in-iframe.html168
-rw-r--r--dom/payments/test/test_pmi_validation.html245
-rw-r--r--dom/payments/test/test_requestShipping.html180
-rw-r--r--dom/payments/test/test_retryPayment.html354
-rw-r--r--dom/payments/test/test_shippingOptions.html208
-rw-r--r--dom/payments/test/test_showPayment.html504
-rw-r--r--dom/payments/test/test_update_errors.html121
42 files changed, 7175 insertions, 0 deletions
diff --git a/dom/payments/test/BasicCardErrorsChromeScript.js b/dom/payments/test/BasicCardErrorsChromeScript.js
new file mode 100644
index 0000000000..f92e5eef5c
--- /dev/null
+++ b/dom/payments/test/BasicCardErrorsChromeScript.js
@@ -0,0 +1,133 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+const defaultCard = {
+ cardholderName: "",
+ cardNumber: "4111111111111111",
+ expiryMonth: "",
+ expiryYear: "",
+ cardSecurityCode: "",
+ billingAddress: null,
+};
+
+function makeBillingAddress() {
+ const billingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+ ].createInstance(Ci.nsIPaymentAddress);
+ const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+ );
+ const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ address.data = "Easton Ave";
+ addressLine.appendElement(address);
+ const addressArgs = [
+ "USA", // country
+ addressLine, // address line
+ "CA", // region
+ "CA", // regionCode
+ "San Bruno", // city
+ "", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "", // organization
+ "Bill A. Pacheco", // recipient
+ "+14344413879", // phone
+ ];
+ billingAddress.init(...addressArgs);
+ return billingAddress;
+}
+
+function makeBasicCardResponse(details) {
+ const basicCardResponseData = Cc[
+ "@mozilla.org/dom/payments/basiccard-response-data;1"
+ ].createInstance(Ci.nsIBasicCardResponseData);
+ const {
+ cardholderName,
+ cardNumber,
+ expiryMonth,
+ expiryYear,
+ cardSecurityCode,
+ billingAddress,
+ } = details;
+
+ const address =
+ billingAddress !== undefined ? billingAddress : makeBillingAddress();
+
+ basicCardResponseData.initData(
+ cardholderName,
+ cardNumber,
+ expiryMonth,
+ expiryYear,
+ cardSecurityCode,
+ address
+ );
+
+ return basicCardResponseData;
+}
+
+const TestingUIService = {
+ showPayment(requestId, details = { ...defaultCard }) {
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ makeBasicCardResponse(details),
+ "Person name",
+ "Person email",
+ "Person phone"
+ );
+
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ // Handles response.retry({ paymentMethod }):
+ updatePayment(requestId) {
+ // Let's echo what was sent in by the error...
+ const request = paymentSrv.getPaymentRequestById(requestId);
+ this.showPayment(requestId, request.paymentDetails.paymentMethodErrors);
+ },
+ // Handles response.complete()
+ completePayment(requestId) {
+ const request = paymentSrv.getPaymentRequestById(requestId);
+ const completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ get QueryInterface() {
+ return ChromeUtils.generateQI(["nsIPaymentUIService"]);
+ },
+};
+
+paymentSrv.setTestingUIService(
+ TestingUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("teardown", () => {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/BasiccardChromeScript.js b/dom/payments/test/BasiccardChromeScript.js
new file mode 100644
index 0000000000..6ce2ca024b
--- /dev/null
+++ b/dom/payments/test/BasiccardChromeScript.js
@@ -0,0 +1,372 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", `${DummyUIService.testName}: ${message}`);
+}
+
+const billingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+billingAddress.init(
+ "USA", // country
+ addressLine, // address line
+ "CA", // region
+ "CA", // region code
+ "San Bruno", // city
+ "", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "", // organization
+ "Bill A. Pacheco", // recipient
+ "+14344413879"
+); // phone
+
+const specialAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const specialAddressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const specialData = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+specialData.data = ":$%@&*";
+specialAddressLine.appendElement(specialData);
+specialAddress.init(
+ "USA", // country
+ specialAddressLine, // address line
+ "CA", // region
+ "CA", // region code
+ "San Bruno", // city
+ "", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "", // organization
+ "Bill A. Pacheco", // recipient
+ "+14344413879"
+); // phone
+
+const basiccardResponseData = Cc[
+ "@mozilla.org/dom/payments/basiccard-response-data;1"
+].createInstance(Ci.nsIBasicCardResponseData);
+
+const basiccardChangeDetails = Cc[
+ "@mozilla.org/dom/payments/basiccard-change-details;1"
+].createInstance(Ci.nsIBasicCardChangeDetails);
+
+const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+].createInstance(Ci.nsIPaymentShowActionResponse);
+
+function abortPaymentResponse(requestId) {
+ let abortResponse = Cc[
+ "@mozilla.org/dom/payments/payment-abort-action-response;1"
+ ].createInstance(Ci.nsIPaymentAbortActionResponse);
+ abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+ paymentSrv.respondPayment(
+ abortResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function completePaymentResponse(requestId) {
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function showRequest(requestId) {
+ if (DummyUIService.showAction === "payment-method-change") {
+ basiccardChangeDetails.initData(billingAddress);
+ try {
+ paymentSrv.changePaymentMethod(
+ requestId,
+ "basic-card",
+ basiccardChangeDetails.QueryInterface(Ci.nsIMethodChangeDetails)
+ );
+ } catch (error) {
+ emitTestFail(
+ `Unexpected error (${error.name}) when calling PaymentRequestService::changePaymentMethod`
+ );
+ }
+ return;
+ }
+ if (DummyUIService.showAction === "detailBasicCardResponse") {
+ try {
+ basiccardResponseData.initData(
+ "Bill A. Pacheco", // cardholderName
+ "4916855166538720", // cardNumber
+ "01", // expiryMonth
+ "2024", // expiryYear
+ "180", // cardSecurityCode
+ billingAddress
+ ); // billingAddress
+ } catch (e) {
+ emitTestFail("Fail to initialize basic card response data.");
+ }
+ }
+ if (DummyUIService.showAction === "simpleBasicCardResponse") {
+ try {
+ basiccardResponseData.initData(
+ "", // cardholderName
+ "4916855166538720", // cardNumber
+ "", // expiryMonth
+ "", // expiryYear
+ "", // cardSecurityCode
+ null
+ ); // billingAddress
+ } catch (e) {
+ emitTestFail("Fail to initialize basic card response data.");
+ }
+ }
+ if (DummyUIService.showAction === "specialAddressResponse") {
+ try {
+ basiccardResponseData.initData(
+ "Bill A. Pacheco", // cardholderName
+ "4916855166538720", // cardNumber
+ "01", // expiryMonth
+ "2024", // expiryYear
+ "180", // cardSecurityCode
+ specialAddress
+ ); // billingAddress
+ } catch (e) {
+ emitTestFail("Fail to initialize basic card response data.");
+ }
+ }
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ basiccardResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+const DummyUIService = {
+ testName: "",
+ showAction: "",
+ showPayment: showRequest,
+ abortPayment: abortPaymentResponse,
+ completePayment: completePaymentResponse,
+ updatePayment: requestId => {
+ try {
+ basiccardResponseData.initData(
+ "Bill A. Pacheco", // cardholderName
+ "4916855166538720", // cardNumber
+ "01", // expiryMonth
+ "2024", // expiryYear
+ "180", // cardSecurityCode
+ billingAddress
+ ); // billingAddress
+ } catch (e) {
+ emitTestFail("Fail to initialize basic card response data.");
+ }
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ basiccardResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ closePayment: requestId => {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("set-detailed-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.showAction = "detailBasicCardResponse";
+ sendAsyncMessage("set-detailed-ui-service-complete");
+});
+
+addMessageListener("set-simple-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.showAction = "simpleBasicCardResponse";
+ sendAsyncMessage("set-simple-ui-service-complete");
+});
+
+addMessageListener("set-special-address-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.showAction = "specialAddressResponse";
+ sendAsyncMessage("set-special-address-ui-service-complete");
+});
+
+addMessageListener("method-change-to-basic-card", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.showAction = "payment-method-change";
+ sendAsyncMessage("method-change-to-basic-card-complete");
+});
+
+addMessageListener("error-response-test", function (testName) {
+ // test empty cardNumber
+ try {
+ basiccardResponseData.initData("", "", "", "", "", null);
+ emitTestFail(
+ "BasicCardResponse should not be initialized with empty cardNumber."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "Empty cardNumber expected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+
+ // test invalid expiryMonth 123
+ try {
+ basiccardResponseData.initData("", "4916855166538720", "123", "", "", null);
+ emitTestFail(
+ "BasicCardResponse should not be initialized with invalid expiryMonth '123'."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "expiryMonth 123 expected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+ // test invalid expiryMonth 99
+ try {
+ basiccardResponseData.initData("", "4916855166538720", "99", "", "", null);
+ emitTestFail(
+ "BasicCardResponse should not be initialized with invalid expiryMonth '99'."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "expiryMonth 99 xpected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+ // test invalid expiryMonth ab
+ try {
+ basiccardResponseData.initData("", "4916855166538720", "ab", "", "", null);
+ emitTestFail(
+ "BasicCardResponse should not be initialized with invalid expiryMonth 'ab'."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "expiryMonth ab expected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+ // test invalid expiryYear abcd
+ try {
+ basiccardResponseData.initData(
+ "",
+ "4916855166538720",
+ "",
+ "abcd",
+ "",
+ null
+ );
+ emitTestFail(
+ "BasicCardResponse should not be initialized with invalid expiryYear 'abcd'."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "expiryYear abcd expected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+ // test invalid expiryYear 11111
+ try {
+ basiccardResponseData.initData(
+ "",
+ "4916855166538720",
+ "",
+ "11111",
+ "",
+ null
+ );
+ emitTestFail(
+ "BasicCardResponse should not be initialized with invalid expiryYear '11111'."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "expiryYear 11111 expected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ try {
+ responseData.initData({});
+ } catch (e) {
+ emitTestFail("Fail to initialize response data with empty object.");
+ }
+
+ try {
+ showResponse.init(
+ "testid",
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ responseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ emitTestFail(
+ "nsIPaymentShowActionResponse should not be initialized with basic-card method and nsIGeneralResponseData."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "ShowResponse init expected 'NS_ERROR_FAILURE', but got " + e.name + "."
+ );
+ }
+ }
+ sendAsyncMessage("error-response-test-complete");
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/Bug1478740ChromeScript.js b/dom/payments/test/Bug1478740ChromeScript.js
new file mode 100644
index 0000000000..6e8633ff87
--- /dev/null
+++ b/dom/payments/test/Bug1478740ChromeScript.js
@@ -0,0 +1,90 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", message);
+}
+
+function rejectPayment(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "", // payment method
+ responseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+const DummyUIService = {
+ testName: "",
+ requestId: "",
+ showPayment(requestId) {
+ this.requestId = requestId;
+ sendAsyncMessage("showing-payment", { data: "successful" });
+ },
+ abortPayment(requestId) {
+ this.requestId = requestId;
+ },
+ completePayment(requestId) {
+ this.requestId = requestId;
+ },
+ updatePayment(requestId) {
+ this.requestId = requestId;
+ },
+ closePayment(requestId) {
+ this.requestId = requestId;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("reject-payment", function () {
+ rejectPayment(DummyUIService.requestId);
+ sendAsyncMessage("reject-payment-complete");
+});
+
+addMessageListener("start-test", function (testName) {
+ DummyUIService.testName = testName;
+ sendAsyncMessage("start-test-complete");
+});
+
+addMessageListener("finish-test", function () {
+ DummyUIService.testName = "";
+ sendAsyncMessage("finish-test-complete");
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/Bug1490698ChromeScript.js b/dom/payments/test/Bug1490698ChromeScript.js
new file mode 100644
index 0000000000..dbae51b117
--- /dev/null
+++ b/dom/payments/test/Bug1490698ChromeScript.js
@@ -0,0 +1,226 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", message);
+}
+
+const billingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+billingAddress.init(
+ "USA", // country
+ addressLine, // address line
+ "CA", // region
+ "CA", // region code
+ "San Bruno", // city
+ "", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "", // organization
+ "Bill A. Pacheco", // recipient
+ "+14344413879"
+); // phone
+
+function acceptPayment(requestId) {
+ const basiccardResponseData = Cc[
+ "@mozilla.org/dom/payments/basiccard-response-data;1"
+ ].createInstance(Ci.nsIBasicCardResponseData);
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ basiccardResponseData.initData(
+ "Bill A. Pacheco", // cardholderName
+ "4916855166538720", // cardNumber
+ "01", // expiryMonth
+ "2024", // expiryYear
+ "180", // cardSecurityCode
+ billingAddress
+ ); // billingAddress
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ basiccardResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function rejectPayment(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "", // payment method
+ responseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+const DummyUIService = {
+ testName: "",
+ requestId: "",
+ showPayment(requestId) {
+ this.requestId = requestId;
+ acceptPayment(requestId);
+ },
+ abortPaymen(requestId) {
+ this.requestId = requestId;
+ },
+ completePayment(requestId) {
+ this.requestId = requestId;
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ updatePayment(requestId) {
+ this.requestId = requestId;
+ },
+ closePayment(requestId) {
+ this.requestId = requestId;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("start-test", function (testName) {
+ DummyUIService.testName = testName;
+ sendAsyncMessage("start-test-complete");
+});
+
+addMessageListener("finish-test", function () {
+ DummyUIService.testName = "";
+ sendAsyncMessage("finish-test-complete");
+});
+
+addMessageListener("interact-with-payment", function () {
+ if (DummyUIService.requestId === "") {
+ emitTestFail(`${DummyUIService.testName}: Unexpected empty requestId`);
+ }
+ try {
+ acceptPayment(DummyUIService.requestId);
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected success when accepting PaymentRequest.`
+ );
+ } catch (err) {
+ if (err.name !== "NS_ERROR_FAILURE") {
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected '${err.name}' when accepting PaymentRequest.`
+ );
+ } else {
+ emitTestPass(
+ `${DummyUIService.testName}: Got expected 'NS_ERROR_FAILURE' when accepting PaymentRequest.`
+ );
+ }
+ }
+
+ try {
+ rejectPayment(DummyUIService.requestId);
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected success when rejecting PaymentRequest.`
+ );
+ } catch (err) {
+ if (err.name !== "NS_ERROR_FAILURE") {
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected '${err.name}' when rejecting PaymentRequest.`
+ );
+ } else {
+ emitTestPass(
+ `${DummyUIService.testName}: Got expected 'NS_ERROR_FAILURE' when rejecting PaymentRequest.`
+ );
+ }
+ }
+
+ try {
+ paymentSrv.changeShippingOption(
+ DummyUIService.requestId,
+ "error shippping option"
+ );
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected success when changing shippingOption.`
+ );
+ } catch (err) {
+ if (err.name !== "NS_ERROR_FAILURE") {
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected '${err.name}' when changin shippingOption.`
+ );
+ } else {
+ emitTestPass(
+ `${DummyUIService.testName}: Got expected 'NS_ERROR_FAILURE' when changing shippingOption.`
+ );
+ }
+ }
+
+ try {
+ paymentSrv.changeShippingOption(DummyUIService.requestId, billingAddress);
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected success when changing shippingAddress.`
+ );
+ } catch (err) {
+ if (err.name !== "NS_ERROR_FAILURE") {
+ emitTestFail(
+ `${DummyUIService.testName}: Got unexpected '${err.name}' when changing shippingAddress.`
+ );
+ } else {
+ emitTestPass(
+ `${DummyUIService.testName}: Got expected 'NS_ERROR_FAILURE' when changing shippingAddress.`
+ );
+ }
+ }
+ sendAsyncMessage("interact-with-payment-complete");
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/ClosePaymentChromeScript.js b/dom/payments/test/ClosePaymentChromeScript.js
new file mode 100644
index 0000000000..60433f8f11
--- /dev/null
+++ b/dom/payments/test/ClosePaymentChromeScript.js
@@ -0,0 +1,160 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", `${DummyUIService.testName}: ${message}`);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", `${DummyUIService.testName}: ${message}`);
+}
+
+addMessageListener("close-check", function () {
+ const paymentEnum = paymentSrv.enumerate();
+ if (paymentEnum.hasMoreElements()) {
+ emitTestFail("Non-empty PaymentRequest queue in PaymentRequestService.");
+ } else {
+ emitTestPass("Got empty PaymentRequest queue in PaymentRequestService.");
+ }
+ sendAsyncMessage("close-check-complete");
+});
+
+var setPaymentNums = 0;
+
+addMessageListener("payment-num-set", function () {
+ setPaymentNums = 0;
+ const paymentEnum = paymentSrv.enumerate();
+ while (paymentEnum.hasMoreElements()) {
+ setPaymentNums = setPaymentNums + 1;
+ paymentEnum.getNext();
+ }
+ sendAsyncMessage("payment-num-set-complete");
+});
+
+addMessageListener("payment-num-check", function (expectedNumPayments) {
+ const paymentEnum = paymentSrv.enumerate();
+ let numPayments = 0;
+ while (paymentEnum.hasMoreElements()) {
+ numPayments = numPayments + 1;
+ paymentEnum.getNext();
+ }
+ if (numPayments !== expectedNumPayments + setPaymentNums) {
+ emitTestFail(
+ "Expected '" +
+ expectedNumPayments +
+ "' PaymentRequests in PaymentRequestService" +
+ ", but got '" +
+ numPayments +
+ "'."
+ );
+ } else {
+ emitTestPass(
+ "Got expected '" +
+ numPayments +
+ "' PaymentRequests in PaymentRequestService."
+ );
+ }
+ // force cleanup PaymentRequests for clear environment to next testcase.
+ paymentSrv.cleanup();
+ sendAsyncMessage("payment-num-check-complete");
+});
+
+addMessageListener("test-setup", testName => {
+ DummyUIService.testName = testName;
+ sendAsyncMessage("test-setup-complete");
+});
+
+addMessageListener("reject-payment", expectedError => {
+ try {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ DummyUIService.respondRequestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "", // payment method
+ responseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ emitTestPass("Reject PaymentRequest successfully");
+ } catch (error) {
+ if (expectedError) {
+ if (error.name === "NS_ERROR_FAILURE") {
+ emitTestPass(
+ "Got expected NS_ERROR_FAILURE when responding a closed PaymentRequest"
+ );
+ sendAsyncMessage("reject-payment-complete");
+ return;
+ }
+ }
+ emitTestFail(
+ "Unexpected error '" +
+ error.name +
+ "' when reponding a closed PaymentRequest"
+ );
+ }
+ sendAsyncMessage("reject-payment-complete");
+});
+
+addMessageListener("update-payment", () => {
+ try {
+ paymentSrv.changeShippingOption(DummyUIService.respondRequestId, "");
+ emitTestPass("Change shippingOption succefully");
+ } catch (error) {
+ emitTestFail(
+ "Unexpected error '" + error.name + "' when changing the shipping option"
+ );
+ }
+ sendAsyncMessage("update-payment-complete");
+});
+
+const DummyUIService = {
+ testName: "",
+ respondRequestId: "",
+ showPayment: requestId => {
+ DummyUIService.respondRequestId = requestId;
+ },
+ abortPayment: requestId => {
+ DummyUIService.respondRequestId = requestId;
+ },
+ completePayment: requestId => {
+ DummyUIService.respondRequestId = requestId;
+ },
+ updatePayment: requestId => {
+ DummyUIService.respondRequestId = requestId;
+ },
+ closePayment: requestId => {
+ this.respondRequestId = requestId;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/ConstructorChromeScript.js b/dom/payments/test/ConstructorChromeScript.js
new file mode 100644
index 0000000000..17e59f6241
--- /dev/null
+++ b/dom/payments/test/ConstructorChromeScript.js
@@ -0,0 +1,490 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+
+function checkSimplestRequest(payRequest) {
+ if (payRequest.topLevelPrincipal.origin != "https://example.com") {
+ emitTestFail(
+ "Top level principal's Origin should be 'https://example.com', but got '" +
+ payRequest.topLevelPrincipal.origin +
+ "'."
+ );
+ }
+
+ if (payRequest.paymentMethods.length != 1) {
+ emitTestFail("paymentMethods' length should be 1.");
+ }
+
+ const methodData = payRequest.paymentMethods.queryElementAt(
+ 0,
+ Ci.nsIPaymentMethodData
+ );
+ if (!methodData) {
+ emitTestFail("Fail to get payment methodData.");
+ }
+ const supportedMethod = methodData.supportedMethods;
+ if (supportedMethod != "basic-card") {
+ emitTestFail("supported method should be 'basic-card'.");
+ }
+ if (methodData.data) {
+ emitTestFail("methodData.data should not exist.");
+ }
+
+ // checking the passed PaymentDetails parameter
+ const details = payRequest.paymentDetails;
+ if (details.totalItem.label != "Total") {
+ emitTestFail("total item's label should be 'Total'.");
+ }
+ if (details.totalItem.amount.currency != "USD") {
+ emitTestFail("total item's currency should be 'USD'.");
+ }
+ if (details.totalItem.amount.value != "1.00") {
+ emitTestFail("total item's value should be '1.00'.");
+ }
+
+ if (details.displayItems.length !== 0) {
+ emitTestFail("details.displayItems should be an empty array.");
+ }
+ if (details.modifiers.length !== 0) {
+ emitTestFail("details.modifiers should be an empty array.");
+ }
+ if (details.shippingOptions.length !== 0) {
+ emitTestFail("details.shippingOptions should be an empty array.");
+ }
+
+ // checking the default generated PaymentOptions parameter
+ const paymentOptions = payRequest.paymentOptions;
+ if (paymentOptions.requestPayerName) {
+ emitTestFail("requestPayerName option should be false.");
+ }
+ if (paymentOptions.requestPayerEmail) {
+ emitTestFail("requestPayerEmail option should be false.");
+ }
+ if (paymentOptions.requestPayerPhone) {
+ emitTestFail("requestPayerPhone option should be false.");
+ }
+ if (paymentOptions.requestShipping) {
+ emitTestFail("requestShipping option should be false.");
+ }
+ if (paymentOptions.shippingType != "shipping") {
+ emitTestFail("shippingType option should be 'shipping'.");
+ }
+}
+
+// eslint-disable-next-line complexity
+function checkComplexRequest(payRequest) {
+ if (payRequest.topLevelPrincipal.origin != "https://example.com") {
+ emitTestFail(
+ "Top level principal's origin should be 'https://example.com', but got '" +
+ payRequest.topLevelPrincipal.origin +
+ "'."
+ );
+ }
+
+ if (payRequest.paymentMethods.length != 1) {
+ emitTestFail("paymentMethods' length should be 1.");
+ }
+
+ const methodData = payRequest.paymentMethods.queryElementAt(
+ 0,
+ Ci.nsIPaymentMethodData
+ );
+ if (!methodData) {
+ emitTestFail("Fail to get payment methodData.");
+ }
+ let supportedMethod = methodData.supportedMethods;
+ if (supportedMethod != "basic-card") {
+ emitTestFail("supported method should be 'basic-card'.");
+ }
+ const data = methodData.data;
+ const supportedNetworks = data.supportedNetworks;
+ const expectedSupportedNetworks = [
+ "unionpay",
+ "visa",
+ "mastercard",
+ "amex",
+ "discover",
+ "diners",
+ "jcb",
+ "mir",
+ ];
+ if (supportedNetworks.length != expectedSupportedNetworks.length) {
+ emitTestFail(
+ "supportedNetworks.length should be " +
+ expectedSupportedNetworks.length +
+ ", but got " +
+ supportedNetworks.length +
+ "."
+ );
+ }
+ for (let idx = 0; idx < supportedNetworks.length; idx++) {
+ if (supportedNetworks[idx] != expectedSupportedNetworks[idx]) {
+ emitTestFail(
+ "supportedNetworks[" +
+ idx +
+ "] should be '" +
+ expectedSupportedNetworks[idx] +
+ "', but got '" +
+ supportedNetworks[idx] +
+ "'."
+ );
+ }
+ }
+ // checking the passed PaymentDetails parameter
+ const details = payRequest.paymentDetails;
+ if (details.id != "payment details") {
+ emitTestFail("details.id should be 'payment details'.");
+ }
+ if (details.totalItem.label != "Total") {
+ emitTestFail("total item's label should be 'Total'.");
+ }
+ if (details.totalItem.amount.currency != "USD") {
+ emitTestFail("total item's currency should be 'USD'.");
+ }
+ if (details.totalItem.amount.value != "100.00") {
+ emitTestFail("total item's value should be '100.00'.");
+ }
+
+ const displayItems = details.displayItems;
+ if (!details.displayItems) {
+ emitTestFail("details.displayItems should not be undefined.");
+ }
+ if (displayItems.length != 2) {
+ emitTestFail("displayItems' length should be 2.");
+ }
+ let item = displayItems.queryElementAt(0, Ci.nsIPaymentItem);
+ if (item.label != "First item") {
+ emitTestFail("1st display item's label should be 'First item'.");
+ }
+ if (item.amount.currency != "USD") {
+ emitTestFail("1st display item's currency should be 'USD'.");
+ }
+ if (item.amount.value != "60.00") {
+ emitTestFail("1st display item's value should be '60.00'.");
+ }
+ item = displayItems.queryElementAt(1, Ci.nsIPaymentItem);
+ if (item.label != "Second item") {
+ emitTestFail("2nd display item's label should be 'Second item'.");
+ }
+ if (item.amount.currency != "USD") {
+ emitTestFail("2nd display item's currency should be 'USD'.");
+ }
+ if (item.amount.value != "40.00") {
+ emitTestFail("2nd display item's value should be '40.00'.");
+ }
+
+ const modifiers = details.modifiers;
+ if (!modifiers) {
+ emitTestFail("details.displayItems should not be undefined.");
+ }
+ if (modifiers.length != 1) {
+ emitTestFail("modifiers' length should be 1.");
+ }
+ const modifier = modifiers.queryElementAt(0, Ci.nsIPaymentDetailsModifier);
+ const supportedMethods = modifier.supportedMethods;
+ if (supportedMethod != "basic-card") {
+ emitTestFail("modifier's supported method name should be 'basic-card'.");
+ }
+ if (modifier.total.label != "Discounted Total") {
+ emitTestFail("modifier's total label should be 'Discounted Total'.");
+ }
+ if (modifier.total.amount.currency != "USD") {
+ emitTestFail("modifier's total currency should be 'USD'.");
+ }
+ if (modifier.total.amount.value != "90.00") {
+ emitTestFail("modifier's total value should be '90.00'.");
+ }
+
+ const additionalItems = modifier.additionalDisplayItems;
+ if (additionalItems.length != 1) {
+ emitTestFail("additionalDisplayItems' length should be 1.");
+ }
+ const additionalItem = additionalItems.queryElementAt(0, Ci.nsIPaymentItem);
+ if (additionalItem.label != "basic-card discount") {
+ emitTestFail("additional item's label should be 'basic-card discount'.");
+ }
+ if (additionalItem.amount.currency != "USD") {
+ emitTestFail("additional item's currency should be 'USD'.");
+ }
+ if (additionalItem.amount.value != "-10.00") {
+ emitTestFail("additional item's value should be '-10.00'.");
+ }
+ if (modifier.data.discountProgramParticipantId != "86328764873265") {
+ emitTestFail(
+ "modifier's data should be '86328764873265', but got '" +
+ modifier.data.discountProgramParticipantId +
+ "'."
+ );
+ }
+
+ const shippingOptions = details.shippingOptions;
+ if (!shippingOptions) {
+ emitTestFail("details.shippingOptions should not be undefined.");
+ }
+ if (shippingOptions.length != 2) {
+ emitTestFail("shippingOptions' length should be 2.");
+ }
+ let shippingOption = shippingOptions.queryElementAt(
+ 0,
+ Ci.nsIPaymentShippingOption
+ );
+ if (shippingOption.id != "NormalShipping") {
+ emitTestFail("1st shippingOption's id should be 'NormalShipping'.");
+ }
+ if (shippingOption.label != "NormalShipping") {
+ emitTestFail("1st shippingOption's lable should be 'NormalShipping'.");
+ }
+ if (shippingOption.amount.currency != "USD") {
+ emitTestFail("1st shippingOption's amount currency should be 'USD'.");
+ }
+ if (shippingOption.amount.value != "10.00") {
+ emitTestFail("1st shippingOption's amount value should be '10.00'.");
+ }
+ if (!shippingOption.selected) {
+ emitTestFail("1st shippingOption should be selected.");
+ }
+ shippingOption = shippingOptions.queryElementAt(
+ 1,
+ Ci.nsIPaymentShippingOption
+ );
+ if (shippingOption.id != "FastShipping") {
+ emitTestFail("2nd shippingOption's id should be 'FastShipping'.");
+ }
+ if (shippingOption.label != "FastShipping") {
+ emitTestFail("2nd shippingOption's lable should be 'FastShipping'.");
+ }
+ if (shippingOption.amount.currency != "USD") {
+ emitTestFail("2nd shippingOption's amount currency should be 'USD'.");
+ }
+ if (shippingOption.amount.value != "30.00") {
+ emitTestFail("2nd shippingOption's amount value should be '30.00'.");
+ }
+ if (shippingOption.selected) {
+ emitTestFail("2nd shippingOption should not be selected.");
+ }
+
+ // checking the default generated PaymentOptions parameter
+ const paymentOptions = payRequest.paymentOptions;
+ if (!paymentOptions.requestPayerName) {
+ emitTestFail("requestPayerName option should be true.");
+ }
+ if (!paymentOptions.requestPayerEmail) {
+ emitTestFail("requestPayerEmail option should be true.");
+ }
+ if (!paymentOptions.requestPayerPhone) {
+ emitTestFail("requestPayerPhone option should be true.");
+ }
+ if (!paymentOptions.requestShipping) {
+ emitTestFail("requestShipping option should be true.");
+ }
+ if (paymentOptions.shippingType != "shipping") {
+ emitTestFail("shippingType option should be 'shipping'.");
+ }
+}
+
+function checkNonBasicCardRequest(payRequest) {
+ if (payRequest.paymentMethods.length != 1) {
+ emitTestFail("paymentMethods' length should be 1.");
+ }
+
+ const methodData = payRequest.paymentMethods.queryElementAt(
+ 0,
+ Ci.nsIPaymentMethodData
+ );
+ if (!methodData) {
+ emitTestFail("Fail to get payment methodData.");
+ }
+ const supportedMethod = methodData.supportedMethods;
+ if (supportedMethod != "testing-payment-method") {
+ emitTestFail("supported method should be 'testing-payment-method'.");
+ }
+
+ const paymentId = methodData.data.paymentId;
+ if (paymentId != "P3892940") {
+ emitTestFail(
+ "methodData.data.paymentId should be 'P3892940', but got " +
+ paymentId +
+ "."
+ );
+ }
+ const paymentType = methodData.data.paymentType;
+ if (paymentType != "prepaid") {
+ emitTestFail(
+ "methodData.data.paymentType should be 'prepaid', but got " +
+ paymentType +
+ "."
+ );
+ }
+
+ // checking the passed PaymentDetails parameter
+ const details = payRequest.paymentDetails;
+ if (details.totalItem.label != "Total") {
+ emitTestFail("total item's label should be 'Total'.");
+ }
+ if (details.totalItem.amount.currency != "USD") {
+ emitTestFail("total item's currency should be 'USD'.");
+ }
+ if (details.totalItem.amount.value != "1.00") {
+ emitTestFail("total item's value should be '1.00'.");
+ }
+
+ if (details.displayItems.length !== 0) {
+ emitTestFail("details.displayItems should be an zero length array.");
+ }
+ if (details.displayItems.length !== 0) {
+ emitTestFail("details.modifiers should be an zero length array.");
+ }
+ if (details.displayItems.length !== 0) {
+ emitTestFail("details.shippingOptions should be an zero length array.");
+ }
+
+ // checking the default generated PaymentOptions parameter
+ const paymentOptions = payRequest.paymentOptions;
+ if (paymentOptions.requestPayerName) {
+ emitTestFail("requestPayerName option should be false.");
+ }
+ if (paymentOptions.requestPayerEmail) {
+ emitTestFail("requestPayerEmail option should be false.");
+ }
+ if (paymentOptions.requestPayerPhone) {
+ emitTestFail("requestPayerPhone option should be false.");
+ }
+ if (paymentOptions.requestShipping) {
+ emitTestFail("requestShipping option should be false.");
+ }
+ if (paymentOptions.shippingType != "shipping") {
+ emitTestFail("shippingType option should be 'shipping'.");
+ }
+}
+
+function checkSimplestRequestHandler() {
+ const paymentEnum = paymentSrv.enumerate();
+ if (!paymentEnum.hasMoreElements()) {
+ emitTestFail(
+ "PaymentRequestService should have at least one payment request."
+ );
+ }
+ for (let payRequest of paymentEnum) {
+ if (!payRequest) {
+ emitTestFail("Fail to get existing payment request.");
+ break;
+ }
+ checkSimplestRequest(payRequest);
+ }
+ paymentSrv.cleanup();
+ sendAsyncMessage("check-complete");
+}
+
+function checkComplexRequestHandler() {
+ const paymentEnum = paymentSrv.enumerate();
+ if (!paymentEnum.hasMoreElements()) {
+ emitTestFail(
+ "PaymentRequestService should have at least one payment request."
+ );
+ }
+ for (let payRequest of paymentEnum) {
+ if (!payRequest) {
+ emitTestFail("Fail to get existing payment request.");
+ break;
+ }
+ checkComplexRequest(payRequest);
+ }
+ paymentSrv.cleanup();
+ sendAsyncMessage("check-complete");
+}
+
+function checkNonBasicCardRequestHandler() {
+ const paymentEnum = paymentSrv.enumerate();
+ if (!paymentEnum.hasMoreElements()) {
+ emitTestFail(
+ "PaymentRequestService should have at least one payment request."
+ );
+ }
+ for (let payRequest of paymentEnum) {
+ if (!payRequest) {
+ emitTestFail("Fail to get existing payment request.");
+ break;
+ }
+ checkNonBasicCardRequest(payRequest);
+ }
+ paymentSrv.cleanup();
+ sendAsyncMessage("check-complete");
+}
+
+function checkMultipleRequestsHandler() {
+ const paymentEnum = paymentSrv.enumerate();
+ if (!paymentEnum.hasMoreElements()) {
+ emitTestFail(
+ "PaymentRequestService should have at least one payment request."
+ );
+ }
+ for (let payRequest of paymentEnum) {
+ if (!payRequest) {
+ emitTestFail("Fail to get existing payment request.");
+ break;
+ }
+ if (payRequest.paymentDetails.id == "payment details") {
+ checkComplexRequest(payRequest);
+ } else {
+ checkSimplestRequest(payRequest);
+ }
+ }
+ paymentSrv.cleanup();
+ sendAsyncMessage("check-complete");
+}
+
+function checkCrossOriginTopLevelPrincipalHandler() {
+ const paymentEnum = paymentSrv.enumerate();
+ if (!paymentEnum.hasMoreElements()) {
+ emitTestFail(
+ "PaymentRequestService should have at least one payment request."
+ );
+ }
+ for (let payRequest of paymentEnum) {
+ if (!payRequest) {
+ emitTestFail("Fail to get existing payment request.");
+ break;
+ }
+ if (payRequest.topLevelPrincipal.origin != "https://example.com") {
+ emitTestFail(
+ "Top level principal's origin should be 'https://example.com', but got '" +
+ payRequest.topLevelPrincipal.origin +
+ "'."
+ );
+ }
+ }
+ paymentSrv.cleanup();
+ sendAsyncMessage("check-complete");
+}
+
+addMessageListener("check-simplest-request", checkSimplestRequestHandler);
+addMessageListener("check-complex-request", checkComplexRequestHandler);
+addMessageListener("check-multiple-requests", checkMultipleRequestsHandler);
+addMessageListener(
+ "check-nonbasiccard-request",
+ checkNonBasicCardRequestHandler
+);
+addMessageListener(
+ "check-cross-origin-top-level-principal",
+ checkCrossOriginTopLevelPrincipalHandler
+);
+
+addMessageListener("teardown", function () {
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/CurrencyAmountValidationChromeScript.js b/dom/payments/test/CurrencyAmountValidationChromeScript.js
new file mode 100644
index 0000000000..a15e79be18
--- /dev/null
+++ b/dom/payments/test/CurrencyAmountValidationChromeScript.js
@@ -0,0 +1,67 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+const InvalidDetailsUIService = {
+ showPayment(requestId) {
+ paymentSrv.changeShippingOption(requestId, "");
+ },
+ abortPayment(requestId) {
+ const abortResponse = Cc[
+ "@mozilla.org/dom/payments/payment-abort-action-response;1"
+ ].createInstance(Ci.nsIPaymentAbortActionResponse);
+ abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+ paymentSrv.respondPayment(
+ abortResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ completePayment(requestId) {},
+ updatePayment(requestId) {},
+ closePayment(requestId) {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+function checkLowerCaseCurrency() {
+ const paymentEnum = paymentSrv.enumerate();
+ if (!paymentEnum.hasMoreElements()) {
+ const msg =
+ "PaymentRequestService should have at least one payment request.";
+ sendAsyncMessage("test-fail", msg);
+ }
+ for (let payRequest of paymentEnum) {
+ if (!payRequest) {
+ sendAsyncMessage("test-fail", "Fail to get existing payment request.");
+ break;
+ }
+ const { currency } = payRequest.paymentDetails.totalItem.amount;
+ if (currency != "USD") {
+ const msg =
+ "Currency of PaymentItem total should be 'USD', but got ${currency}";
+ sendAsyncMessage("check-complete");
+ }
+ }
+ paymentSrv.cleanup();
+ sendAsyncMessage("check-complete");
+}
+
+addMessageListener("check-lower-case-currency", checkLowerCaseCurrency);
+
+addMessageListener("set-update-with-invalid-details-ui-service", () => {
+ paymentSrv.setTestingUIService(
+ InvalidDetailsUIService.QueryInterface(Ci.nsIPaymentUIService)
+ );
+});
+
+addMessageListener("teardown", () => sendAsyncMessage("teardown-complete"));
diff --git a/dom/payments/test/DefaultData.js b/dom/payments/test/DefaultData.js
new file mode 100644
index 0000000000..13723b5799
--- /dev/null
+++ b/dom/payments/test/DefaultData.js
@@ -0,0 +1,59 @@
+// testing data declation
+const defaultMethods = [
+ {
+ supportedMethods: "basic-card",
+ data: {
+ supportedNetworks: [
+ "unionpay",
+ "visa",
+ "mastercard",
+ "amex",
+ "discover",
+ "diners",
+ "jcb",
+ "mir",
+ ],
+ },
+ },
+ {
+ supportedMethods: "testing-payment-method",
+ },
+];
+
+const defaultDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00",
+ },
+ selected: false,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "5.00",
+ },
+ selected: false,
+ },
+ ],
+};
+
+const defaultOptions = {
+ requestPayerName: true,
+ requestPayerEmail: false,
+ requestPayerPhone: false,
+ requestShipping: true,
+ shippingType: "shipping",
+};
diff --git a/dom/payments/test/GeneralChromeScript.js b/dom/payments/test/GeneralChromeScript.js
new file mode 100644
index 0000000000..74fe4c299a
--- /dev/null
+++ b/dom/payments/test/GeneralChromeScript.js
@@ -0,0 +1,20 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/PMIValidationChromeScript.js b/dom/payments/test/PMIValidationChromeScript.js
new file mode 100644
index 0000000000..3deec2a89c
--- /dev/null
+++ b/dom/payments/test/PMIValidationChromeScript.js
@@ -0,0 +1,82 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+const UIService = {
+ showPayment(requestId) {
+ paymentSrv.changeShippingOption(requestId, "");
+ },
+ abortPayment(requestId) {
+ let abortResponse = Cc[
+ "@mozilla.org/dom/payments/payment-abort-action-response;1"
+ ].createInstance(Ci.nsIPaymentAbortActionResponse);
+ abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+ paymentSrv.respondPayment(
+ abortResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ completePayment(requestId) {
+ const completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ updatePayment(requestId) {
+ const showResponseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ showResponseData.initData({
+ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",
+ });
+
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "https://example.com", // payment method
+ showResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ closePayment(requestId) {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+
+addMessageListener("set-ui-service", function () {
+ paymentSrv.setTestingUIService(
+ UIService.QueryInterface(Ci.nsIPaymentUIService)
+ );
+});
+
+addMessageListener("teardown", function () {
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/PayerDetailsChromeScript.js b/dom/payments/test/PayerDetailsChromeScript.js
new file mode 100644
index 0000000000..77024cc754
--- /dev/null
+++ b/dom/payments/test/PayerDetailsChromeScript.js
@@ -0,0 +1,83 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+const TestingUIService = {
+ showPayment(requestId, name = "", email = "", phone = "") {
+ const showResponseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ showResponseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method", // payment method
+ showResponseData, // payment method data
+ name,
+ email,
+ phone
+ );
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ // .retry({ payer }) and .updateWith({payerErrors}) both get routed here:
+ updatePayment(requestId) {
+ // Let's echo what was sent in by the error...
+ const request = paymentSrv.getPaymentRequestById(requestId);
+ const { name, email, phone } = request.paymentDetails.payerErrors;
+ const { error } = request.paymentDetails;
+ // Let's use the .error as the switch
+ switch (error) {
+ case "retry-fire-payerdetaichangeevent": {
+ paymentSrv.changePayerDetail(requestId, name, email, phone);
+ break;
+ }
+ case "update-with": {
+ this.showPayment(requestId, name, email, phone);
+ break;
+ }
+ default:
+ const msg = `Expect details.error value: '${error}'`;
+ sendAsyncMessage("test-fail", msg);
+ }
+ },
+ completePayment(requestId) {
+ const request = paymentSrv.getPaymentRequestById(requestId);
+ const completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ get QueryInterface() {
+ return ChromeUtils.generateQI(["nsIPaymentUIService"]);
+ },
+};
+
+paymentSrv.setTestingUIService(
+ TestingUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("teardown", () => {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/RequestShippingChromeScript.js b/dom/payments/test/RequestShippingChromeScript.js
new file mode 100644
index 0000000000..d8dafb8a08
--- /dev/null
+++ b/dom/payments/test/RequestShippingChromeScript.js
@@ -0,0 +1,116 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+
+const shippingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+shippingAddress.init(
+ "", // country
+ addressLine, // address line
+ "", // region
+ "", // region code
+ "", // city
+ "", // dependent locality
+ "", // postal code
+ "", // sorting code
+ "", // organization
+ "", // recipient
+ ""
+); // phone
+
+const NormalUIService = {
+ shippingOptionChanged: false,
+ showPayment(requestId) {
+ paymentSrv.changeShippingAddress(requestId, shippingAddress);
+ },
+ abortPayment(requestId) {},
+ completePayment(requestId) {
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ updatePayment(requestId) {
+ let showResponse = null;
+ let payRequest = paymentSrv.getPaymentRequestById(requestId);
+
+ const shippingOptions = payRequest.paymentDetails.shippingOptions;
+ if (shippingOptions.length) {
+ emitTestFail("Wrong length for shippingOptions.");
+ }
+
+ const showResponseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+
+ try {
+ showResponseData.initData({
+ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",
+ });
+ } catch (e) {
+ emitTestFail(
+ 'Fail to initialize response data with { paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",}'
+ );
+ }
+
+ showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method", // payment method
+ showResponseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ closePayment(requestId) {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+addMessageListener("set-normal-ui-service", function () {
+ paymentSrv.setTestingUIService(
+ NormalUIService.QueryInterface(Ci.nsIPaymentUIService)
+ );
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/RetryPaymentChromeScript.js b/dom/payments/test/RetryPaymentChromeScript.js
new file mode 100644
index 0000000000..d1486d676d
--- /dev/null
+++ b/dom/payments/test/RetryPaymentChromeScript.js
@@ -0,0 +1,238 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", message);
+}
+
+const billingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+billingAddress.init(
+ "USA", // country
+ addressLine, // address line
+ "CA", // region
+ "CA", // region code
+ "San Bruno", // city
+ "", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "", // organization
+ "Bill A. Pacheco", // recipient
+ "+14344413879"
+); // phone
+
+function acceptPayment(requestId, mode) {
+ const basiccardResponseData = Cc[
+ "@mozilla.org/dom/payments/basiccard-response-data;1"
+ ].createInstance(Ci.nsIBasicCardResponseData);
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ basiccardResponseData.initData(
+ "Bill A. Pacheco", // cardholderName
+ "4916855166538720", // cardNumber
+ "01", // expiryMonth
+ "2024", // expiryYear
+ "180", // cardSecurityCode
+ billingAddress
+ ); // billingAddress
+ if (mode === "show") {
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ basiccardResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ }
+ if (mode == "retry") {
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "basic-card", // payment method
+ basiccardResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "bpacheco@test.org", // payer email
+ "+123456789"
+ ); // payer phone
+ }
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function rejectPayment(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "", // payment method
+ responseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function checkAddressErrors(testName, errors) {
+ if (!errors) {
+ emitTestFail(
+ `${testName}: Expect non-null shippingAddressErrors, but got null.`
+ );
+ return;
+ }
+ for (const [key, msg] of Object.entries(errors)) {
+ const expected = `${key} error`;
+ if (msg !== expected) {
+ emitTestFail(
+ `${testName}: Expected '${expected}' on shippingAddressErrors.${key}, but got '${msg}'.`
+ );
+ return;
+ }
+ }
+}
+
+function checkPayerErrors(testName, errors) {
+ if (!errors) {
+ emitTestFail(`${testName}: Expect non-null payerErrors, but got null.`);
+ return;
+ }
+ for (const [key, msg] of Object.entries(errors)) {
+ const expected = `${key} error`;
+ if (msg !== expected) {
+ emitTestFail(
+ `${testName}: Expected '${expected}' on payerErrors.${key}, but got '${msg}'.`
+ );
+ return;
+ }
+ }
+}
+
+function checkPaymentMethodErrors(testName, errors) {
+ if (!errors) {
+ emitTestFail(
+ `${testName} :Expect non-null paymentMethodErrors, but got null.`
+ );
+ return;
+ }
+ for (const [key, msg] of Object.entries(errors)) {
+ const expected = `method ${key} error`;
+ if (msg !== expected) {
+ emitTestFail(
+ `${testName}: Expected '${expected}' on paymentMethodErrors.${key}, but got '${msg}'.`
+ );
+ return;
+ }
+ }
+}
+
+const DummyUIService = {
+ testName: "",
+ rejectRetry: false,
+ showPayment(requestId) {
+ acceptPayment(requestId, "show");
+ },
+ abortPaymen(requestId) {
+ respondRequestId = requestId;
+ },
+ completePayment(requestId) {
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ updatePayment(requestId) {
+ const payment = paymentSrv.getPaymentRequestById(requestId);
+ if (payment.paymentDetails.error !== "error") {
+ emitTestFail(
+ "Expect 'error' on details.error, but got '" +
+ payment.paymentDetails.error +
+ "'"
+ );
+ }
+ checkAddressErrors(
+ this.testName,
+ payment.paymentDetails.shippingAddressErrors
+ );
+ checkPayerErrors(this.testName, payment.paymentDetails.payerErrors);
+ checkPaymentMethodErrors(
+ this.testName,
+ payment.paymentDetails.paymentMethodErrors
+ );
+ if (this.rejectRetry) {
+ rejectPayment(requestId);
+ } else {
+ acceptPayment(requestId, "retry");
+ }
+ },
+ closePayment: requestId => {
+ respondRequestId = requestId;
+ },
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("start-test", function (testName) {
+ DummyUIService.testName = testName;
+ sendAsyncMessage("start-test-complete");
+});
+
+addMessageListener("finish-test", function () {
+ DummyUIService.testName = "";
+ sendAsyncMessage("finish-test-complete");
+});
+
+addMessageListener("reject-retry", function () {
+ DummyUIService.rejectRetry = true;
+ sendAsyncMessage("reject-retry-complete");
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/ShippingOptionsChromeScript.js b/dom/payments/test/ShippingOptionsChromeScript.js
new file mode 100644
index 0000000000..d3f23ab391
--- /dev/null
+++ b/dom/payments/test/ShippingOptionsChromeScript.js
@@ -0,0 +1,120 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", message);
+}
+
+let expectedRequestOption = null;
+let expectedUpdatedOption = null;
+let changeShippingOption = null;
+
+function showResponse(requestId) {
+ const showResponseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ showResponseData.initData({});
+ const showActionResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showActionResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method", // payment method
+ showResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showActionResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function showRequest(requestId) {
+ let request = paymentSrv.getPaymentRequestById(requestId);
+ const message =
+ "request.shippingOption should be " +
+ expectedRequestOption +
+ " when calling show(), but got " +
+ request.shippingOption +
+ ".";
+ if (request.shippingOption != expectedRequestOption) {
+ emitTestFail(message);
+ } else {
+ emitTestPass(message);
+ }
+ if (changeShippingOption) {
+ paymentSrv.changeShippingOption(requestId, changeShippingOption);
+ } else {
+ showResponse(requestId);
+ }
+}
+
+function updateRequest(requestId) {
+ let request = paymentSrv.getPaymentRequestById(requestId);
+ const message =
+ "request.shippingOption should be " +
+ expectedUpdatedOption +
+ " when calling updateWith(), but got " +
+ request.shippingOption +
+ ".";
+ if (request.shippingOption != expectedUpdatedOption) {
+ emitTestFail(message);
+ } else {
+ emitTestPass(message);
+ }
+ showResponse(requestId);
+}
+
+const TestingUIService = {
+ showPayment: showRequest,
+ abortPayment(requestId) {},
+ completePayment(requestId) {
+ let request = paymentSrv.getPaymentRequestById(requestId);
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+ },
+ updatePayment: updateRequest,
+ closePayment(requestId) {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ TestingUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("set-expected-results", function (results) {
+ expectedRequestOption = results.requestResult;
+ expectedUpdatedOption = results.responseResult;
+ changeShippingOption = results.changeOptionResult;
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/ShowPaymentChromeScript.js b/dom/payments/test/ShowPaymentChromeScript.js
new file mode 100644
index 0000000000..c8848ab622
--- /dev/null
+++ b/dom/payments/test/ShowPaymentChromeScript.js
@@ -0,0 +1,393 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", `${DummyUIService.testName}: ${message}`);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", `${DummyUIService.testName}: ${message}`);
+}
+
+const shippingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+shippingAddress.init(
+ "USA", // country
+ addressLine, // address line
+ "CA", // region
+ "CA", // region code
+ "San Bruno", // city
+ "Test locality", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "Testing Org", // organization
+ "Bill A. Pacheco", // recipient
+ "+1-434-441-3879"
+); // phone
+
+function acceptShow(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({
+ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",
+ });
+ let showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method", // payment method
+ responseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function rejectShow(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "", // payment method
+ responseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function updateShow(requestId) {
+ if (DummyUIService.expectedUpdateAction == "updateaddress") {
+ paymentSrv.changeShippingAddress(requestId, shippingAddress);
+ } else if (
+ DummyUIService.expectedUpdateAction == "accept" ||
+ DummyUIService.expectedUpdateAction == "error"
+ ) {
+ paymentSrv.changeShippingOption(requestId, "FastShipping");
+ } else {
+ emitTestFail(
+ "Unknown expected update action: " + DummyUIService.expectedUpdateAction
+ );
+ }
+}
+
+function showRequest(requestId) {
+ const request = paymentSrv.getPaymentRequestById(requestId);
+ if (request.completeStatus == "initial") {
+ return;
+ }
+ if (DummyUIService.expectedShowAction == "accept") {
+ acceptShow(requestId);
+ } else if (DummyUIService.expectedShowAction == "reject") {
+ rejectShow(requestId);
+ } else if (DummyUIService.expectedShowAction == "update") {
+ updateShow(requestId);
+ } else {
+ emitTestFail(
+ "Unknown expected show action: " + DummyUIService.expectedShowAction
+ );
+ }
+}
+
+function abortRequest(requestId) {
+ let abortResponse = Cc[
+ "@mozilla.org/dom/payments/payment-abort-action-response;1"
+ ].createInstance(Ci.nsIPaymentAbortActionResponse);
+ abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+ paymentSrv.respondPayment(abortResponse);
+}
+
+function completeRequest(requestId) {
+ let request = paymentSrv.getPaymentRequestById(requestId);
+ if (DummyUIService.expectedCompleteStatus) {
+ if (request.completeStatus == DummyUIService.expectedCompleteStatus) {
+ emitTestPass(
+ "request.completeStatus matches expectation of " +
+ DummyUIService.expectedCompleteStatus
+ );
+ } else {
+ emitTestFail(
+ "request.completeStatus incorrect. Expected " +
+ DummyUIService.expectedCompleteStatus +
+ ", got " +
+ request.completeStatus
+ );
+ }
+ }
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function updateRequest(requestId) {
+ let request = paymentSrv.getPaymentRequestById(requestId);
+ if (request.completeStatus !== "") {
+ emitTestFail(
+ "request.completeStatus should be empty, but got '" +
+ request.completeStatus +
+ "'."
+ );
+ }
+ if (DummyUIService.expectedUpdateAction == "accept") {
+ if (request.paymentDetails.error != "") {
+ emitTestFail(
+ "updatedDetails should not have errors(" +
+ request.paymentDetails.error +
+ ")."
+ );
+ }
+ const shippingOptions = request.paymentDetails.shippingOptions;
+ let shippingOption = shippingOptions.queryElementAt(
+ 0,
+ Ci.nsIPaymentShippingOption
+ );
+ if (shippingOption.selected) {
+ emitTestFail(shippingOption.label + " should not be selected.");
+ }
+ shippingOption = shippingOptions.queryElementAt(
+ 1,
+ Ci.nsIPaymentShippingOption
+ );
+ if (!shippingOption.selected) {
+ emitTestFail(shippingOption.label + " should be selected.");
+ }
+ acceptShow(requestId);
+ } else if (DummyUIService.expectedUpdateAction == "error") {
+ if (request.paymentDetails.error != "Update with Error") {
+ emitTestFail(
+ "details.error should be 'Update with Error', but got " +
+ request.paymentDetails.error +
+ "."
+ );
+ }
+ rejectShow(requestId);
+ } else if (DummyUIService.expectedUpdateAction == "updateaddress") {
+ if (request.paymentDetails.error != "") {
+ emitTestFail(
+ "updatedDetails should not have errors(" +
+ request.paymentDetails.error +
+ ")."
+ );
+ }
+ DummyUIService.expectedUpdateAction = "accept";
+ paymentSrv.changeShippingOption(requestId, "FastShipping");
+ } else {
+ emitTestFail(
+ "Unknown expected update aciton: " + DummyUIService.expectedUpdateAction
+ );
+ }
+}
+
+const DummyUIService = {
+ testName: "",
+ expectedCompleteStatus: null,
+ expectedShowAction: "accept",
+ expectedUpdateAction: "accept",
+ showPayment: showRequest,
+ abortPayment: abortRequest,
+ completePayment: completeRequest,
+ updatePayment: updateRequest,
+ closePayment(requestId) {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+function testShowResponseInit() {
+ const showResponseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ try {
+ showResponseData.initData(null);
+ emitTestFail(
+ "nsIGeneralResponseData can not be initialized with null object."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_FAILURE") {
+ emitTestFail(
+ "Expected 'NS_ERROR_FAILURE' when initializing nsIGeneralResponseData with null object, but got " +
+ e.name +
+ "."
+ );
+ }
+ emitTestPass(
+ "Get expected result for initializing nsIGeneralResponseData with null object"
+ );
+ }
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ try {
+ showResponse.init(
+ "test request id",
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method", // payment method
+ showResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ emitTestPass(
+ "Get expected result for initializing response with accepted and empty data."
+ );
+ } catch (e) {
+ emitTestFail(
+ "Unexpected error " +
+ e.name +
+ " when initializing response with accepted and empty data."
+ );
+ }
+
+ try {
+ showResponse.init(
+ "test request id",
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "testing-payment-method",
+ null,
+ "Bill A. Pacheco",
+ "",
+ ""
+ );
+ emitTestPass(
+ "Get expected result for initializing response with rejected and null data."
+ );
+ } catch (e) {
+ emitTestFail(
+ "Unexpected error " +
+ e.name +
+ " when initializing response with rejected and null data."
+ );
+ }
+
+ try {
+ showResponse.init(
+ "test request id",
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method",
+ null,
+ "Bill A. Pacheco",
+ "",
+ ""
+ );
+ emitTestFail(
+ "nsIPaymentShowActionResponse can not be initialized with accpeted and null data."
+ );
+ } catch (e) {
+ if (e.name != "NS_ERROR_ILLEGAL_VALUE") {
+ emitTestFail(
+ "Expected 'NS_ERROR_ILLEGAL_VALUE', but got " + e.name + "."
+ );
+ }
+ emitTestPass(
+ "Get expected result for initializing response with accepted and null data."
+ );
+ }
+ sendAsyncMessage("test-show-response-init-complete");
+}
+
+addMessageListener("set-simple-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.expectedCompleteStatus = null;
+ DummyUIService.expectedShowAction = "accept";
+ DummyUIService.expectedUpdateAction = "accept";
+ sendAsyncMessage("set-simple-ui-service-complete");
+});
+
+addMessageListener("set-normal-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.expectedCompleteStatus = null;
+ DummyUIService.expectedShowAction = "update";
+ DummyUIService.expectedUpdateAction = "updateaddress";
+ sendAsyncMessage("set-normal-ui-service-complete");
+});
+
+addMessageListener("set-reject-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.expectedCompleteStatus = null;
+ DummyUIService.expectedShowAction = "reject";
+ DummyUIService.expectedUpdateAction = "error";
+ sendAsyncMessage("set-reject-ui-service-complete");
+});
+
+addMessageListener("set-update-with-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.expectedCompleteStatus = null;
+ DummyUIService.expectedShowAction = "update";
+ DummyUIService.expectedUpdateAction = "accept";
+ sendAsyncMessage("set-update-with-ui-service-complete");
+});
+
+addMessageListener("set-update-with-error-ui-service", function (testName) {
+ DummyUIService.testName = testName;
+ DummyUIService.expectedCompleteStatus = null;
+ DummyUIService.expectedShowAction = "update";
+ DummyUIService.expectedUpdateAction = "error";
+ sendAsyncMessage("set-update-with-error-ui-service-complete");
+});
+
+addMessageListener("test-show-response-init", testShowResponseInit);
+
+addMessageListener("set-complete-status-success", function () {
+ DummyUIService.expectedCompleteStatus = "success";
+ sendAsyncMessage("set-complete-status-success-complete");
+});
+
+addMessageListener("set-complete-status-fail", function () {
+ DummyUIService.expectedCompleteStatus = "fail";
+ sendAsyncMessage("set-complete-status-fail-complete");
+});
+
+addMessageListener("set-complete-status-unknown", function () {
+ DummyUIService.expectedCompleteStatus = "unknown";
+ sendAsyncMessage("set-complete-status-unknown-complete");
+});
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/UpdateErrorsChromeScript.js b/dom/payments/test/UpdateErrorsChromeScript.js
new file mode 100644
index 0000000000..ac288dbc15
--- /dev/null
+++ b/dom/payments/test/UpdateErrorsChromeScript.js
@@ -0,0 +1,214 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env mozilla/chrome-script */
+
+"use strict";
+
+const { XPCOMUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/XPCOMUtils.sys.mjs"
+);
+
+const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+].getService(Ci.nsIPaymentRequestService);
+
+function emitTestFail(message) {
+ sendAsyncMessage("test-fail", message);
+}
+function emitTestPass(message) {
+ sendAsyncMessage("test-pass", message);
+}
+
+const shippingAddress = Cc[
+ "@mozilla.org/dom/payments/payment-address;1"
+].createInstance(Ci.nsIPaymentAddress);
+const addressLine = Cc["@mozilla.org/array;1"].createInstance(
+ Ci.nsIMutableArray
+);
+const address = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+);
+address.data = "Easton Ave";
+addressLine.appendElement(address);
+shippingAddress.init(
+ "USA", // country
+ addressLine, // address line
+ "CA", // region
+ "CA", // region code
+ "San Bruno", // city
+ "Test locality", // dependent locality
+ "94066", // postal code
+ "123456", // sorting code
+ "Testing Org", // organization
+ "Bill A. Pacheco", // recipient
+ "+1-434-441-3879"
+); // phone
+
+function acceptShow(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({
+ paymentToken: "6880281f-0df3-4b8e-916f-66575e2457c1",
+ });
+ let showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_ACCEPTED,
+ "testing-payment-method", // payment method
+ responseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function rejectShow(requestId) {
+ const responseData = Cc[
+ "@mozilla.org/dom/payments/general-response-data;1"
+ ].createInstance(Ci.nsIGeneralResponseData);
+ responseData.initData({});
+ const showResponse = Cc[
+ "@mozilla.org/dom/payments/payment-show-action-response;1"
+ ].createInstance(Ci.nsIPaymentShowActionResponse);
+ showResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.PAYMENT_REJECTED,
+ "", // payment method
+ responseData, // payment method data
+ "", // payer name
+ "", // payer email
+ ""
+ ); // payer phone
+ paymentSrv.respondPayment(
+ showResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function updateShow(requestId) {
+ paymentSrv.changeShippingAddress(requestId, shippingAddress);
+}
+
+function showRequest(requestId) {
+ updateShow(requestId);
+}
+
+function abortRequest(requestId) {
+ let abortResponse = Cc[
+ "@mozilla.org/dom/payments/payment-abort-action-response;1"
+ ].createInstance(Ci.nsIPaymentAbortActionResponse);
+ abortResponse.init(requestId, Ci.nsIPaymentActionResponse.ABORT_SUCCEEDED);
+ paymentSrv.respondPayment(abortResponse);
+}
+
+function completeRequest(requestId) {
+ let payRequest = paymentSrv.getPaymentRequestById(requestId);
+ let completeResponse = Cc[
+ "@mozilla.org/dom/payments/payment-complete-action-response;1"
+ ].createInstance(Ci.nsIPaymentCompleteActionResponse);
+ completeResponse.init(
+ requestId,
+ Ci.nsIPaymentActionResponse.COMPLETE_SUCCEEDED
+ );
+ paymentSrv.respondPayment(
+ completeResponse.QueryInterface(Ci.nsIPaymentActionResponse)
+ );
+}
+
+function checkAddressErrors(errors) {
+ if (!errors) {
+ emitTestFail("Expect non-null shippingAddressErrors, but got null.");
+ }
+ if (errors.addressLine != "addressLine error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.addressLine as 'addressLine error', but got" +
+ errors.addressLine
+ );
+ }
+ if (errors.city != "city error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.city as 'city error', but got" + errors.city
+ );
+ }
+ if (errors.dependentLocality != "dependentLocality error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.dependentLocality as 'dependentLocality error', but got" +
+ errors.dependentLocality
+ );
+ }
+ if (errors.organization != "organization error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.organization as 'organization error', but got" +
+ errors.organization
+ );
+ }
+ if (errors.phone != "phone error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.phone as 'phone error', but got" +
+ errors.phone
+ );
+ }
+ if (errors.postalCode != "postalCode error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.postalCode as 'postalCode error', but got" +
+ errors.postalCode
+ );
+ }
+ if (errors.recipient != "recipient error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.recipient as 'recipient error', but got" +
+ errors.recipient
+ );
+ }
+ if (errors.region != "region error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.region as 'region error', but got" +
+ errors.region
+ );
+ }
+ if (errors.regionCode != "regionCode error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.regionCode as 'regionCode error', but got" +
+ errors.region
+ );
+ }
+ if (errors.sortingCode != "sortingCode error") {
+ emitTestFail(
+ "Expect shippingAddressErrors.sortingCode as 'sortingCode error', but got" +
+ errors.sortingCode
+ );
+ }
+}
+
+function updateRequest(requestId) {
+ let request = paymentSrv.getPaymentRequestById(requestId);
+ const addressErrors = request.paymentDetails.shippingAddressErrors;
+ const payerErrors = request.paymentDetails.payerErrors;
+ checkAddressErrors(addressErrors);
+ rejectShow(requestId);
+}
+
+const DummyUIService = {
+ showPayment: showRequest,
+ abortPayment: abortRequest,
+ completePayment: completeRequest,
+ updatePayment: updateRequest,
+ closePayment(requestId) {},
+ QueryInterface: ChromeUtils.generateQI(["nsIPaymentUIService"]),
+};
+
+paymentSrv.setTestingUIService(
+ DummyUIService.QueryInterface(Ci.nsIPaymentUIService)
+);
+
+addMessageListener("teardown", function () {
+ paymentSrv.setTestingUIService(null);
+ sendAsyncMessage("teardown-complete");
+});
diff --git a/dom/payments/test/blank_page.html b/dom/payments/test/blank_page.html
new file mode 100644
index 0000000000..7323b00a28
--- /dev/null
+++ b/dom/payments/test/blank_page.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Payment Request Testing</title>
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+ <meta content="utf-8" http-equiv="encoding">
+ </head>
+ <body>
+ <h1>blank page.html</h1>
+ <script type="text/javascript">
+ if(window.parent) {
+ window.parent.postMessage("successful", '*');
+ }
+ </script>
+ </body>
+</html>
diff --git a/dom/payments/test/browser.ini b/dom/payments/test/browser.ini
new file mode 100644
index 0000000000..1fb3809b80
--- /dev/null
+++ b/dom/payments/test/browser.ini
@@ -0,0 +1,10 @@
+[DEFAULT]
+prefs =
+ dom.payments.request.enabled=true
+skip-if =
+ true # we don't ship webpayments right now bug 1514425
+support-files =
+ head.js
+ simple_payment_request.html
+
+[browser_payment_in_different_tabs.js]
diff --git a/dom/payments/test/browser_payment_in_different_tabs.js b/dom/payments/test/browser_payment_in_different_tabs.js
new file mode 100644
index 0000000000..c811d32dd2
--- /dev/null
+++ b/dom/payments/test/browser_payment_in_different_tabs.js
@@ -0,0 +1,37 @@
+"use strict";
+
+// kTestRoot is from head.js
+const kTestPage = kTestRoot + "simple_payment_request.html";
+const TABS_TO_OPEN = 5;
+add_task(async () => {
+ Services.prefs.setBoolPref("dom.payments.request.enabled", true);
+ const tabs = [];
+ const options = {
+ gBrowser: Services.wm.getMostRecentWindow("navigator:browser").gBrowser,
+ url: kTestPage,
+ };
+ for (let i = 0; i < TABS_TO_OPEN; i++) {
+ const tab = await BrowserTestUtils.openNewForegroundTab(options);
+ tabs.push(tab);
+ }
+ const paymentSrv = Cc[
+ "@mozilla.org/dom/payments/payment-request-service;1"
+ ].getService(Ci.nsIPaymentRequestService);
+ const paymentEnum = paymentSrv.enumerate();
+ ok(
+ paymentEnum.hasMoreElements(),
+ "PaymentRequestService should have at least one payment request."
+ );
+ const payments = new Set();
+ for (let payment of paymentEnum) {
+ ok(payment, "Fail to get existing payment request.");
+ checkSimplePayment(payment);
+ payments.add(payment);
+ }
+ is(payments.size, TABS_TO_OPEN, `Should be ${TABS_TO_OPEN} unique objects.`);
+ for (const tab of tabs) {
+ await TestUtils.waitForTick();
+ BrowserTestUtils.removeTab(tab);
+ }
+ Services.prefs.setBoolPref("dom.payments.request.enabled", false);
+});
diff --git a/dom/payments/test/bug1478740.html b/dom/payments/test/bug1478740.html
new file mode 100644
index 0000000000..ddcc04bbb0
--- /dev/null
+++ b/dom/payments/test/bug1478740.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Payment Request Testing</title>
+<script>
+const methods = [
+ {
+ supportedMethods: "basic-card",
+ },
+];
+const details = {
+ id: "simple details",
+ total: {
+ label: "Donation",
+ amount: { currency: "USD", value: "55.00" },
+ },
+};
+const updatedDetails = {
+ id: "simple details",
+ total: {
+ label: "Donation",
+ amount: { currency: "USD", value: "55.00" },
+ },
+ error: "",
+};
+
+window.onmessage = async ({ data: action }) => {
+ let msg = "successful";
+ switch (action) {
+ case "Show Payment":
+ try {
+ let request = new PaymentRequest(methods, details);
+ let responsePromise = await request.show();
+ } catch (err) {
+ msg = err.name;
+ }
+ window.parent.postMessage(msg, "*")
+ break;
+ default:
+ window.parent.postMessage(`fail - unknown postmessage action: ${action}`, "*");
+ }
+};
+
+window.parent.postMessage("successful", "*");
+</script>
diff --git a/dom/payments/test/echo_payment_request.html b/dom/payments/test/echo_payment_request.html
new file mode 100644
index 0000000000..b1bf3da90c
--- /dev/null
+++ b/dom/payments/test/echo_payment_request.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Payment Request Testing</title>
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+ <meta content="utf-8" http-equiv="encoding">
+</head>
+<body>
+ <script>
+ window.onmessage = (e) => {
+ const paymentArgs = [[{supportedMethods: 'basic-card'}], {total: {label: 'label', amount: {currency: 'USD', value: '5.00'}}}];
+
+ if (e.data === 'new PaymentRequest') {
+ try {
+ new PaymentRequest(...paymentArgs);
+ if (window.parent) {
+ window.parent.postMessage("successful", '*');
+ }
+ } catch(ex) {
+ if (window.parent) {
+ window.parent.postMessage(ex.name, '*');
+ }
+ }
+ } else if (e.data === 'new PaymentRequest in a new iframe') {
+ var ifrr = document.createElement('iframe');
+ ifrr.allow = "payment";
+ ifrr.src = "https://example.com/tests/dom/payments/test/simple_payment_request.html";
+ document.body.appendChild(ifrr);
+ } else {
+ if (window.parent) {
+ window.parent.postMessage(e.data, '*');
+ }
+ }
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/payments/test/head.js b/dom/payments/test/head.js
new file mode 100644
index 0000000000..3a377e09ae
--- /dev/null
+++ b/dom/payments/test/head.js
@@ -0,0 +1,127 @@
+const kTestRoot = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+function checkSimplePayment(aSimplePayment) {
+ // checking the passed PaymentMethods parameter
+ is(
+ aSimplePayment.paymentMethods.length,
+ 1,
+ "paymentMethods' length should be 1."
+ );
+
+ const methodData = aSimplePayment.paymentMethods.queryElementAt(
+ 0,
+ Ci.nsIPaymentMethodData
+ );
+ ok(methodData, "Fail to get payment methodData.");
+ is(
+ methodData.supportedMethods,
+ "basic-card",
+ "supported method should be 'basic-card'."
+ );
+ ok(!methodData.data, "methodData.data should not exist.");
+
+ // checking the passed PaymentDetails parameter
+ const details = aSimplePayment.paymentDetails;
+ is(details.id, "simple details", "details.id should be 'simple details'.");
+ is(
+ details.totalItem.label,
+ "Donation",
+ "total item's label should be 'Donation'."
+ );
+ is(
+ details.totalItem.amount.currency,
+ "USD",
+ "total item's currency should be 'USD'."
+ );
+ is(
+ details.totalItem.amount.value,
+ "55.00",
+ "total item's value should be '55.00'."
+ );
+
+ is(
+ details.displayItems.length,
+ 0,
+ "details.displayItems should be a zero length array."
+ );
+ is(
+ details.modifiers.length,
+ 0,
+ "details.modifiers should be a zero length array."
+ );
+ is(
+ details.shippingOptions.length,
+ 0,
+ "details.shippingOptions should be a zero length array."
+ );
+
+ // checking the default generated PaymentOptions parameter
+ const paymentOptions = aSimplePayment.paymentOptions;
+ ok(!paymentOptions.requestPayerName, "payerName option should be false");
+ ok(!paymentOptions.requestPayerEmail, "payerEmail option should be false");
+ ok(!paymentOptions.requestPayerPhone, "payerPhone option should be false");
+ ok(!paymentOptions.requestShipping, "requestShipping option should be false");
+ is(
+ paymentOptions.shippingType,
+ "shipping",
+ "shippingType option should be 'shipping'"
+ );
+}
+
+function checkDupShippingOptionsPayment(aPayment) {
+ // checking the passed PaymentMethods parameter
+ is(aPayment.paymentMethods.length, 1, "paymentMethods' length should be 1.");
+
+ const methodData = aPayment.paymentMethods.queryElementAt(
+ 0,
+ Ci.nsIPaymentMethodData
+ );
+ ok(methodData, "Fail to get payment methodData.");
+ is(
+ methodData.supportedMethods,
+ "basic-card",
+ "methodData.supportedMethod name should be 'basic-card'."
+ );
+ ok(!methodData.data, "methodData.data should not exist.");
+
+ // checking the passed PaymentDetails parameter
+ const details = aPayment.paymentDetails;
+ is(
+ details.id,
+ "duplicate shipping options details",
+ "details.id should be 'duplicate shipping options details'."
+ );
+ is(
+ details.totalItem.label,
+ "Donation",
+ "total item's label should be 'Donation'."
+ );
+ is(
+ details.totalItem.amount.currency,
+ "USD",
+ "total item's currency should be 'USD'."
+ );
+ is(
+ details.totalItem.amount.value,
+ "55.00",
+ "total item's value should be '55.00'."
+ );
+
+ const shippingOptions = details.shippingOptions;
+ is(shippingOptions.length, 0, "shippingOptions' length should be 0.");
+
+ // checking the passed PaymentOptions parameter
+ const paymentOptions = aPayment.paymentOptions;
+ ok(paymentOptions.requestPayerName, "payerName option should be true");
+ ok(paymentOptions.requestPayerEmail, "payerEmail option should be true");
+ ok(paymentOptions.requestPayerPhone, "payerPhone option should be true");
+ ok(paymentOptions.requestShipping, "requestShipping option should be true");
+ is(
+ paymentOptions.shippingType,
+ "shipping",
+ "shippingType option should be 'shipping'"
+ );
+}
diff --git a/dom/payments/test/mochitest.ini b/dom/payments/test/mochitest.ini
new file mode 100644
index 0000000000..2b302feed6
--- /dev/null
+++ b/dom/payments/test/mochitest.ini
@@ -0,0 +1,53 @@
+[DEFAULT]
+prefs =
+ dom.payments.request.enabled=true
+# Android crashes on nearly all tests, bug 1525959
+skip-if =
+ true # we don't ship webpayments right now bug 1514425
+scheme = https
+support-files =
+ blank_page.html
+ bug1478740.html
+ simple_payment_request.html
+ echo_payment_request.html
+ BasiccardChromeScript.js
+ Bug1478740ChromeScript.js
+ BasicCardErrorsChromeScript.js
+ Bug1490698ChromeScript.js
+ ClosePaymentChromeScript.js
+ ConstructorChromeScript.js
+ CurrencyAmountValidationChromeScript.js
+ DefaultData.js
+ GeneralChromeScript.js
+ PayerDetailsChromeScript.js
+ PMIValidationChromeScript.js
+ RequestShippingChromeScript.js
+ RetryPaymentChromeScript.js
+ ShippingOptionsChromeScript.js
+ ShowPaymentChromeScript.js
+ UpdateErrorsChromeScript.js
+
+[test_abortPayment.html]
+run-if = nightly_build # Bug 1390018: Depends on the Nightly-only UI service
+skip-if = debug # Bug 1507251 - Leak
+[test_basiccard.html]
+[test_basiccarderrors.html]
+[test_block_none10s.html]
+skip-if = true # Bug 1408250: Don't expose PaymentRequest Constructor in non-e10s
+[test_bug1478740.html]
+[test_bug1490698.html]
+[test_canMakePayment.html]
+run-if = nightly_build # Bug 1390737: Depends on the Nightly-only UI service
+skip-if = debug # Bug 1507251 - Leak
+[test_closePayment.html]
+[test_constructor.html]
+skip-if = (os == "linux") || (os == "mac") || (os == "win" && os_version == "10.0") # Bug 1514425
+[test_currency_amount_validation.html]
+[test_payerDetails.html]
+[test_payment-request-in-iframe.html]
+[test_pmi_validation.html]
+[test_requestShipping.html]
+[test_retryPayment.html]
+[test_shippingOptions.html]
+[test_showPayment.html]
+[test_update_errors.html]
diff --git a/dom/payments/test/simple_payment_request.html b/dom/payments/test/simple_payment_request.html
new file mode 100644
index 0000000000..b532ba6101
--- /dev/null
+++ b/dom/payments/test/simple_payment_request.html
@@ -0,0 +1,81 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Payment Request Testing</title>
+<script>
+const methods = [
+ {
+ supportedMethods: "basic-card",
+ },
+];
+const details = {
+ id: "simple details",
+ total: {
+ label: "Donation",
+ amount: { currency: "USD", value: "55.00" },
+ },
+};
+const updatedDetails = {
+ id: "simple details",
+ total: {
+ label: "Donation",
+ amount: { currency: "USD", value: "55.00" },
+ },
+ error: "",
+};
+
+let request;
+let shippingChangedEvent;
+
+let msg = "successful";
+try {
+ request = new PaymentRequest(methods, details);
+ request.onshippingoptionchange = (event) => {
+ shippingChangedEvent = event;
+ window.parent.postMessage("successful", "*");
+ };
+ request.onshippingaddresschange = (event) => {
+ shippingChangedEvent = event;
+ window.parent.postMessage("successful", "*");
+ };
+
+} catch (err) {
+ msg = err.name;
+}
+window.parent.postMessage(msg, "*");
+
+
+if (request) {
+ window.onmessage = async ({ data: action }) => {
+ msg = "successful";
+ switch (action) {
+ case "show PaymentRequest":
+ const responsePromise = request.show();
+ window.parent.postMessage(msg, "*");
+ try {
+ await responsePromise;
+ } catch (err) {
+ if (err.name !== "AbortError") {
+ msg = err.name;
+ }
+ }
+ window.parent.postMessage(msg, "*")
+ break;
+ case "updateWith PaymentRequest":
+ if (shippingChangedEvent) {
+ try {
+ shippingChangedEvent.updateWith(updatedDetails);
+ } catch(err) {
+ if (err.name !== "InvalidStateError") {
+ msg = err.name;
+ }
+ }
+ window.parent.postMessage(msg, "*");
+ shippingChangedEvent = undefined;
+ }
+ break;
+ default:
+ window.parent.postMessage(`fail - unknown postmessage action: ${action}`, "*");
+ }
+ };
+}
+</script>
diff --git a/dom/payments/test/test_abortPayment.html b/dom/payments/test/test_abortPayment.html
new file mode 100644
index 0000000000..64285914aa
--- /dev/null
+++ b/dom/payments/test/test_abortPayment.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1345367
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1345367</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('GeneralChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ const defaultMethods = [{
+ supportedMethods: "basic-card",
+ }];
+ const defaultDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ };
+
+ function testBeforeShow() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ payRequest.abort().then((result) => {
+ ok(false, "Should throw 'InvalidStateError', but got resolved.");
+ resolve();
+ }).catch((err) => {
+ is(err.name, "InvalidStateError",
+ "Expected 'InvalidStateError', but got '" + err.name + "'");
+ resolve();
+ });
+ });
+ }
+
+ function testAfterShow() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ const acceptPromise = payRequest.show();
+ payRequest.abort().then((abortResult) => {
+ is(abortResult, undefined, "Should be resolved with undefined.");
+ resolve();
+ }).catch( (err) => {
+ ok(false, "Expected no error, but got '" + err.name + "'.");
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ function runTests() {
+ testBeforeShow()
+ .then(testAfterShow)
+ .then(teardown)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345367">Mozilla Bug 1345367</a>
+</pre>
+</body>
+</html>
diff --git a/dom/payments/test/test_basiccard.html b/dom/payments/test/test_basiccard.html
new file mode 100644
index 0000000000..e8d30fbd06
--- /dev/null
+++ b/dom/payments/test/test_basiccard.html
@@ -0,0 +1,371 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1375345
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1375345</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('BasiccardChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ async function requestChromeAction(action, params) {
+ await new Promise(resolve => {
+ gScript.addMessageListener(`${action}-complete`, function completeListener() {
+ gScript.removeMessageListener(`${action}-complete`, completeListener);
+ resolve();
+ });
+ gScript.sendAsyncMessage(action, params);
+ });
+ }
+
+ const errorNetworksMethods = [{
+ supportedMethods: "basic-card",
+ data: {
+ supportedNetworks: ["myNetwork"],
+ },
+ }];
+
+ const nullDataMethods = [{
+ supportedMethods: "basic-card",
+ }];
+
+ const emptyDataMethods = [{
+ supportedMethods: "basic-card",
+ data: {},
+ }];
+
+ const unconvertableDataMethods = [{
+ supportedMethods: "basic-card",
+ data: "unconvertable data",
+ }];
+
+ const defaultMethods = [{
+ supportedMethods: "basic-card",
+ data: {
+ supportedNetworks: ["unionpay", "visa", "mastercard", "amex", "discover",
+ "diners", "jcb", "mir",
+ ],
+ },
+ }];
+ const defaultDetails = {
+ id: "test payment",
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: true,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: false,
+ },
+ ],
+ };
+
+ const updateDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: true,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: false,
+ },
+ ],
+ error: "",
+ };
+
+ const defaultOptions = {
+ requestPayerName: true,
+ requestPayerEmail: false,
+ requestPayerPhone: false,
+ requestShipping: true,
+ shippingType: "shipping"
+ };
+
+ async function testBasicCardRequestWithErrorNetworks() {
+ const testName = "testBasicCardRequestWithErrorNetworks";
+ try {
+ const request = new PaymentRequest(errorNetworksMethods, defaultDetails, defaultOptions);
+ ok(false, `${testName}: Expected 'TypeError', but got success construction.`);
+ } catch (e) {
+ is(e.name, "TypeError", `${testName}: Expected TypeError, but got ${e.name}`);
+ }
+ }
+
+ async function testBasicCardRequestWithUnconvertableData() {
+ const testName = "testBasicCardRequestWithUnconvertableData";
+ try {
+ const request = new PaymentRequest(unconvertableDataMethods, defaultDetails, defaultOptions);
+ ok(false, `${testName}: Expected 'TypeError', but got success construction.`);
+ } catch (e) {
+ is(e.name, "TypeError", `${testName}: Expected TypeError, but got ${e.name}`);
+ }
+ }
+
+ async function testBasicCardRequestWithNullData() {
+ const testName = "testBasicCardRequestWithNullData";
+ try {
+ const request = new PaymentRequest(nullDataMethods, defaultDetails, defaultOptions);
+ ok(request, `${testName}: PaymentRequest should be constructed with null data BasicCardRequest.`);
+ } catch (e) {
+ ok(false, `${testName}: Unexpected error: ${e.name}`);
+ }
+ }
+
+ async function testBasicCardRequestWithEmptyData() {
+ const testName = "testBasicCardRequestWithEmptyData";
+ try {
+ const request = new PaymentRequest(emptyDataMethods, defaultDetails, defaultOptions);
+ ok(request, `${testName}: PaymentRequest should be constructed with empty data BasicCardRequest.`);
+ } catch (e) {
+ ok(false, `${testName}: Unexpected error: ${e.name}`);
+ }
+ }
+
+ async function testCanMakePaymentWithBasicCardRequest() {
+ const testName = "testCanMakePaymentWithBasicCardRequest";
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ try {
+ const result = await request.canMakePayment();
+ ok(result, `${testName}: canMakePayment() should be resolved with true.`);
+ } catch (e) {
+ ok(false, `${testName}: Unexpected error: ${e.name}`);
+ }
+ }
+
+ async function testBasicCardSimpleResponse() {
+ const testName = "testBasicCardSimpleResponse";
+ await requestChromeAction("set-simple-ui-service", testName);
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ const response = await request.show();
+ ok(response.details, `${testName}: basiccard response should exists.`);
+ ok(!response.details.cardholderName, `${testName}: response.details.cardholderName should not exist.`);
+ is(response.details.cardNumber, "4916855166538720",
+ `${testName}: response.details.cardNumber should be '4916855166538720'.`);
+ ok(!response.details.expiryMonth, `${testName}: response.details.expiryMonth should not exist.`);
+ ok(!response.details.expiryYear, `${testName}: response.details.expiryYear should be '2024'.`);
+ ok(!response.details.cardSecurityCode, `${testName}: response.details.cardSecurityCode should not exist.`);
+ ok(!response.details.billingAddress, `${testName}: response.details.billingAddress should not exist.`);
+ await response.complete("success");
+ } catch (e) {
+ ok(false, `${testName}: Unexpected error: ${e.name}`);
+ }
+ await handler.destruct();
+ }
+
+ async function testBasicCardDetailedResponse() {
+ const testName = "testBasicCardDetailedResponse";
+ await requestChromeAction("set-detailed-ui-service", testName);
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ const response = await request.show();
+ ok(response.details, `${testName}: basiccard response should exists.`);
+ ok(response.details.cardholderName, `${testName}: response.details.cardholderName should not exist.`);
+ is(response.details.cardNumber, "4916855166538720",
+ `${testName}: response.details.cardNumber should be '4916855166538720'.`);
+ ok(response.details.expiryMonth, `${testName}: response.details.expiryMonth should not exist.`);
+ ok(response.details.expiryYear, `${testName}: response.details.expiryYear should be '2024'.`);
+ ok(response.details.cardSecurityCode, `${testName}: response.details.cardSecurityCode should not exist.`);
+ ok(response.details.billingAddress, `${testName}: response.details.billingAddress should not exist.`);
+ const billingAddress = response.details.billingAddress;
+ is(billingAddress.country, "USA", `${testName}: country should be 'USA'.`);
+ is(billingAddress.addressLine.length, 1, `${testName}: addressLine.length should be 1.`);
+ is(billingAddress.addressLine[0], "Easton Ave", `${testName}: addressLine[0] should be 'Easton Ave'.`);
+ is(billingAddress.region, "CA", `${testName}: region should be 'CA'.`);
+ is(billingAddress.regionCode, "CA", `${testName}: regionCode should be 'CA'.`);
+ is(billingAddress.city, "San Bruno", `${testName}: city should be 'San Bruno'.`);
+ is(billingAddress.dependentLocality, "", `${testName}: dependentLocality should be empty.`);
+ is(billingAddress.postalCode, "94066", `${testName}: postalCode should be '94066'.`);
+ is(billingAddress.sortingCode, "123456", `${testName}: sortingCode should be '123456'.`);
+ is(billingAddress.organization, "", `${testName}: organization should be empty.`);
+ is(billingAddress.recipient, "Bill A. Pacheco", `${testName}: recipient should be 'Bill A. Pacheco'.`);
+ is(billingAddress.phone, "+14344413879", `${testName}: phone should be '+14344413879'.`);
+ await response.complete("success");
+ } catch (e) {
+ ok(false, `${testName}: Unexpected error: ${e.name}`);
+ }
+ await handler.destruct();
+ }
+
+ async function testSpecialAddressResponse() {
+ const testName = "testSpecialAddressResponse";
+ await requestChromeAction("set-special-address-ui-service", testName);
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ const response = await request.show();
+ ok(response.details, `${testName}: BasiccardResponse should exist.`);
+ ok(response.details.billingAddress,
+ `${testName}: BasiccardResponse.billingAddress should exist.`);
+ is(response.details.billingAddress.addressLine[0], ":$%@&*",
+ `${testName}: AddressLine should be ':$%@&*'`);
+ await response.complete("success");
+ } catch (e) {
+ ok(false, `${testName}: Unexpected error: ${e.name}`);
+ }
+ await handler.destruct();
+ }
+
+ async function testMethodChangeWithoutRequestBillingAddress() {
+ const testName = "testMethodChangeWithoutRequestBillingAddress";
+ await requestChromeAction("method-change-to-basic-card", testName);
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ request.addEventListener("paymentmethodchange", async (event) => {
+ is(event.methodName, "basic-card", `${testName}: PaymentMethodChangeEvent.methodName should be 'basic-card'.`)
+ ok(event.methodDetails, `PaymentMethodChangeEvent.methodDetails should exist.`);
+ ok(!event.methodDetails.billingAddress, `PaymentMethodChangeEvent.methodDetails.billingAddres should not exist.`);
+ event.updateWith(updateDetails);
+ });
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ const response = await request.show();
+ await response.complete("success");
+ } catch (error) {
+ ok(false, `${testName}: Unexpected error: ${error.name}`);
+ }
+ await handler.destruct();
+ }
+
+ async function testMethodChangeWithRequestBillingAddress() {
+ const testName = "testMethodChangeWithRequestBillingAddress";
+ await requestChromeAction("method-change-to-basic-card", testName);
+ const options = {
+ requestPayerName: true,
+ requestBillingAddress: true,
+ requestShipping: true,
+ shippingType: "shipping",
+ };
+ const request = new PaymentRequest(defaultMethods, defaultDetails, options);
+ request.addEventListener("paymentmethodchange", async (event) => {
+ is(event.methodName, "basic-card", `${testName}: PaymentMethodChangeEvent.methodName should be 'basic-card'.`)
+ ok(event.methodDetails, `PaymentMethodChangeEvent.methodDetails should exist.`);
+ const billingAddress = event.methodDetails.billingAddress;
+ is(billingAddress.country, "USA", `${testName}: country should be 'USA'.`);
+ is(billingAddress.addressLine.length, 1, `${testName}: addressLine.length should be 1.`);
+ is(billingAddress.addressLine[0], "Easton Ave", `${testName}: addressLine[0] should be 'Easton Ave'.`);
+ is(billingAddress.region, "CA", `${testName}: region should be 'CA'.`);
+ is(billingAddress.regionCode, "CA", `${testName}: regionCode should be 'CA'.`);
+ is(billingAddress.city, "San Bruno", `${testName}: city should be 'San Bruno'.`);
+ is(billingAddress.dependentLocality, "", `${testName}: dependentLocality should be empty.`);
+ is(billingAddress.postalCode, "94066", `${testName}: postalCode should be '94066'.`);
+ is(billingAddress.sortingCode, "123456", `${testName}: sortingCode should be '123456'.`);
+ is(billingAddress.organization, "", `${testName}: organization should be empty.`);
+ is(billingAddress.recipient, "Bill A. Pacheco", `${testName}: recipient should be 'Bill A. Pacheco'.`);
+ is(billingAddress.phone, "+14344413879", `${testName}: phone should be '+14344413879'.`);
+ event.updateWith(updateDetails);
+ });
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ const response = await request.show();
+ await response.complete("success");
+ } catch (error) {
+ ok(false, `${testName}: Unexpected error: ${error.name}`);
+ }
+ await handler.destruct();
+ }
+
+
+ async function testBasicCardErrorResponse() {
+ const testName = "testBasicCardErrorResponse";
+ return requestChromeAction("error-response-test", testName);
+ }
+
+ async function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler)
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ async function runTests() {
+ try {
+ await testBasicCardRequestWithErrorNetworks();
+ await testBasicCardRequestWithUnconvertableData();
+ await testBasicCardRequestWithNullData();
+ await testBasicCardRequestWithEmptyData();
+ await testCanMakePaymentWithBasicCardRequest();
+ await testBasicCardSimpleResponse();
+ await testBasicCardDetailedResponse();
+ await testSpecialAddressResponse();
+ await testBasicCardErrorResponse();
+ await testMethodChangeWithoutRequestBillingAddress();
+ await testMethodChangeWithRequestBillingAddress()
+ await teardown();
+ } catch (e) {
+ ok(false, `test_basiccard.html: Unexpected error: ${e.name}`);
+ SimpleTest.finish();
+ };
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1375345">Mozilla Bug 1375345</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_basiccarderrors.html b/dom/payments/test/test_basiccarderrors.html
new file mode 100644
index 0000000000..f9ac76ae75
--- /dev/null
+++ b/dom/payments/test/test_basiccarderrors.html
@@ -0,0 +1,85 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1489968
+-->
+<meta charset="utf-8">
+<title>Test for Bug 1489968</title>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="./DefaultData.js"></script>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+const gUrl = SimpleTest.getTestFileURL("BasicCardErrorsChromeScript.js");
+const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+function sendOnce(message) {
+ return data => {
+ return new Promise(resolve => {
+ const doneMsg = `${message}-complete`;
+ gScript.addMessageListener(doneMsg, function listener() {
+ gScript.removeMessageListener(doneMsg, listener);
+ resolve();
+ });
+ gScript.sendAsyncMessage(message, data);
+ });
+ };
+}
+const sendTearDown = sendOnce("teardown");
+
+async function teardown() {
+ await sendTearDown();
+ gScript.destroy();
+ SimpleTest.finish();
+}
+
+async function testBasicCardErrors() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(
+ true
+ );
+ const request = new PaymentRequest(
+ [{ supportedMethods: "basic-card" }],
+ defaultDetails
+ );
+ const response = await request.show();
+ // Smoke test the initial state
+ is(response.details.cardNumber, "4111111111111111", "Expected cardNumber to initially be 4111111111111111");
+ // We send these up and have the chrome script echo them back to us.
+ const expected = {
+ cardholderName: "PASS",
+ cardNumber: "3566002020360505",
+ cardSecurityCode: "666",
+ expiryMonth: "02",
+ expiryYear: "2020",
+ };
+ await response.retry({ paymentMethod: expected });
+ // the values of the response would have been updated with the expected
+ for (const [member, expectedValue] of Object.entries(expected)) {
+ const actual = response.details[member];
+ is(
+ actual,
+ expectedValue,
+ `Expected member ${member} to be "${expectedValue}, but got "${actual}"`
+ );
+ }
+ await response.complete("success");
+ handler.destruct();
+}
+
+async function runTests() {
+ try {
+ await testBasicCardErrors();
+ } catch (err) {
+ ok(false, `Unexpected error: ${err} ${err.stack}.`);
+ } finally {
+ await teardown();
+ }
+}
+
+window.addEventListener("load", () => {
+ const prefs = [["dom.payments.request.enabled", true]];
+ SpecialPowers.pushPrefEnv({ set: prefs }, runTests);
+});
+</script>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1489968">Mozilla Bug 1489968</a>
diff --git a/dom/payments/test/test_block_none10s.html b/dom/payments/test/test_block_none10s.html
new file mode 100644
index 0000000000..b1d654f38c
--- /dev/null
+++ b/dom/payments/test/test_block_none10s.html
@@ -0,0 +1,58 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Test for Bug 1408250</title>
+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
+ <meta content="utf-8" http-equiv="encoding">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script type="text/javascript">
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ function testInNone10s() {
+ return new Promise((resolve,reject) => {
+ const supportedInstruments = [{
+ supportedMethods: "basic-card",
+ }];
+ const details = {
+ id: "simple details",
+ total: {
+ label: "Donation",
+ amount: { currency: "USD", value: "55.00" }
+ },
+ };
+ try {
+ const payRequest = new PaymentRequest(supportedInstruments, details);
+ ok(false, "Unexpected, new PaymentRequest() can not be used in non-e10s.");
+ } catch (err) {
+ ok(err.name, "ReferenceError",
+ "Expected ReferenceError when calling new PaymentRequest()");
+ }
+ resolve();
+
+ });
+ }
+
+ function runTests() {
+ testInNone10s()
+ .then(SimpleTest.finish)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+ </script>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1408250">Mozilla Bug 1408250</a>
+ </body>
+</html>
diff --git a/dom/payments/test/test_bug1478740.html b/dom/payments/test/test_bug1478740.html
new file mode 100644
index 0000000000..e877face76
--- /dev/null
+++ b/dom/payments/test/test_bug1478740.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1478740
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for retry PaymentRequest</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="DefaultData.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ const gUrl = SimpleTest.getTestFileURL('Bug1478740ChromeScript.js');
+ const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ async function requestChromeAction(action, params) {
+ gScript.sendAsyncMessage(action, params);
+ await new Promise(resolve => {
+ gScript.addMessageListener(`${action}-complete`, function completeListener() {
+ gScript.removeMessageListener(`${action}-complete`, completeListener);
+ resolve();
+ });
+ });
+ }
+ function unexpectedErrMsg(testName, errName, timing) {
+ return `${testName}: Unexpected error(${errName}) when ${timing} the PaymentRequest.`;
+ }
+
+ async function testMultipleShows() {
+ const testName = "testMultipleShows";
+ await requestChromeAction("start-test", testName);
+ let expectedResults = ["successful",
+ "successful",
+ "successful",
+ "AbortError",
+ "AbortError",
+ "AbortError"];
+ let nextStatus = ["creating first page",
+ "creating second page",
+ "showing first payment",
+ "showing second payment",
+ "showing third payment",
+ "aborting first payment"];
+ let currStatus = nextStatus.shift();
+ let ifr1 = document.createElement('iframe');
+ let ifr2 = document.createElement('iframe');
+
+ await new Promise(resolve => {
+ let listener = async function(event) {
+ let expected = expectedResults.shift();
+ is(event.data, expected,
+ `${testName}: Expected '${expected}' when ${currStatus}, but got '${event.data}'`);
+ switch (currStatus) {
+ case "creating first page":
+ ifr2.src = "bug1478740.html";
+ document.body.appendChild(ifr2);
+ break;
+ case "creating second page":
+ ifr1.contentWindow.postMessage("Show Payment", "*");
+ break;
+ case "showing first payment":
+ ifr2.contentWindow.postMessage("Show Payment", "*");
+ break;
+ case "showing second payment":
+ ifr2.contentWindow.postMessage("Show Payment", "*");
+ break;
+ case "showing third payment":
+ await requestChromeAction("reject-payment");
+ break;
+ case "aborting first payment":
+ window.removeEventListener("message", listener);
+ gScript.removeMessageListener("showing-payment", listener);
+ document.body.removeChild(ifr1);
+ document.body.removeChild(ifr2);
+ resolve();
+ break;
+ default:
+ ok(false, `unknown status ${currStatus}`);
+ }
+ currStatus = nextStatus.shift();
+ }
+ window.addEventListener("message", listener);
+ gScript.addMessageListener("showing-payment", listener);
+ ifr1.src = "bug1478740.html";
+ document.body.appendChild(ifr1);
+ });
+ await requestChromeAction("finish-test");
+ }
+
+ function teardown() {
+ return new Promise((resolve, reject) => {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ resolve();
+ });
+ gScript.sendAsyncMessage("teardown");
+ });
+ }
+
+ async function runTests() {
+ try {
+ await testMultipleShows();
+ await teardown();
+ } catch(e) {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ['dom.payments.request.user_interaction_required', false],
+ ]
+ }, runTests);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1478740">Mozilla Bug 1478740</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_bug1490698.html b/dom/payments/test/test_bug1490698.html
new file mode 100644
index 0000000000..e1126af770
--- /dev/null
+++ b/dom/payments/test/test_bug1490698.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1490698
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for retry PaymentRequest</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="DefaultData.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ const gUrl = SimpleTest.getTestFileURL('Bug1490698ChromeScript.js');
+ const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ async function requestChromeAction(action, params) {
+ gScript.sendAsyncMessage(action, params);
+ await new Promise(resolve => {
+ gScript.addMessageListener(`${action}-complete`, function completeListener() {
+ gScript.removeMessageListener(`${action}-complete`, completeListener);
+ resolve();
+ });
+ });
+ }
+ function unexpectedErrMsg(testName, errName, timing) {
+ return `${testName}: Unexpected error(${errName}) when ${timing} the PaymentRequest.`;
+ }
+
+ async function testInteractWithPaymentUnderWrongState() {
+ const testName = "testInteractWithPaymentUnderWrongState";
+ await requestChromeAction("start-test", testName);
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ ok(payRequest, testName + ": failed to create PaymentRequest.");
+ if (!payRequest) {
+ await requestChromeAction("finish-test");
+ return;
+ }
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let payResponse;
+ try {
+ payResponse = await payRequest.show();
+ info(`${testName}: Interact with payment when PaymentRequest is eClosed`);
+ await requestChromeAction("interact-with-payment");
+ handler.destruct();
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+ handler.destruct();
+ await requestChromeAction("finish-test");
+ return;
+ }
+ try {
+ await payResponse.complete("success");
+ ok(true, `${testName}: complete() is successful after PaymentRequest's state is eClosed.`);
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "completing"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ try {
+ info(`${testName}: Interact with payment when PaymentRequest is completed`);
+ await requestChromeAction("interact-with-payment");
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "completing"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ await requestChromeAction("finish-test");
+ }
+
+ function teardown() {
+ return new Promise((resolve, reject) => {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ resolve();
+ });
+ gScript.sendAsyncMessage("teardown");
+ });
+ }
+
+ async function runTests() {
+ try {
+ await testInteractWithPaymentUnderWrongState();
+ await teardown();
+ } catch(e) {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1490698">Mozilla Bug 1490698</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_canMakePayment.html b/dom/payments/test/test_canMakePayment.html
new file mode 100644
index 0000000000..112fb8ce72
--- /dev/null
+++ b/dom/payments/test/test_canMakePayment.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1345365
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for PaymentRequest API canMakePayment()</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('GeneralChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ const defaultMethods = [{
+ supportedMethods: "basic-card",
+ }];
+ const defaultDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ };
+
+ const nonsupportedMethods = [{
+ supportedMethods: "testing-payment-method",
+ }];
+
+ function testDefaultAction() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(nonsupportedMethods, defaultDetails);
+ payRequest.canMakePayment().then((result) => {
+ ok(!result, "Should be resolved with false, but got " + result + ".");
+ resolve();
+ }).catch((err) => {
+ ok(false, "Expected no error, but got '" + err.name +"'.");
+ resolve();
+ });
+ });
+ }
+
+ function testSimple() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ payRequest.canMakePayment().then((result) => {
+ ok(result, "Should be resolved with true, but got " + result + ".");
+ resolve();
+ }).catch((err) => {
+ ok(false, "Expected no error, but got '" + err.name +"'.");
+ resolve();
+ });
+ });
+ }
+
+ function testAfterShow() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ const acceptPromise = payRequest.show();
+ payRequest.canMakePayment().then((result) => {
+ ok(false, "Should throw 'InvalidStateError', but got resolved.");
+ resolve();
+ }).catch( (err) => {
+ is(err.name, "InvalidStateError",
+ "Expected 'InvalidStateError', but got '" + err.name + "'.");
+ payRequest.abort().then((abortResult) => {
+ is(abortResult, undefined, "abort() should be resolved with undefined.");
+ resolve();
+ }).catch( (error) => {
+ ok(false, "Expected no error, but got '" + error.name + "'.");
+ resolve();
+ }).finally(handler.destruct);
+ });
+ });
+ }
+
+ function testAfterAbort() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ const acceptPromise = payRequest.show();
+ payRequest.abort().then((abortResult) => {
+ payRequest.canMakePayment().then((result) => {
+ ok(false, "Should throw 'InvalidStateError', but got resolved.");
+ resolve();
+ }).catch( (err) => {
+ is(err.name, "InvalidStateError",
+ "Expected 'InvalidStateError', but got '" + err.name + "'.");
+ resolve();
+ });
+ }).catch( (err) => {
+ ok(false, "Expected no error, but got '" + err.name +"'.");
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ async function testNotAllowed() {
+ let payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ for (let i = 0; i < 1000; i++) {
+ try {
+ await payRequest.canMakePayment();
+ } catch(err) {
+ is(err.name, "NotAllowedError",
+ "Expected 'NotAllowError', but got '" + err.name + "'");
+ break;
+ }
+ }
+ for (let i = 0; i < 1000; i++) {
+ try {
+ await new PaymentRequest(defaultMethods, defaultDetails).canMakePayment();
+ } catch(err) {
+ is(err.name, "NotAllowedError",
+ "Expected 'NotAllowError', but got '" + err.name + "'");
+ break;
+ }
+ }
+ }
+
+ function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ function runTests() {
+ testDefaultAction()
+ .then(testSimple)
+ .then(testAfterShow)
+ .then(testAfterAbort)
+ .then(testNotAllowed)
+ .then(teardown)
+ .catch(e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345365">Mozilla Bug 1345365</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_closePayment.html b/dom/payments/test/test_closePayment.html
new file mode 100644
index 0000000000..8f2ad7cd00
--- /dev/null
+++ b/dom/payments/test/test_closePayment.html
@@ -0,0 +1,284 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1408234
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for closing PaymentRequest</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="DefaultData.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('ClosePaymentChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ async function requestChromeAction(action, params) {
+ gScript.sendAsyncMessage(action, params);
+ await new Promise(resolve => {
+ gScript.addMessageListener(`${action}-complete`, function completeListener() {
+ gScript.removeMessageListener(`${action}-complete`, completeListener);
+ resolve();
+ });
+ });
+ }
+
+ async function testCloseByReloading() {
+ const testName = "testCloseByReloading";
+ await requestChromeAction("test-setup", testName);
+ info(testName);
+ let nextStatus = ["creating", "reloading"];
+ let currStatus = nextStatus.shift();
+ let ifr = document.createElement('iframe');
+ await requestChromeAction("payment-num-set");
+
+ await new Promise((resolve) => {
+ let listener = function(event) {
+ is(event.data, "successful",
+ `${testName}: Expected 'successful' when ${currStatus}, but got '${event.data}'.`);
+ if (currStatus === "creating") {
+ ifr.contentWindow.location.reload();
+ } else if (currStatus === "reloading") {
+ window.removeEventListener("message", listener);
+ resolve();
+ }
+ currStatus = nextStatus.shift();
+ }
+ window.addEventListener("message", listener);
+ ifr.src = "simple_payment_request.html";
+ document.body.appendChild(ifr);
+ });
+
+ await requestChromeAction("payment-num-check", 1);
+ document.body.removeChild(ifr);
+
+ }
+
+ async function testCloseByRedirecting() {
+ const testName = "testCloseByRedirecting";
+ await requestChromeAction("test-setup", testName);
+ return new Promise((resolve) => {
+ let nextStatus = ["creating", "redirecting"];
+ let currStatus = nextStatus.shift();
+ let ifr = document.createElement('iframe');
+ let listener = async function(event) {
+ is(event.data, "successful",
+ `${testName}: Expected 'successful' when ${currStatus}, but got '${event.data}'.`);
+ if (currStatus === "creating") {
+ ifr.src = "blank_page.html";
+ } else if (currStatus === "redirecting"){
+ window.removeEventListener("message", listener);
+ await requestChromeAction("close-check");
+ document.body.removeChild(ifr);
+ resolve();
+ }
+ currStatus = nextStatus.shift();
+ };
+ window.addEventListener("message", listener);
+ ifr.src = "simple_payment_request.html";
+ document.body.appendChild(ifr);
+ });
+ }
+
+ async function testCloseByRedirectingAfterShow() {
+ const testName = "testCloseByRedirectingAfterShow";
+ await requestChromeAction("test-setup", testName);
+ return new Promise((resolve) => {
+ let nextStatus = ["creating", "showing", "redirecting"];
+ let currStatus = nextStatus.shift();
+ let ifr = document.createElement('iframe');
+ let handler = undefined;
+ let listener = async (event) => {
+ is(event.data, "successful",
+ `${testName}: Expected 'successful' when ${currStatus}, but got '${event.data}'.`);
+ if (currStatus === "creating") {
+ handler = SpecialPowers.getDOMWindowUtils(ifr.contentWindow).setHandlingUserInput(true);
+ ifr.contentWindow.postMessage("show PaymentRequest", "*");
+ } else if (currStatus === "showing") {
+ handler.destruct();
+ ifr.src = "blank_page.html";
+ } else if (currStatus === "redirecting") {
+ window.removeEventListener("message", listener);
+ await requestChromeAction("close-check");
+ await requestChromeAction("reject-payment", true);
+ document.body.removeChild(ifr);
+ resolve();
+ }
+ currStatus = nextStatus.shift();
+ }
+ window.addEventListener("message", listener);
+ ifr.src = "simple_payment_request.html";
+ document.body.appendChild(ifr);
+ });
+ }
+
+ async function testCloseByRemovingIframe() {
+ const testName = "testCloseByRemovingIframe";
+ await requestChromeAction("test-setup", testName);
+ return new Promise((resolve) => {
+ let nextStatus = ["creating"];
+ let currStatus = nextStatus.shift();
+ let ifr = document.createElement('iframe');
+ let listener = async function(event) {
+ is(event.data, "successful",
+ `${testName}: Expected 'successful' when ${currStatus}, but got '${event.data}'.`);
+ document.body.removeChild(ifr);
+ window.removeEventListener("message", listener);
+ await requestChromeAction("close-check");
+ resolve();
+ };
+ window.addEventListener("message", listener);
+ ifr.src = "simple_payment_request.html";
+ document.body.appendChild(ifr);
+ });
+ }
+
+ async function testUpdateWithRespondedPayment() {
+ const testName = "testUpdateWithRespondedPayment";
+ await requestChromeAction("test-setup", testName);
+ return new Promise(resolve => {
+ let nextStatus = ["creating", "showing", "closing", "updating", "finishing"];
+ let currStatus = nextStatus.shift();
+ let ifr = document.createElement('iframe');
+ let handler = undefined;
+ let listener = async function(event) {
+ is(event.data, "successful",
+ `${testName}: Expected 'successful' when ${currStatus}, but got '${event.data}'.`);
+ switch (currStatus) {
+ case "creating":
+ handler = SpecialPowers.getDOMWindowUtils(ifr.contentWindow).setHandlingUserInput(true);
+ ifr.contentWindow.postMessage("show PaymentRequest", "*");
+ break;
+ case "showing":
+ await requestChromeAction("update-payment");
+ break;
+ case "closing":
+ await requestChromeAction("reject-payment", false);
+ break;
+ case "updating":
+ await requestChromeAction("close-check");
+ ifr.contentWindow.postMessage("updateWith PaymentRequest", "*");
+ break;
+ case "finishing":
+ handler.destruct();
+ document.body.removeChild(ifr);
+ window.removeEventListener("message", listener);
+ resolve();
+ break;
+ default:
+ ok(false, testName + ": Unknown status()" + currStatus);
+ break;
+ }
+ currStatus = nextStatus.shift();
+ }
+ window.addEventListener("message", listener);
+ ifr.src = "simple_payment_request.html";
+ document.body.appendChild(ifr);
+ });
+ }
+
+ function getLoadedPaymentRequest(iframe, url) {
+ return new Promise(resolve => {
+ iframe.addEventListener(
+ "load",
+ () => {
+ const { PaymentRequest } = iframe.contentWindow;
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ resolve(request);
+ },
+ { once: true }
+ );
+ iframe.src = url;
+ });
+ }
+
+ async function testNonfullyActivePayment() {
+ const testName = "testNonfullyActivePayment";
+ await requestChromeAction("test-setup", testName);
+
+ const outer = document.createElement("iframe");
+ outer.allow = "payment";
+ document.body.appendChild(outer);
+ await getLoadedPaymentRequest(outer,"blank_page.html");
+
+ const inner = outer.contentDocument.createElement("iframe");
+ inner.allow = "payment";
+ outer.contentDocument.body.appendChild(inner);
+
+ const request = await getLoadedPaymentRequest(inner,"blank_page.html");
+ ok(request, `${testName}: PaymentRequest in inner iframe should exist.`);
+
+ await new Promise(res => {
+ outer.addEventListener("load", res);
+ outer.src = "simple_payment_request.html";
+ });
+
+ let handler = SpecialPowers.getDOMWindowUtils(inner.contentWindow).setHandlingUserInput(true);
+ try {
+ const showPromise = await request.show();
+ ok(false, `${testName}: expected 'AbortError', but got resolved.`);
+ } catch (error) {
+ is(error.name, "AbortError",
+ `${testName}: expected 'AbortError'.`);
+ }
+ await handler.destruct();
+ inner.remove();
+ outer.remove();
+ }
+
+ async function teardown() {
+ return new Promise((resolve) => {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ resolve();
+ });
+ gScript.sendAsyncMessage("teardown");
+ });
+ }
+
+ async function runTests() {
+ try {
+ await testCloseByReloading();
+ await testCloseByRedirecting();
+ await testCloseByRedirectingAfterShow();
+ await testCloseByRemovingIframe();
+ await testUpdateWithRespondedPayment();
+ await testNonfullyActivePayment();
+ await teardown();
+ } catch(e) {
+ ok(false, "test_closePayment.html: Unexpected error: " + e.name);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1408234">Mozilla Bug 1408234</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1483470">Mozilla Bug 1483470</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_constructor.html b/dom/payments/test/test_constructor.html
new file mode 100644
index 0000000000..4517a37028
--- /dev/null
+++ b/dom/payments/test/test_constructor.html
@@ -0,0 +1,351 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1345361
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1345361</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('ConstructorChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+
+ const simplestMethods = [{
+ supportedMethods: "basic-card",
+ }];
+ const simplestDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ }
+ };
+
+ const complexMethods = [{
+ supportedMethods: "basic-card",
+ data: {
+ supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
+ 'diners', 'jcb', 'mir',
+ ],
+ },
+ }];
+
+ const nonBasicCardMethods = [{
+ supportedMethods: "testing-payment-method",
+ data: {
+ paymentId: "P3892940",
+ paymentType: "prepaid",
+ },
+ }];
+
+ const complexDetails = {
+ id: "payment details",
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "100.00"
+ }
+ },
+ displayItems: [
+ {
+ label: "First item",
+ amount: {
+ currency: "USD",
+ value: "60.00"
+ }
+ },
+ {
+ label: "Second item",
+ amount: {
+ currency: "USD",
+ value: "40.00"
+ }
+ }
+ ],
+ modifiers: [
+ {
+ supportedMethods: "basic-card",
+ total: {
+ label: "Discounted Total",
+ amount: {
+ currency: "USD",
+ value: "90.00"
+ }
+ },
+ additionalDisplayItems: [
+ {
+ label: "basic-card discount",
+ amount: {
+ currency: "USD",
+ value: "-10.00"
+ }
+ }
+ ],
+ data: { discountProgramParticipantId: "86328764873265", }
+ },
+ ],
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: true,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: false,
+ },
+ ],
+ };
+
+ const complexOptions = {
+ requestPayerName: true,
+ requestPayerEmail: true,
+ requestPayerPhone: true,
+ requestShipping: true,
+ shippingType: "shipping"
+ };
+
+ const duplicateShippingOptionsDetails = {
+ id: "duplicate shipping options details",
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ shippingOptions: [
+ {
+ id: "dupShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: true,
+ },
+ {
+ id: "dupShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: false,
+ },
+ ],
+ };
+
+
+ function testWithSimplestParameters() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(simplestMethods, simplestDetails);
+ ok(payRequest, "PaymentRequest should be created");
+ gScript.addMessageListener("check-complete", function checkCompleteHandler() {
+ gScript.removeMessageListener("check-complete", checkCompleteHandler);
+ resolve();
+ });
+ gScript.sendAsyncMessage("check-simplest-request");
+ });
+ }
+
+ function testWithComplexParameters() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(complexMethods, complexDetails, complexOptions);
+ ok(payRequest, "PaymentRequest should be created");
+ gScript.addMessageListener("check-complete", function checkCompleteHandler() {
+ gScript.removeMessageListener("check-complete", checkCompleteHandler);
+ resolve();
+ });
+ gScript.sendAsyncMessage("check-complex-request");
+ });
+ }
+
+ function testWithNonBasicCardMethods() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(nonBasicCardMethods, simplestDetails);
+ ok(payRequest, "PaymentRequest should be created");
+ gScript.addMessageListener("check-complete", function checkCompleteHandler() {
+ gScript.removeMessageListener("check-complete", checkCompleteHandler);
+ resolve();
+ });
+ gScript.sendAsyncMessage("check-nonbasiccard-request");
+ });
+ }
+
+ function testWithDuplicateShippingOptionsParameters() {
+ return new Promise((resolve, reject) => {
+ try {
+ const payRequest = new PaymentRequest(simplestMethods,
+ duplicateShippingOptionsDetails,
+ {requestShipping: true});
+ ok(false, "Construction should fail with duplicate shippingOption Ids.");
+ resolve();
+ } catch (e) {
+ is(e.name, "TypeError", "Expected 'TypeError' with duplicate shippingOption Ids.");
+ resolve();
+ }
+ });
+ }
+
+ function testShippingOptionAttribute() {
+ return new Promise((resolve, reject) => {
+ const details = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ shippingOptions: [
+ {
+ id: "option1",
+ label: "option1",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ selected: false,
+ },
+ {
+ id: "option2",
+ label: "option2",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ selected: false,
+ },
+ ],
+ };
+ const payRequest1 = new PaymentRequest(simplestMethods,
+ details,
+ {requestShipping: false});
+ ok(payRequest1, "PaymentRequest should be created");
+ is(payRequest1.shippingOption, null,
+ "request.shippingOption should be null in default, when options.requestShipping is false");
+ details.shippingOptions[0].selected = true;
+ const payRequest2 = new PaymentRequest(simplestMethods,
+ details,
+ {requestShipping: false});
+ ok(payRequest2, "PaymentRequest should be created");
+ is(payRequest2.shippingOption, null,
+ "request.shippingOption should be null in default, when options.requestShipping is false");
+ const payRequest3 = new PaymentRequest(simplestMethods,
+ details,
+ {requestShipping: true});
+ ok(payRequest3, "PaymentRequest should be created");
+ ok(payRequest3.shippingOption,
+ "request.shippingOption should not be null when both shoppingOtpion.selected and options.requestOptions are true");
+ is(payRequest3.shippingOption, "option1",
+ "request.shippingOption should be 'option1'");
+ details.shippingOptions[1].selected = true;
+ const payRequest4 = new PaymentRequest(simplestMethods,
+ details,
+ {requestShipping: true});
+ ok(payRequest4, "PaymentRequest should be created");
+ ok(payRequest4.shippingOption,
+ "request.shippingOption should not be null when both shoppingOtpion.selected and options.requestOptions are true");
+ is(payRequest4.shippingOption, "option2",
+ "request.shippingOption should be 'option2' which is the last one selected.");
+ resolve();
+ });
+ }
+
+ function testMultipleRequests() {
+ return new Promise((resolve, reject) => {
+ const payRequest1 = new PaymentRequest(complexMethods, complexDetails, complexOptions);
+ const payRequest2 = new PaymentRequest(simplestMethods, simplestDetails);
+ ok(payRequest1, "PaymentRequest with complex parameters should be created");
+ ok(payRequest2, "PaymentRequest with simplest parameters should be created");
+ gScript.addMessageListener("check-complete", function checkCompleteHandler() {
+ gScript.removeMessageListener("check-complete", checkCompleteHandler);
+ resolve();
+ });
+ gScript.sendAsyncMessage("check-multiple-requests");
+ });
+ }
+
+ function testCrossOriginTopLevelPrincipal() {
+ return new Promise((resolve, reject) => {
+ var ifrr = document.createElement('iframe');
+
+ window.addEventListener("message", function(event) {
+ is(event.data, "successful",
+ "Expected 'successful', but got '" + event.data + "'");
+ gScript.addMessageListener("check-complete", function checkCompleteHandler() {
+ gScript.removeMessageListener("check-complete", checkCompleteHandler);
+ resolve();
+ });
+ gScript.sendAsyncMessage("check-cross-origin-top-level-principal");
+ });
+
+ ifrr.setAttribute('allow', 'payment');
+ ifrr.src = "https://test1.example.com:443/tests/dom/payments/test/simple_payment_request.html";
+ document.body.appendChild(ifrr);
+ });
+ }
+
+ function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler)
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ function runTests() {
+ testWithSimplestParameters()
+ .then(testWithComplexParameters)
+ .then(testWithNonBasicCardMethods)
+ .then(testWithDuplicateShippingOptionsParameters)
+ .then(testMultipleRequests)
+ .then(testCrossOriginTopLevelPrincipal)
+ .then(testShippingOptionAttribute)
+ .then(teardown)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345361">Mozilla Bug 1345361</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_currency_amount_validation.html b/dom/payments/test/test_currency_amount_validation.html
new file mode 100644
index 0000000000..bf8284f37a
--- /dev/null
+++ b/dom/payments/test/test_currency_amount_validation.html
@@ -0,0 +1,353 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1367669
+https://bugzilla.mozilla.org/show_bug.cgi?id=1388661
+-->
+<title>Test for PaymentRequest API currency amount validation</title>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+"use strict";
+SimpleTest.waitForExplicitFinish();
+
+const gUrl = SimpleTest.getTestFileURL(
+ "CurrencyAmountValidationChromeScript.js"
+);
+const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+function testFailHandler(message) {
+ ok(false, message);
+}
+gScript.addMessageListener("test-fail", testFailHandler);
+
+const defaultMethods = [
+ {
+ supportedMethods: "basic-card",
+ },
+];
+const defaultDetails = {
+ total: {
+ label: "total",
+ amount: {
+ currency: "usd",
+ value: "1.00",
+ },
+ },
+};
+
+const specialAmountDetails = {
+ total: {
+ label: "total",
+ amount: {
+ currency: "usd",
+ value: {
+ toString() {
+ throw "42";
+ },
+ },
+ },
+ },
+};
+
+const wellFormedCurrencyCodes = [
+ "BOB",
+ "EUR",
+ "usd", // currency codes are case-insensitive
+ "XdR",
+ "xTs",
+];
+
+const invalidCurrencyCodes = [
+ "",
+ "€",
+ "$",
+ "SFr.",
+ "DM",
+ "KR₩",
+ "702",
+ "ßP",
+ "ınr",
+ "invalid",
+ "in",
+ "123",
+];
+
+const updatedInvalidCurrencyDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "Invalid",
+ value: "1.00",
+ },
+ },
+};
+
+const updatedInvalidAmountDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "-1.00",
+ },
+ },
+};
+
+const invalidAmounts = [
+ "-",
+ "notdigits",
+ "ALSONOTDIGITS",
+ "10.",
+ ".99",
+ "-10.",
+ "-.99",
+ "10-",
+ "1-0",
+ "1.0.0",
+ "1/3",
+ "",
+ null,
+ " 1.0 ",
+ " 1.0 ",
+ "1.0 ",
+ "USD$1.0",
+ "$1.0",
+ {
+ toString() {
+ return " 1.0";
+ },
+ },
+ undefined,
+];
+const invalidTotalAmounts = invalidAmounts.concat([
+ "-1",
+ "-1.0",
+ "-1.00",
+ "-1000.000",
+]);
+
+function updateWithInvalidAmount() {
+ return new Promise((resolve, reject) => {
+ resolve(updatedInvalidAmountDetails);
+ });
+}
+
+async function testWithLowerCaseCurrency() {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ return new Promise(resolve => {
+ gScript.addMessageListener(
+ "check-complete",
+ function checkCompleteHandler() {
+ gScript.removeMessageListener("check-complete", checkCompleteHandler);
+ resolve();
+ }
+ );
+ gScript.sendAsyncMessage("check-lower-case-currency");
+ });
+}
+
+function testWithWellFormedCurrencyCodes() {
+ for (const currency of wellFormedCurrencyCodes) {
+ const details = {
+ total: {
+ label: "Well Formed Currency",
+ amount: {
+ currency,
+ value: "1.00",
+ },
+ },
+ };
+ try {
+ const payRequest = new PaymentRequest(defaultMethods, details);
+ } catch (e) {
+ const msg = `Unexpected error while creating payment request with well-formed currency (${currency}) ${
+ e.name
+ }`;
+ ok(false, msg);
+ }
+ }
+}
+
+function testWithInvalidCurrencyCodes() {
+ for (const invalidCurrency of invalidCurrencyCodes) {
+ const invalidDetails = {
+ total: {
+ label: "Invalid Currency",
+ amount: {
+ currency: invalidCurrency,
+ value: "1.00",
+ },
+ },
+ };
+ try {
+ const payRequest = new PaymentRequest(defaultMethods, invalidDetails);
+ ok(
+ false,
+ `Creating a Payment Request with invalid currency (${invalidCurrency}) must throw.`
+ );
+ } catch (e) {
+ is(
+ e.name,
+ "RangeError",
+ `Expected rejected with 'RangeError', but got '${e.name}'.`
+ );
+ }
+ }
+}
+
+async function testUpdateWithInvalidCurrency() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(
+ true
+ );
+ gScript.sendAsyncMessage("set-update-with-invalid-details-ui-service");
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ payRequest.addEventListener("shippingaddresschange", event => {
+ event.updateWith(Promise.resolve(updatedInvalidCurrencyDetails));
+ });
+ payRequest.addEventListener("shippingoptionchange", event => {
+ event.updateWith(updatedInvalidCurrencyDetails);
+ });
+ try {
+ await payRequest.show();
+ ok(false, "Should have rejected with 'RangeError'");
+ } catch (err) {
+ is(
+ err.name,
+ "RangeError",
+ `Should be rejected with 'RangeError', but got '${err.name}'.`
+ );
+ }
+ handler.destruct();
+}
+
+async function testUpdateWithInvalidAmount() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(
+ true
+ );
+ gScript.sendAsyncMessage("set-update-with-invalid-details-ui-service");
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ payRequest.addEventListener("shippingaddresschange", event => {
+ event.updateWith(updateWithInvalidAmount());
+ });
+ payRequest.addEventListener("shippingoptionchange", event => {
+ event.updateWith(updateWithInvalidAmount());
+ });
+ try {
+ await payRequest.show();
+ ok(false, "Should be rejected with 'TypeError'");
+ } catch (err) {
+ is(
+ err.name,
+ "TypeError",
+ `Should be rejected with 'TypeError', but got ${err.name}.`
+ );
+ }
+ handler.destruct();
+}
+
+function testSpecialAmount() {
+ try {
+ new PaymentRequest(defaultMethods, specialAmountDetails);
+ ok(false, "Should throw '42', but got resolved.");
+ } catch (e) {
+ is(e, "42", "Expected throw '42'. but got " + e);
+ }
+}
+
+function testInvalidTotalAmounts() {
+ for (const invalidAmount of invalidTotalAmounts) {
+ try {
+ const invalidDetails = {
+ total: {
+ label: "",
+ amount: {
+ currency: "USD",
+ value: invalidAmount,
+ },
+ },
+ };
+ new PaymentRequest(defaultMethods, invalidDetails);
+ ok(false, "Should throw 'TypeError', but got resolved.");
+ } catch (err) {
+ is(err.name, "TypeError", `Expected 'TypeError', but got '${err.name}'`);
+ }
+ }
+}
+
+function testInvalidAmounts() {
+ for (const invalidAmount of invalidAmounts) {
+ try {
+ new PaymentRequest(defaultMethods, {
+ total: {
+ label: "",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ displayItems: [
+ {
+ label: "",
+ amount: {
+ currency: "USD",
+ value: invalidAmount,
+ },
+ },
+ ],
+ });
+ ok(false, "Should throw 'TypeError', but got resolved.");
+ } catch (err) {
+ is(err.name, "TypeError", `Expected 'TypeError', but got '${err.name}'.`);
+ }
+ }
+}
+
+function teardown() {
+ return new Promise(resolve => {
+ gScript.addMessageListener(
+ "teardown-complete",
+ function teardownCompleteHandler() {
+ gScript.removeMessageListener(
+ "teardown-complete",
+ teardownCompleteHandler
+ );
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ resolve();
+ }
+ );
+ gScript.sendAsyncMessage("teardown");
+ });
+}
+
+async function runTests() {
+ try {
+ testInvalidTotalAmounts();
+ testSpecialAmount();
+ testInvalidAmounts();
+ testWithWellFormedCurrencyCodes();
+ testWithInvalidCurrencyCodes();
+ await testUpdateWithInvalidAmount();
+ await testUpdateWithInvalidCurrency();
+ await testWithLowerCaseCurrency();
+ await teardown();
+ } catch (e) {
+ console.error(e);
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ }
+}
+
+window.addEventListener("load", () => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["dom.payments.request.enabled", true]],
+ },
+ runTests
+ );
+});
+</script>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1367669">Mozilla Bug 1367669</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1388661">Mozilla Bug 1388661</a>
diff --git a/dom/payments/test/test_payerDetails.html b/dom/payments/test/test_payerDetails.html
new file mode 100644
index 0000000000..9a241803af
--- /dev/null
+++ b/dom/payments/test/test_payerDetails.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Test for PaymentResponse.prototype.onpayerdetailchange</title>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="./DefaultData.js"></script>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ const gUrl = SimpleTest.getTestFileURL("PayerDetailsChromeScript.js");
+ const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function okTester(result) {
+ return message => ok(result, message);
+ }
+ const passListener = okTester(true);
+ const failListener = okTester(false);
+
+ gScript.addMessageListener("test-fail", failListener);
+ gScript.addMessageListener("test-pass", passListener);
+
+ function sendOnce(message) {
+ return data => {
+ return new Promise(resolve => {
+ const doneMsg = `${message}-complete`;
+ gScript.addMessageListener(doneMsg, function listener() {
+ gScript.removeMessageListener(doneMsg, listener);
+ resolve();
+ });
+ gScript.sendAsyncMessage(message, data);
+ });
+ };
+ }
+ const sendTearDown = sendOnce("teardown");
+
+ async function loopTest(iterations) {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(
+ true
+ );
+ const options = {
+ requestPayerName: true,
+ requestPayerEmail: true,
+ requestPayerPhone: true,
+ }
+ const request = new PaymentRequest(defaultMethods, defaultDetails, options);
+ const response = await request.show();
+ is(response.payerName, "", ".payerName must initially be ''");
+ is(response.payerEmail, "", ".payerEmail must initially be ''");
+ is(response.payerPhone, "", ".payerPhone must initially be ''");
+ for (let i = 0; i < iterations; i++) {
+ const payer = {
+ name: `test name ${i}`,
+ phone: `test phone ${i}`,
+ email: `test email ${i}`,
+ }
+
+ // Capture the event to firing
+ const eventPromise = new Promise(resolve => {
+ response.onpayerdetailchange = resolve;
+ });
+ const retryPromise = response.retry({
+ error: "retry-fire-payerdetaichangeevent",
+ payer
+ });
+ const event = await eventPromise;
+
+ // Check things got updated
+ is(response.payerName, payer.name, `.payerName must be "${payer.name}"`);
+ is(response.payerEmail, payer.email, `.payerEmail must be "${payer.email}"`);
+ is(response.payerPhone, payer.phone, `.payerPhone must be "${payer.phone}"`);
+
+ // Finally, let's do an updateWith()
+ event.updateWith({ error: "update-with", payerErrors: payer, ...defaultDetails });
+
+ await retryPromise;
+ }
+
+ await response.complete("success");
+ handler.destruct();
+ }
+
+ async function teardown() {
+ await sendTearDown();
+ gScript.removeMessageListener("test-fail", failListener);
+ gScript.removeMessageListener("test-pass", passListener);
+ gScript.destroy();
+ SimpleTest.finish();
+ }
+
+ async function runTests() {
+ try {
+ await loopTest(5); // lets go around 5 times
+ } catch (err) {
+ ok(false, `Unexpected error: ${err}.`);
+ } finally {
+ await teardown();
+ }
+ }
+
+ window.addEventListener("load", () => {
+ const prefs = [["dom.payments.request.enabled", true]];
+ SpecialPowers.pushPrefEnv({ set: prefs }, runTests);
+ });
+</script>
+
+<body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1472026">Mozilla Bug 1472026</a>
diff --git a/dom/payments/test/test_payment-request-in-iframe.html b/dom/payments/test/test_payment-request-in-iframe.html
new file mode 100644
index 0000000000..0a4b690f9b
--- /dev/null
+++ b/dom/payments/test/test_payment-request-in-iframe.html
@@ -0,0 +1,168 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1318988
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1318988</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ function testRequestInSameOrigin() {
+ return new Promise((resolve, reject) => {
+ var ifr = document.createElement('iframe');
+
+ let listener = function(event) {
+ is(event.data, "successful",
+ "Expected 'successful', but got '" + event.data + "'");
+ resolve();
+ };
+
+ window.addEventListener("message", listener);
+
+ ifr.src = "simple_payment_request.html";
+ document.body.appendChild(ifr);
+
+ ifr.addEventListener('load', function() {
+ window.removeEventListener("message", listener);
+ });
+ });
+ }
+
+ function testRequestInIFrame() {
+ return new Promise((resolve, reject) => {
+ var ifr = document.createElement('iframe');
+
+ let listener = function(event) {
+ is(event.data, "SecurityError",
+ "Expected 'SecurityError', but got '" + event.data + "'");
+ resolve();
+ };
+
+ window.addEventListener("message", listener);
+
+ ifr.src = "https://test1.example.com:443/tests/dom/payments/test/simple_payment_request.html";
+ document.body.appendChild(ifr);
+
+ ifr.addEventListener('load', function() {
+ window.removeEventListener("message", listener);
+ });
+ });
+ }
+
+ function testRequestInIFrameWithAttribute() {
+ return new Promise((resolve, reject) => {
+ var ifrr = document.createElement('iframe');
+
+ let listener = function(event) {
+ is(event.data, "successful",
+ "Expected 'successful', but got '" + event.data + "'");
+ resolve();
+ };
+
+ window.addEventListener("message", listener);
+
+ ifrr.setAttribute('allow', 'payment');
+ ifrr.src = "https://test1.example.com:443/tests/dom/payments/test/simple_payment_request.html";
+ document.body.appendChild(ifrr);
+
+ ifrr.addEventListener('load', function() {
+ window.removeEventListener("message", listener);
+ });
+ });
+ }
+
+ function testRequestWithAttributeChanged() {
+ return new Promise((resolve, reject) => {
+ var ifrr = document.createElement('iframe');
+
+ let i = 0;
+
+ ifrr.addEventListener('load', function() {
+ if (i === 0) {
+ ifrr.removeAttribute("allow");
+ }
+ ifrr.contentWindow.postMessage('new PaymentRequest', '*');
+ });
+
+ let listener = function(event) {
+ i++;
+ if (i === 1) {
+ is(event.data, "successful",
+ "Expected successful when running with allow=payment attribute.");
+ ifrr.contentWindow.location.href = ifrr.src;
+ } else {
+ is(event.data, "SecurityError",
+ "Expected SecurityError when running without allow=payment attribute.");
+ window.removeEventListener("message", listener);
+ resolve();
+ }
+ }
+ window.addEventListener("message", listener);
+
+ ifrr.setAttribute("allow", "payment");
+ ifrr.src = "https://test1.example.com:443/tests/dom/payments/test/echo_payment_request.html";
+
+ document.body.appendChild(ifrr);
+ });
+ }
+
+ function testRequestInCrossOriginNestedIFrame() {
+ return new Promise((resolve, reject) => {
+ var ifrr = document.createElement('iframe');
+
+ let listener = function(event) {
+ if (ifrr.allow =! 'payment') {
+ is(event.data, "SecurityError",
+ "Expected 'SecurityError' without allow=payment in nested iframe");
+ ifrr.setAttribute('allow', "payment");
+ ifrr.contentWindow.location.href = ifrr.src;
+ } else {
+ is(event.data, "successful",
+ "Expected 'successful' with allow='payment' in nested iframe");
+ window.removeEventListener("message", listener);
+ resolve();
+ }
+ };
+ window.addEventListener("message", listener);
+
+ ifrr.addEventListener("load", function() {
+ ifrr.contentWindow.postMessage('new PaymentRequest in a new iframe', '*');
+ })
+
+ ifrr.src = "https://test1.example.com:443/tests/dom/payments/test/echo_payment_request.html";
+ document.body.appendChild(ifrr);
+ });
+ }
+
+ function runTests() {
+ testRequestInSameOrigin()
+ .then(testRequestInIFrame)
+ .then(testRequestInIFrameWithAttribute)
+ .then(testRequestWithAttributeChanged)
+ .then(testRequestInCrossOriginNestedIFrame)
+ .then(SimpleTest.finish)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1318988">Mozilla Bug 1318988</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_pmi_validation.html b/dom/payments/test/test_pmi_validation.html
new file mode 100644
index 0000000000..00d5c0771c
--- /dev/null
+++ b/dom/payments/test/test_pmi_validation.html
@@ -0,0 +1,245 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1389418
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for PaymentRequest API payment method identifier validation</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('PMIValidationChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+
+ const defaultMethods = [{
+ supportedMethods: "basic-card",
+ }];
+
+ const defaultDetails = {
+ total: {
+ label: "total",
+ amount: {
+ currency: "usd",
+ value: "1.00",
+ },
+ },
+ };
+
+ const validPMIs = [
+ "https://wpt",
+ "https://wpt.fyi/",
+ "https://wpt.fyi/payment",
+ "https://wpt.fyi/payment-request",
+ "https://wpt.fyi/payment-request?",
+ "https://wpt.fyi/payment-request?this=is",
+ "https://wpt.fyi/payment-request?this=is&totally",
+ "https://wpt.fyi:443/payment-request?this=is&totally",
+ "https://wpt.fyi:443/payment-request?this=is&totally#fine",
+ "https://:@wpt.fyi:443/payment-request?this=is&totally#👍",
+ " \thttps://wpt\n ",
+ "https://xn--c1yn36f",
+ "https://點看",
+ "e",
+ "n6jzof05mk2g4lhxr-u-q-w1-c-i-pa-ty-bdvs9-ho-ae7-p-md8-s-wq3-h-qd-e-q-sa",
+ "a-b-q-n-s-pw0",
+ "m-u",
+ "s-l5",
+ "k9-f",
+ "m-l",
+ "u4-n-t",
+ "i488jh6-g18-fck-yb-v7-i",
+ "x-x-t-t-c34-o",
+ "basic-card",
+ ];
+
+ const invalidPMIs = [
+ "https://:password@example.com",
+ "https://username@example.com",
+ "https://username:password@example.com/pay",
+ "http://username:password@example.com/pay",
+ "https://:@example.com:100000000/pay",
+ "https://foo.com:100000000/pay",
+ "basic-💳",
+ "not-https://wpt.fyi/payment-request",
+ "../realitive/url",
+ "/absolute/../path?",
+ "https://",
+ "¡basic-*-card!",
+ "Basic-Card",
+ "0",
+ "-",
+ "--",
+ "a--b",
+ "-a--b",
+ "a-b-",
+ "0-",
+ "0-a",
+ "a0--",
+ "A-",
+ "A-B",
+ "A-b",
+ "a-0",
+ "a-0b",
+ " a-b",
+ "\t\na-b",
+ "a-b ",
+ "a-b\n\t",
+ ];
+
+ function testWithValidPMIs() {
+ return new Promise((resolve, reject) => {
+ for (const validPMI of validPMIs) {
+ try {
+ const validMethods = [{supportedMethods: validPMI},];
+ const payRequest = new PaymentRequest(validMethods, defaultDetails);
+ resolve();
+ } catch (e) {
+ ok(false, "Unexpected error '" + e.name + "'.");
+ resolve();
+ }
+ }
+ });
+ }
+
+ function testWithInvalidPMIs() {
+ return new Promise((resolve, reject) => {
+ for (const invalidPMI of invalidPMIs) {
+ try {
+ const invalidMethods = [{supportedMethods: invalidPMI},];
+ const payRequest = new PaymentRequest(invalidMethods, defaultDetails);
+ ok(false, "Expected throw 'RangeError', but got resolved");
+ resolve();
+ } catch (e) {
+ is(e.name, "RangeError", "Expected 'RangeError'.");
+ resolve();
+ }
+ }
+ });
+ }
+
+ function testUpdateWithValidPMI() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+
+ gScript.sendAsyncMessage("set-ui-service");
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ payRequest.addEventListener("shippingoptionchange", event => {
+ const validDetails = {
+ total: {
+ label: "total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ modifiers: [{
+ supportedMethods: "https://example.com",
+ total: {
+ label: "total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ }
+ },],
+ }
+ event.updateWith(validDetails);
+ });
+ payRequest.show().then((response) => {
+ response.complete("success").then(() => {
+ resolve();
+ }).catch((e) => {
+ ok(false, "Unexpected error '" + e.name + "'.");
+ resolve();
+ });
+ }).catch((e) => {
+ ok(false, "Unexpected error '" + e.name + "'.");
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ function testUpdateWithInvalidPMI() {
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+
+ gScript.sendAsyncMessage("set-ui-service");
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails);
+ payRequest.addEventListener("shippingoptionchange", event => {
+ const invalidDetails = {
+ total: {
+ label: "total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ modifiers: [{
+ supportedMethods: "https://username:password@example.com",
+ total: {
+ label: "total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ },],
+ }
+ event.updateWith(invalidDetails);
+ });
+ payRequest.show().then((result) => {
+ ok(false, "Expected throw 'RangeError', but got resolved.");
+ resolve();
+ }).catch((e) => {
+ is(e.name, "RangeError", "Expected 'RangeError'.");
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler)
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ function runTests() {
+ testWithValidPMIs()
+ .then(testWithInvalidPMIs)
+ .then(testUpdateWithValidPMI)
+ .then(testUpdateWithInvalidPMI)
+ .then(teardown)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1389418">Mozilla Bug 1389418</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_requestShipping.html b/dom/payments/test/test_requestShipping.html
new file mode 100644
index 0000000000..b866588953
--- /dev/null
+++ b/dom/payments/test/test_requestShipping.html
@@ -0,0 +1,180 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1436903
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1436903</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('RequestShippingChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+
+ const defaultMethods = [{
+ supportedMethods: "basic-card",
+ data: {
+ supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
+ 'diners', 'jcb', 'mir',
+ ],
+ },
+ }, {
+ supportedMethods: "testing-payment-method",
+ }];
+ const defaultDetails = {
+ id: "test payment",
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: false,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: false,
+ },
+ ],
+ };
+
+ const defaultOptions = {
+ requestPayerName: true,
+ requestPayerEmail: false,
+ requestPayerPhone: false,
+ requestShipping: false,
+ shippingType: "shipping"
+ };
+
+ const updatedOptionDetails = {
+ total: {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00"
+ }
+ },
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: false,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: true,
+ },
+ ],
+ };
+
+ const nonSupportedMethods = [{
+ supportedMethods: "nonsupported-method",
+ }];
+
+
+ function updateWithShippingAddress() {
+ return new Promise((resolve, reject) => {
+ resolve(defaultDetails);
+ });
+ }
+
+ function updateWithShippingOption() {
+ return new Promise((resolve, reject) => {
+ resolve(updatedOptionDetails);
+ });
+ }
+
+ function testShow() {
+ gScript.sendAsyncMessage("set-normal-ui-service");
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+
+ payRequest.addEventListener("shippingaddresschange", event => {
+ event.updateWith(updateWithShippingAddress());
+ });
+ payRequest.addEventListener("shippingoptionchange", event => {
+ event.updateWith(updateWithShippingOption());
+ });
+
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ payRequest.show().then(response => {
+ response.complete("success").then(() =>{
+ resolve();
+ }).catch(e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ }).catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ function teardown() {
+ ok(true, "Mandatory assert");
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler)
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ function runTests() {
+ testShow()
+ .then(teardown)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1436903">Mozilla Bug 1436903</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_retryPayment.html b/dom/payments/test/test_retryPayment.html
new file mode 100644
index 0000000000..3ce389f475
--- /dev/null
+++ b/dom/payments/test/test_retryPayment.html
@@ -0,0 +1,354 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1435161
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for retry PaymentRequest</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="DefaultData.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ const gUrl = SimpleTest.getTestFileURL('RetryPaymentChromeScript.js');
+ const gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ async function requestChromeAction(action, params) {
+ gScript.sendAsyncMessage(action, params);
+ await new Promise(resolve => {
+ gScript.addMessageListener(`${action}-complete`, function completeListener() {
+ gScript.removeMessageListener(`${action}-complete`, completeListener);
+ resolve();
+ });
+ });
+ }
+
+ const validationErrors = {
+ error: "error",
+ shippingAddress: {
+ addressLine: "addressLine error",
+ city: "city error",
+ country: "country error",
+ dependentLocality: "dependentLocality error",
+ organization: "organization error",
+ phone: "phone error",
+ postalCode: "postalCode error",
+ recipient: "recipient error",
+ region: "region error",
+ regionCode: "regionCode error",
+ sortingCode: "sortingCode error",
+ },
+ payer: {
+ name: "name error",
+ email: "email error",
+ phone: "phone error",
+ },
+ paymentMethod: {
+ account: "method account error",
+ password: "method password error",
+ },
+ };
+
+ const options = {
+ requestPayerName: true,
+ requestPayerEmail: true,
+ requestPayerPhone: true,
+ requestShipping: true,
+ shippingType: "shipping"
+ };
+
+ function checkShowResponse(testName, payResponse) {
+ const { payerName, payerEmail, payerPhone } = payResponse.toJSON();
+ is(
+ payerName,
+ "Bill A. Pacheco",
+ `${testName}: Expected 'Bill A. Pacheco' on payerName, but got '${payerName}' after show PaymentRequest`
+ );
+ is(
+ payerEmail,
+ "",
+ `${testName}: Expected '' on payerEmail, but got '${payerEmail}' after show PaymentRequest`
+ );
+ is(
+ payerPhone,
+ "",
+ `${testName}: Expected '' on payerPhone, but got '${payerPhone}' after show PaymentRequest`
+ );
+ }
+
+ function checkRetryResponse(testName, payResponse) {
+ const { payerName, payerEmail, payerPhone } = payResponse.toJSON();
+ is(
+ payerName,
+ "Bill A. Pacheco",
+ `${testName}: Expected 'Bill A. Pacheco' on payerName, but got '${payerName}' after retry PaymentRequest`
+ );
+ is(
+ payerEmail,
+ "bpacheco@test.org",
+ `${testName} : Expected 'bpacheco@test.org' on payerEmail, but got '${payerEmail}' after retry PaymentRequest`
+ );
+ is(
+ payerPhone,
+ "+123456789",
+ `${testName} : Expected '+123456789' on payerPhone, but got '${payerPhone}' after retry PaymentRequest`
+ );
+ }
+
+ function unexpectedErrMsg(testName, errName, timing) {
+ return `${testName}: Unexpected error(${errName}) when ${timing} the PaymentRequest.`;
+ }
+
+ function expectedErrMsg(testName, expectedErr, errName, timing) {
+ return `${testName}: Expected '${expectedErr}' when ${timing} PaymentResponse, but got '${errName}'.`;
+ }
+
+ async function testRetryAfterComplete() {
+ const testName = "testRetryAfterComplete";
+ await requestChromeAction("start-test", testName);
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+ ok(payRequest, testName + ": failed to create PaymentRequest.");
+ if (!payRequest) {
+ await requestChromeAction("finish-test");
+ return;
+ }
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let payResponse;
+ try {
+ payResponse = await payRequest.show();
+ await checkShowResponse(testName, payResponse);
+ handler.destruct();
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.Name, "showing"));
+ await requestChromeAction("finish-test");
+ handler.destruct();
+ return;
+ }
+ try {
+ await payResponse.complete("success");
+ } catch(err) {
+ let errName = err.Name;
+ ok(false, unexpectedErrMsg(testName, err.Name, "completing"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ try {
+ await payResponse.retry(validationErrors);
+ ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+ return;
+ } catch(err) {
+ is(err.name,
+ "InvalidStateError",
+ expectedErrMsg(testName, "InvalidStateError", err.name, "retrying"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ await requestChromeAction("finish-test");
+ }
+
+ async function testRetryAfterRetry() {
+ const testName = "testRetryAfterRetry";
+ await requestChromeAction("start-test", testName);
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+ ok(payRequest, testName + ": failed to create PaymentRequest.");
+ if (!payRequest) {
+ await requestChromeAction("finish-test");
+ return;
+ }
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let payResponse;
+ try {
+ payResponse = await payRequest.show();
+ await checkShowResponse(testName, payResponse);
+ handler.destruct();
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+ await requestChromeAction("finish-test");
+ handler.destruct();
+ return;
+ }
+ let retryPromise;
+ try {
+ retryPromise = payResponse.retry(validationErrors);
+ await payResponse.retry(validationErrors);
+ ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+ await requestChromeAction("finish-test");
+ return;
+ } catch(err) {
+ is(err.name,
+ "InvalidStateError",
+ expectedErrMsg(testName, "InvalidStateError", err.name, "retrying"));
+ }
+ try {
+ await retryPromise;
+ await payResponse.complete("success");
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "completing"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ await requestChromeAction("finish-test");
+ }
+
+ async function testRetryWithEmptyErrors() {
+ const testName = "testRetryWithEmptyErrors";
+ await requestChromeAction("start-test", testName);
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+ ok(payRequest, testName + ": failed to create PaymentRequest.");
+ if (!payRequest) {
+ requestChromeAction("finish-test");
+ return;
+ }
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let payResponse;
+ try {
+ payResponse = await payRequest.show();
+ await checkShowResponse(testName, payResponse);
+ handler.destruct();
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+ await requestChromeAction("finish-test");
+ handler.destruct();
+ return;
+ }
+ try {
+ await payResponse.retry();
+ ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+ await requestChromeAction("finish-test");
+ return;
+ } catch(err) {
+ is(err.name,
+ "AbortError",
+ expectedErrMsg(testName, "AbortError", err.name, "retrying"));
+ }
+ try {
+ await payResponse.complete("success");
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "completing"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ await requestChromeAction("finish-test");
+ }
+
+ async function testRetry() {
+ const testName = "testRetry";
+ await requestChromeAction("start-test", testName);
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+ ok(payRequest, testName + ": failed to create PaymentRequest.");
+ if (!payRequest) {
+ await requestChromeAction("finish-test");
+ return;
+ }
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let payResponse;
+ try {
+ payResponse = await payRequest.show();
+ await checkShowResponse(testName, payResponse);
+ handler.destruct();
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+ await requestChromeAction("finish-test");
+ handler.destruct();
+ return;
+ }
+ try {
+ await payResponse.retry(validationErrors);
+ await checkRetryResponse(testName, payResponse);
+ await payResponse.complete("success");
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "retrying"));
+ await requestChromeAction("finish-test");
+ return;
+ }
+ await requestChromeAction("finish-test");
+ }
+
+ async function testRetryAbortByUser() {
+ const testName = "testRetryAbortByUser";
+ await requestChromeAction("reject-retry");
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, options);
+ ok(payRequest, testName + ": failed to create PaymentRequest.");
+ if (!payRequest) {
+ await requestChromeAction("finish-test");
+ return;
+ }
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let payResponse;
+ try {
+ payResponse = await payRequest.show();
+ await checkShowResponse(testName, payResponse);
+ handler.destruct();
+ } catch(err) {
+ ok(false, unexpectedErrMsg(testName, err.name, "showing"));
+ handler.destruct();
+ await requestChromeAction("finish-test");
+ return;
+ }
+ try {
+ await payResponse.retry(validationErrors);
+ ok(false, `${testName}: Unexpected success when retry the PaymentResponse.`);
+ await requestChromeAction("finish-test");
+ return;
+ } catch(err) {
+ is(err.name,
+ "AbortError",
+ expectedErrMsg(testName, "AbortError", err.name, "retrying"));
+ }
+ await requestChromeAction("finish-test");
+ }
+
+ function teardown() {
+ return new Promise((resolve, reject) => {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ resolve();
+ });
+ gScript.sendAsyncMessage("teardown");
+ });
+ }
+
+ async function runTests() {
+ try {
+ await testRetryAfterComplete()
+ await testRetryAfterRetry()
+ await testRetryWithEmptyErrors()
+ await testRetry()
+ await testRetryAbortByUser()
+ await teardown()
+ } catch(e) {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1435161">Mozilla Bug 1435161</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_shippingOptions.html b/dom/payments/test/test_shippingOptions.html
new file mode 100644
index 0000000000..887ec30de5
--- /dev/null
+++ b/dom/payments/test/test_shippingOptions.html
@@ -0,0 +1,208 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1440041
+https://bugzilla.mozilla.org/show_bug.cgi?id=1443914
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for shippingOptions related bugs</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="./DefaultData.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('ShippingOptionsChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ let shippingOptions = [{
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00",
+ },
+ selected: true,
+ },{
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "5.00",
+ },
+ selected: false,
+ }]
+
+ // testing function main body
+ function testShippingOptionsTemplate(initDetails,
+ optionUpdateDetails,
+ expectedRequestOption,
+ expectedOptionChangeOption,
+ expectedResponseOption) {
+ const expectedResults = {requestResult: expectedRequestOption,
+ changeOptionResult: expectedOptionChangeOption,
+ responseResult: expectedResponseOption,};
+ gScript.sendAsyncMessage("set-expected-results", expectedResults);
+ return new Promise((resolve, reject) => {
+ const request = new PaymentRequest(defaultMethods, initDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ is(request.shippingOption, expectedRequestOption,
+ "request.shippingOption should be " + expectedRequestOption +
+ " after created, but got " + request.shippingOption + ".");
+ if (optionUpdateDetails) {
+ request.addEventListener("shippingoptionchange", event => {
+ is(request.shippingOption, expectedOptionChangeOption,
+ "request.shippingOption should be " + expectedOptionChangeOption +
+ " in shippingoptionchange event, but got " + request.shippingOption + ".");
+ event.updateWith(optionUpdateDetails);
+ });
+ }
+ request.show().then(response => {
+ is(response.shippingOption, expectedResponseOption,
+ "response.shippingOption should be " + expectedResponseOption +
+ ", but got " + response.shippingOption + ".");
+ response.complete("success").then(() => {
+ resolve();
+ }).catch(error => {
+ ok(false, "Unexpected error: " + error.name);
+ resolve();
+ })
+ }, response => {
+ }).catch(error => {
+ ok(false, "Unexpected error: " + error.name);
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ // test no selected shipping option in default
+ function testNoSelectedShippingOptions() {
+ return testShippingOptionsTemplate(defaultDetails, // initial details
+ null, // update details for optionchange
+ null, // expected request.shippintOption after create
+ null, // expected request.shippingOption after optionchange
+ null); // expected response.shippingOption
+ }
+
+ // test select one shipping option in default
+ function testSelectedOneShippingOption() {
+ let details = Object.assign({}, defaultDetails);
+ details.shippingOptions = shippingOptions;
+ details.shippingOptions[0].selected = true;
+ details.shippingOptions[1].selected = false;
+ const expectedOption = details.shippingOptions[0].id;
+ return testShippingOptionsTemplate(details, // initial details
+ null, // update details for optionchange
+ expectedOption, // expected request.shippintOption after create
+ null, // expected request.shippingOption after optionchange
+ expectedOption); // expected response.shippingOption
+ }
+
+ // test select multiple shipping options in default
+ function testMultiSelectedShippingOptions() {
+ let details = Object.assign({}, defaultDetails);
+ details.shippingOptions = shippingOptions;
+ details.shippingOptions[0].selected = true;
+ details.shippingOptions[1].selected = true;
+ const expectedOption = details.shippingOptions[1].id;
+ return testShippingOptionsTemplate(details, // initial details
+ null, // update details for optionchange
+ expectedOption, // expected request.shippintOption after create
+ null, // expected request.shippingOption after optionchange
+ expectedOption); // expected response.shippingOption
+ }
+
+ // test no selected shipping option in default, but selected by user
+ function testSelectedByUser() {
+ let updateDetails = Object.assign({}, defaultDetails);
+ updateDetails.shippingOptions = shippingOptions;
+ updateDetails.shippingOptions[0].selected = true;
+ updateDetails.shippingOptions[1].selected = false;
+ const expectedOption = updateDetails.shippingOptions[0].id;
+ return testShippingOptionsTemplate(defaultDetails, // initial details
+ updateDetails, // update details for optionchange
+ null, // expected request.shippintOption after create
+ expectedOption, // expected request.shippingOption after optionchange
+ expectedOption); // expected response.shippingOption
+ }
+
+ // test no selected shipping option in default, but selected by user then updated
+ // by merchant to the other.
+ function testUpdateSelectedByMerchant() {
+ let updateDetails = Object.assign({}, defaultDetails);
+ updateDetails.shippingOptions = shippingOptions;
+ updateDetails.shippingOptions[0].selected = false;
+ updateDetails.shippingOptions[1].selected = true;
+ const expectedOption = updateDetails.shippingOptions[0].id;
+ const expectedResponse = updateDetails.shippingOptions[1].id;
+ return testShippingOptionsTemplate(defaultDetails, // initial details
+ updateDetails, // update details for optionchange
+ null, // expected request.shippintOption after create
+ expectedOption, // expected request.shippingOption after optionchange
+ expectedResponse);// expected response.shippingOption
+ }
+
+ // test update shipping options to null
+ function testUpdateShippingOptionsToNull() {
+ let updateDetails = Object.assign({}, defaultDetails);
+ delete updateDetails.shippingOptions;
+ const expectedOption = defaultDetails.shippingOptions[0].id;
+ return testShippingOptionsTemplate(defaultDetails, // initial details
+ updateDetails, // update details for optionchange
+ null, // expected request.shippintOption after create
+ expectedOption, // expected request.shippingOption after optionchange
+ null); // expected response.shippingOption
+ }
+
+ function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ function runTests() {
+ testNoSelectedShippingOptions()
+ .then(testSelectedOneShippingOption)
+ .then(testMultiSelectedShippingOptions)
+ .then(testSelectedByUser)
+ .then(testUpdateSelectedByMerchant)
+ .then(testUpdateShippingOptionsToNull)
+ .then(teardown)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1440041">Mozilla Bug 1440041</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1443914">Mozilla Bug 1443914</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_showPayment.html b/dom/payments/test/test_showPayment.html
new file mode 100644
index 0000000000..2a4a0bb4f7
--- /dev/null
+++ b/dom/payments/test/test_showPayment.html
@@ -0,0 +1,504 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1345366
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1345366</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('ShowPaymentChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ async function requestChromeAction(action, params) {
+ await new Promise(resolve => {
+ gScript.addMessageListener(`${action}-complete`, function completeListener() {
+ gScript.removeMessageListener(`${action}-complete`, completeListener);
+ resolve();
+ });
+ gScript.sendAsyncMessage(action, params);
+ });
+ }
+
+ // testing data declaration
+ // default parameters for PaymentRequest construction
+ const defaultMethods = [{
+ supportedMethods: "basic-card",
+ data: {
+ supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
+ 'diners', 'jcb', 'mir',
+ ],
+ },
+ }, {
+ supportedMethods: "testing-payment-method",
+ }];
+
+ const defaultTotal = {
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ }
+
+ const defaultDetails = {
+ id: "test payment",
+ total: defaultTotal,
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: false,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: false,
+ },
+ ],
+ };
+
+ const defaultOptions = {
+ requestPayerName: true,
+ requestPayerEmail: false,
+ requestPayerPhone: false,
+ requestShipping: true,
+ shippingType: "shipping"
+ };
+
+ // testing data for PaymentRequestUpdateEvent.updateWith()
+ const updatedShippingOptionsDetails = {
+ total: defaultTotal,
+ shippingOptions: [
+ {
+ id: "NormalShipping",
+ label: "NormalShipping",
+ amount: {
+ currency: "USD",
+ value: "10.00"
+ },
+ selected: false,
+ },
+ {
+ id: "FastShipping",
+ label: "FastShipping",
+ amount: {
+ currency: "USD",
+ value: "30.00"
+ },
+ selected: true,
+ },
+ ],
+ };
+
+ const updatedErrorDetails = {
+ total: defaultTotal,
+ error: "Update with Error",
+ };
+
+ // Promise function for PaymentRequestUpdateEvent.updateWith()
+ function updateWithPromise(detailsUpdate) {
+ return new Promise((resolve, reject) => {
+ if (detailsUpdate) {
+ resolve(detailsUpdate);
+ } else {
+ reject();
+ }
+ });
+ }
+
+ // testing data for PaymentRequest.show() with Non-supported methods
+ const nonSupportedMethods = [{
+ supportedMethods: "nonsupported-method",
+ }];
+
+
+ // checking functions
+ function checkAddress(testName, address, fromEvent) {
+ is(address.country,
+ "USA",
+ `${testName}: address.country should be 'USA'.`);
+ is(address.region,
+ "CA",
+ `${testName}: address.region should be 'CA'.`);
+ is(address.city,
+ "San Bruno",
+ `${testName}: address.city should be 'San Bruno'.`);
+ is(address.dependentLocality,
+ "Test locality",
+ `${testName}: address.dependentLocality should be 'Test locality'.`);
+ is(address.postalCode,
+ "94066",
+ `${testName}: address.postalCode should be '94066'.`);
+ is(address.sortingCode,
+ "123456",
+ `${testName}: address.sortingCode should be '123456'.`);
+ if (fromEvent) {
+ is(address.addressLine.length,
+ 0,
+ `${testName}: address.addressLine.length should be 0 from event.`);
+ is(address.organization,
+ "",
+ `${testName}: address.organization should be empty from event.`);
+ is(address.recipient,
+ "",
+ `${testName}: address.recipient should be empty from event.`);
+ is(address.phone,
+ "",
+ `${testName}: address.phone should be empty from event.`);
+ } else {
+ is(address.addressLine.length,
+ 1,
+ `${testName}: address.addressLine.length should be 1 from promise.`);
+ is(address.addressLine[0],
+ "Easton Ave",
+ `${testName}: address.addressLine[0] should be 'Easton Ave' from promise.`);
+ is(address.organization,
+ "Testing Org",
+ `${testName}: address.organization should be 'Testing Org' from promise.`);
+ is(address.recipient,
+ "Bill A. Pacheco",
+ `${testName}: address.recipient should be 'Bill A. Pacheco' from promise.`);
+ is(address.phone,
+ "+1-434-441-3879",
+ `${testName}: address.phone should be '+1-434-441-3879' from promise.`);
+ }
+ }
+
+ function checkResponse(testName, response) {
+ is(response.requestId,
+ "test payment",
+ `${testName}: response.requestId should be 'test payment'.`);
+ is(response.methodName,
+ "testing-payment-method",
+ `${testName}: response.methodName should be 'testing-payment-method'.`);
+ is(response.details.paymentToken,
+ "6880281f-0df3-4b8e-916f-66575e2457c1",
+ `${testName}: response.details.paymentToken should be '6880281f-0df3-4b8e-916f-66575e2457c1'.`);
+ checkAddress(testName, response.shippingAddress, false/*fromEvent*/);
+ is(response.shippingOption,
+ "FastShipping",
+ `${testName}: response.shippingOption should be 'FastShipping'.`);
+ is(response.payerName,
+ "Bill A. Pacheco",
+ `${testName}: response.payerName should be 'Bill A. Pacheco'.`);
+ ok(!response.payerEmail,
+ `${testName}: response.payerEmail should be empty`);
+ ok(!response.payerPhone,
+ `${testName}: response.payerPhone should be empty`);
+ }
+
+ // testing functions
+ async function testShowNormalFlow() {
+ const testName = "testShowNormalFlow";
+ await requestChromeAction("set-normal-ui-service", testName);
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ request.addEventListener("shippingaddresschange", event => {
+ checkAddress(testName, request.shippingAddress, true/*fromEvent*/);
+ event.updateWith(updateWithPromise(defaultDetails));
+ });
+ request.addEventListener("shippingoptionchange", event => {
+ event.updateWith(updateWithPromise(updatedShippingOptionsDetails));
+ });
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ let response = await request.show();
+ checkResponse(testName, response, false);
+ await response.complete();
+ } catch (error) {
+ ok(false, `${testName} Unexpected error: ${e.name}`);
+ }
+ await handler.destruct();
+ }
+
+ // testing show with nonsupported methods
+ async function testCannotMakePaymentShow() {
+ const testName = "testCannotMakePaymentShow";
+ await requestChromeAction("set-simple-ui-service", testName);
+
+ const request = new PaymentRequest(nonSupportedMethods, defaultDetails);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let result = await request.canMakePayment();
+ ok(!result, `${testName}: canMakePayment() should return false.`);
+ try {
+ await request.show();
+ ok(false, `${testName}: should be rejected with 'NotSupportedError' but got resolved.`);
+ } catch (error) {
+ is(error.name, "NotSupportedError", `${testName}: should be rejected with 'NotSupportedError'.`);
+ }
+ await handler.destruct();
+ }
+
+ // testing show rejected by user
+ async function testRejectShow() {
+ const testName = "testRejectShow";
+ await requestChromeAction("set-reject-ui-service", testName);
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ await request.show();
+ ok(false, `${testName}: Should be rejected with 'AbortError' but got resolved.`);
+ } catch(error) {
+ is(error.name, "AbortError", `${testName}: Should be rejected with 'AbortError'.`);
+ }
+ await handler.destruct();
+ }
+
+ // testing PaymentResponse.complete() with specified result
+ async function testCompleteStatus(testName, result) {
+ await requestChromeAction("set-simple-ui-service", testName);
+ if (result) {
+ await requestChromeAction(`set-complete-status-${result}`);
+ } else {
+ await requestChromeAction(`set-complete-status-unknown`);
+ }
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ let response = await request.show();
+ await response.complete(result);
+ } catch (error) {
+ ok(false, `${testName}: Unexpected error ${error.name}.`);
+ }
+ await handler.destruct();
+ }
+
+ async function testCompleteFail() {
+ const testName = "testCompleteFail";
+ return testCompleteStatus(testName, "fail");
+ }
+
+ async function testCompleteSuccess() {
+ const testName = "testCompleteSuccess";
+ return testCompleteStatus(testName, "success");
+ }
+
+ async function testCompleteUnknown() {
+ const testName = "testCompleteUnknown"
+ return testCompleteStatus(testName, "unknown");
+ }
+
+ async function testCompleteEmpty() {
+ const testName = "testCompleteEmpty";
+ return testCompleteStatus(testName);
+ }
+
+ // testing PaymentRequestUpdateEvent.updateWith with specified details and error
+ async function testUpdateWith(testName, detailsUpdate, expectedError) {
+ if (expectedError) {
+ await requestChromeAction("set-update-with-error-ui-service", testName);
+ } else {
+ await requestChromeAction("set-update-with-ui-service", testName);
+ }
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ request.addEventListener("shippingaddresschange", event => {
+ event.updateWith(updateWithPromise(detailsUpdate));
+ });
+ request.addEventListener("shippingoptionchange", event => {
+ event.updateWith(updateWithPromise(detailsUpdate));
+ });
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ const response = await request.show();
+ if (expectedError) {
+ ok(false, `${testName}: Should be rejected with ${expectedError} but got resolved.`);
+ } else {
+ await response.complete("success");
+ }
+ } catch(error) {
+ if (expectedError) {
+ is(error.name, expectedError, `${testName}: Should be rejected with ${expectedError}.`);
+ } else {
+ ok(false, `${testName}: Unexpected error ${error.name}.`);
+ }
+ }
+ await handler.destruct();
+ }
+
+ async function testUpdateWithReject() {
+ const testName = "testUpdateWithReject";
+ return testUpdateWith(testName, null, "AbortError");
+ }
+
+ async function testUpdateWithValidDetails() {
+ const testName = "testUpdateWithValidDetails";
+ return testUpdateWith(testName, updatedShippingOptionsDetails, null);
+ }
+
+ async function testUpdateWithInvalidDetails() {
+ const testName = "testUpdateWithInvalidDetails";
+ return testUpdateWith(testName, {total: "invalid details"}, "TypeError");
+ }
+
+ async function testUpdateWithError() {
+ const testName = "testUpdateWithError";
+ return testUpdateWith(testName, updatedErrorDetails, "AbortError");
+ }
+
+ // testing show with detailsUpdate promise
+ async function testShowWithDetailsPromise(testName, detailsUpdate, expectedError) {
+ if (expectedError) {
+ await requestChromeAction("set-reject-ui-service", testName);
+ } else {
+ await requestChromeAction("set-simple-ui-service", testName);
+ }
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ ok(!request.shippingOption, `${testName}: request.shippingOption should be null.`);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ try {
+ let response = await request.show(updateWithPromise(detailsUpdate));
+ if (expectedError) {
+ ok(false, `${testName}: Should be rejected with ${expectedError} but got resolved.`);
+ } else {
+ ok(response.shippingOption,
+ `${testName}: response.shippingOption should not be null.`);
+ }
+ await response.complete();
+ } catch(error) {
+ if (expectedError) {
+ is(error.name, expectedError, `${testName}: Should be rejected with ${expectedError}.`);
+ } else {
+ ok(false, `${testName}: Unexpected error ${error.name}.`);
+ }
+ }
+ await handler.destruct();
+ }
+ async function testShowWithValidPromise() {
+ const testName = "testShowWithValidPromise";
+ return testShowWithDetailsPromise(testName, updatedShippingOptionsDetails, null);
+ }
+
+ async function testShowWithRejectedPromise() {
+ const testName = "testShowWithRejectedPromise";
+ return testShowWithDetailsPromise(testName, null, "AbortError");
+ }
+
+ async function testShowWithInvalidPromise() {
+ const testName = "testShowWithInvalidPromise";
+ return testShowWithDetailsPromise(testName, {total: "invalid details"}, "TypeError");
+ }
+
+ async function testShowWithErrorPromise() {
+ const testName = "testShowWithErrorPromise";
+ return testShowWithDetailsPromise(testName, updatedErrorDetails, "AbortError");
+ }
+
+ async function testShowWithPromiseResolvedByRejectedPromise() {
+ const testName = "testShowWithPromiseResolvedByRejectedPromise";
+ await requestChromeAction("set-reject-ui-service", testName);
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ let rejectPromise = Promise.reject(new TypeError());
+ let detailsUpdatePromise = Promise.resolve(rejectPromise);
+ try {
+ await request.show(detailsUpdatePromise);
+ ok(false, `${testName}: should be rejected with AbortError but got resolved.`);
+ } catch(error) {
+ is(error.name, "AbortError", `${testName}: should be rejected with AbortError.`);
+ }
+ await handler.destruct();
+ }
+
+ // testing show response initialization in chrome process
+ async function testShowResponseInit() {
+ const testName = "testShowResponseInit";
+ await requestChromeAction("test-show-response-init", testName);
+ }
+
+ // testing show that is not triggered by user.
+ async function testShowNotTriggeredByUser() {
+ const testName = "testShowNotTriggeredByUser";
+ await requestChromeAction("set-simple-ui-service", testName);
+
+ const request = new PaymentRequest(defaultMethods, defaultDetails);
+ try {
+ await request.show();
+ ok(false, `${testName}: should be rejected with SecurityError, but got resolved.`);
+ } catch (error) {
+ is(error.name, "SecurityError", `${testName}: should be rejected with SecurityError.`);
+ }
+ }
+
+ // teardown function
+ async function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ // test main body
+ async function runTests() {
+ try {
+ await testCannotMakePaymentShow();
+ await testRejectShow();
+ await testShowNormalFlow();
+ await testCompleteSuccess();
+ await testCompleteFail();
+ await testCompleteUnknown();
+ await testCompleteEmpty();
+ await testUpdateWithReject();
+ await testUpdateWithValidDetails();
+ await testUpdateWithInvalidDetails();
+ await testUpdateWithError();
+ await testShowWithValidPromise();
+ await testShowWithInvalidPromise();
+ await testShowWithRejectedPromise();
+ await testShowWithErrorPromise();
+ await testShowWithPromiseResolvedByRejectedPromise();
+ await testShowResponseInit();
+ await testShowNotTriggeredByUser();
+ await teardown();
+ } catch (error) {
+ ok(false, `test_showPayment: Unexpected error: ${error.name}`);
+ SimpleTest.finish();
+ }
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1345366">Mozilla Bug 1345366</a>
+</body>
+</html>
diff --git a/dom/payments/test/test_update_errors.html b/dom/payments/test/test_update_errors.html
new file mode 100644
index 0000000000..a473cf2706
--- /dev/null
+++ b/dom/payments/test/test_update_errors.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1435157
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1435157</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="DefaultData.js"></script>
+ <script type="application/javascript">
+
+ "use strict";
+ SimpleTest.waitForExplicitFinish();
+
+ var gUrl = SimpleTest.getTestFileURL('UpdateErrorsChromeScript.js');
+ var gScript = SpecialPowers.loadChromeScript(gUrl);
+
+ function testFailHandler(message) {
+ ok(false, message);
+ }
+ function testPassHandler(message) {
+ ok(true, message);
+ }
+ gScript.addMessageListener("test-fail", testFailHandler);
+ gScript.addMessageListener("test-pass", testPassHandler);
+
+ const addressErrors = {
+ addressLine: "addressLine error",
+ city: "city error",
+ country: "country error",
+ dependentLocality: "dependentLocality error",
+ organization: "organization error",
+ phone: "phone error",
+ postalCode: "postalCode error",
+ recipient: "recipient error",
+ region: "region error",
+ regionCode: "regionCode error",
+ sortingCode: "sortingCode error",
+ };
+
+ const payErrors = {
+ email: "email error",
+ name: "name error",
+ phone: "phone error",
+ };
+
+ let updateDetails = {
+ total:{
+ label: "Total",
+ amount: {
+ currency: "USD",
+ value: "1.00",
+ },
+ },
+ erros: "shipping address error",
+ shippingAddressErrors: addressErrors,
+ payerErrors: payErrors,
+ }
+
+ // testing functions
+ function testUpdateErrors() {
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ payRequest.addEventListener("shippingaddresschange", event => {
+ event.updateWith(updateDetails);
+ });
+ payRequest.addEventListener("shippingoptionchange", event => {
+ event.updateWith(updatedDetails);
+ });
+ const handler = SpecialPowers.getDOMWindowUtils(window).setHandlingUserInput(true);
+ payRequest.show().then(response => {
+ ok(false, "Expected AbortError, but got pass");
+ resolve();
+ }, error => {
+ is(error.name, "AbortError", "Expect AbortError, but got " + error.name);
+ resolve();
+ }).catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ }).finally(handler.destruct);
+ });
+ }
+
+ // teardown function
+ function teardown() {
+ gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
+ gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
+ gScript.destroy();
+ SimpleTest.finish();
+ });
+ gScript.sendAsyncMessage("teardown");
+ }
+
+ // test main body
+ function runTests() {
+ testUpdateErrors()
+ .then(teardown)
+ .catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ SimpleTest.finish();
+ });
+ }
+
+ window.addEventListener('load', function() {
+ SpecialPowers.pushPrefEnv({
+ 'set': [
+ ['dom.payments.request.enabled', true],
+ ]
+ }, runTests);
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1435157">Mozilla Bug 1435157</a>
+</body>
+</html>