Bug 1441683 - response.complete() should make the completeStatus available to the payment UI service. r?baku
MozReview-Commit-ID: TnszpuqAa
--- a/dom/interfaces/payments/nsIPaymentRequest.idl
+++ b/dom/interfaces/payments/nsIPaymentRequest.idl
@@ -76,14 +76,16 @@ interface nsIPaymentOptions : nsISupport
};
[scriptable, builtinclass, uuid(2fa36783-d684-4487-b7a8-9def6ae3128f)]
interface nsIPaymentRequest : nsISupports
{
readonly attribute uint64_t tabId;
readonly attribute nsIPrincipal topLevelPrincipal;
readonly attribute AString requestId;
+ readonly attribute AString completeStatus;
readonly attribute nsIArray paymentMethods;
readonly attribute nsIPaymentDetails paymentDetails;
readonly attribute nsIPaymentOptions paymentOptions;
+ [noscript] void setCompleteStatus(in AString aCompleteStatus);
void updatePaymentDetails(in nsIPaymentDetails aDetails);
};
--- a/dom/payments/PaymentRequestData.cpp
+++ b/dom/payments/PaymentRequestData.cpp
@@ -676,16 +676,30 @@ PaymentRequest::GetPaymentOptions(nsIPay
NS_IMETHODIMP
PaymentRequest::UpdatePaymentDetails(nsIPaymentDetails* aPaymentDetails)
{
MOZ_ASSERT(aPaymentDetails);
return mPaymentDetails->Update(aPaymentDetails);
}
+NS_IMETHODIMP
+PaymentRequest::SetCompleteStatus(const nsAString& aCompleteStatus)
+{
+ mCompleteStatus = aCompleteStatus;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+PaymentRequest::GetCompleteStatus(nsAString& aCompleteStatus)
+{
+ aCompleteStatus = mCompleteStatus;
+ return NS_OK;
+}
+
/* PaymentAddress */
NS_IMPL_ISUPPORTS(PaymentAddress, nsIPaymentAddress)
NS_IMETHODIMP
PaymentAddress::Init(const nsAString& aCountry,
nsIArray* aAddressLine,
const nsAString& aRegion,
--- a/dom/payments/PaymentRequestData.h
+++ b/dom/payments/PaymentRequestData.h
@@ -186,16 +186,17 @@ public:
nsIPaymentDetails* aPaymentDetails,
nsIPaymentOptions* aPaymentOptions);
private:
~PaymentRequest() = default;
uint64_t mTabId;
nsString mRequestId;
+ nsString mCompleteStatus;
nsCOMPtr<nsIPrincipal> mTopLevelPrincipal;
nsCOMPtr<nsIArray> mPaymentMethods;
nsCOMPtr<nsIPaymentDetails> mPaymentDetails;
nsCOMPtr<nsIPaymentOptions> mPaymentOptions;
};
class PaymentAddress final : public nsIPaymentAddress
{
--- a/dom/payments/PaymentRequestService.cpp
+++ b/dom/payments/PaymentRequestService.cpp
@@ -313,18 +313,41 @@ PaymentRequestService::RequestPayment(ns
}
rv = LaunchUIAction(requestId, type);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
}
}
break;
}
- case nsIPaymentActionRequest::ABORT_ACTION:
+ case nsIPaymentActionRequest::ABORT_ACTION: {
+ rv = LaunchUIAction(requestId, type);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_FAILURE;
+ }
+ break;
+ }
case nsIPaymentActionRequest::COMPLETE_ACTION: {
+ nsCOMPtr<nsIPaymentCompleteActionRequest> request =
+ do_QueryInterface(aRequest);
+ MOZ_ASSERT(request);
+ nsAutoString completeStatus;
+ rv = request->GetCompleteStatus(completeStatus);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<nsIPaymentRequest> payment;
+ rv = GetPaymentRequestById(requestId, getter_AddRefs(payment));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_FAILURE;
+ }
+ rv = payment->SetCompleteStatus(completeStatus);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return NS_ERROR_FAILURE;
+ }
rv = LaunchUIAction(requestId, type);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_FAILURE;
}
break;
}
case nsIPaymentActionRequest::UPDATE_ACTION: {
nsCOMPtr<nsIPaymentUpdateActionRequest> request = do_QueryInterface(aRequest);
--- a/dom/payments/test/ShowPaymentChromeScript.js
+++ b/dom/payments/test/ShowPaymentChromeScript.js
@@ -1,20 +1,24 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
const paymentSrv = Cc["@mozilla.org/dom/payments/payment-request-service;1"].getService(Ci.nsIPaymentRequestService);
+let expectedCompleteStatus;
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
@@ -24,24 +28,73 @@ shippingAddress.init("USA",
"Test locality", // dependent locality
"94066", // postal code
"123456", // sorting code
"en", // language code
"Testing Org", // organization
"Bill A. Pacheco", // recipient
"+1-434-441-3879"); // phone
+const DummyUIService = {
+ showPayment: function(requestId) {
+ 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\",}");
+ }
+ 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
+ showResponseData, // payment method data
+ "Bill A. Pacheco", // payer name
+ "", // payer email
+ ""); // payer phone
+ paymentSrv.respondPayment(showResponse.QueryInterface(Ci.nsIPaymentActionResponse));
+ },
+ abortPayment: function(requestId) {
+ },
+ completePayment: function(requestId) {
+ let payRequest = paymentSrv.getPaymentRequestById(requestId);
+ if (payRequest.completeStatus == expectedCompleteStatus) {
+ emitTestPass("request.completeStatus matches expectation of " + expectedCompleteStatus);
+ } else {
+ emitTestFail("request.completeStatus incorrect. Expected " +
+ expectedCompleteStatus + ", got " + payRequest.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));
+ },
+ updatePayment: function(requestId) {
+ },
+ QueryInterface: XPCOMUtils.generateQI([Ci.nsIPaymentUIService]),
+};
+
const NormalUIService = {
shippingOptionChanged: false,
showPayment: function(requestId) {
paymentSrv.changeShippingAddress(requestId, shippingAddress);
},
abortPayment: function(requestId) {
},
completePayment: function(requestId) {
+ let payRequest = paymentSrv.getPaymentRequestById(requestId);
+ if (payRequest.completeStatus == expectedCompleteStatus) {
+ emitTestPass("request.completeStatus matches expectation of " + expectedCompleteStatus);
+ } else {
+ emitTestFail("request.completeStatus incorrect. Expected " +
+ expectedCompleteStatus + ", got " + payRequest.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));
},
updatePayment: function(requestId) {
let showResponse = null;
let payRequest = paymentSrv.getPaymentRequestById(requestId);
@@ -203,28 +256,43 @@ function testInitDataAndResponse() {
} catch (e) {
if (e.name != "NS_ERROR_ILLEGAL_VALUE") {
emitTestFail("Expected 'NS_ERROR_ILLEGAL_VALUE', but got " + e.name + ".");
}
}
sendAsyncMessage("test-init-data-and-response-complete");
}
+addMessageListener("set-dummy-ui-service", function() {
+ paymentSrv.setTestingUIService(DummyUIService.QueryInterface(Ci.nsIPaymentUIService));
+});
+
addMessageListener("set-normal-ui-service", function() {
paymentSrv.setTestingUIService(NormalUIService.QueryInterface(Ci.nsIPaymentUIService));
});
addMessageListener("set-reject-ui-service", function() {
paymentSrv.setTestingUIService(RejectUIService.QueryInterface(Ci.nsIPaymentUIService));
});
addMessageListener("set-update-with-error-ui-service", function() {
paymentSrv.setTestingUIService(ErrorUIService.QueryInterface(Ci.nsIPaymentUIService));
});
addMessageListener("test-init-data-and-response", testInitDataAndResponse);
+addMessageListener("set-complete-status-success", function() {
+ expectedCompleteStatus = "success";
+});
+
+addMessageListener("set-complete-status-fail", function() {
+ expectedCompleteStatus = "fail";
+});
+
+addMessageListener("set-complete-status-unknown", function() {
+ expectedCompleteStatus = "unknown";
+});
addMessageListener("teardown", function() {
paymentSrv.cleanup();
paymentSrv.setTestingUIService(null);
sendAsyncMessage('teardown-complete');
});
--- a/dom/payments/test/test_showPayment.html
+++ b/dom/payments/test/test_showPayment.html
@@ -14,17 +14,21 @@ https://bugzilla.mozilla.org/show_bug.cg
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);
const defaultMethods = [{
supportedMethods: "basic-card",
data: {
supportedNetworks: ['unionpay', 'visa', 'mastercard', 'amex', 'discover',
'diners', 'jcb', 'mir',
],
supportedTypes: ['prepaid', 'debit', 'credit'],
@@ -150,18 +154,20 @@ https://bugzilla.mozilla.org/show_bug.cg
}
function updateWithError() {
return new Promise((resolve, reject) => {
resolve(updatedErrorDetails);
});
}
- function testShow() {
+ function testShowWithSuccess() {
+ info("starting testShowWithSuccess");
gScript.sendAsyncMessage("set-normal-ui-service");
+ gScript.sendAsyncMessage("set-complete-status-success");
return new Promise((resolve, reject) => {
const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
payRequest.addEventListener("shippingaddresschange", event => {
is(payRequest.shippingAddress.country, "USA", "payRequest.shippingAddress.country should be 'USA' from event.");
is(payRequest.shippingAddress.addressLine.length, 0, "payRequest.shippingAddress.addressLine.length should be 0 from event.");
is(payRequest.shippingAddress.region, "CA", "payRequest.shippingAddress.region should be 'CA' from event.");
is(payRequest.shippingAddress.city, "San Bruno", "payRequest.shippingAddress.city should be 'San Bruno' from event.");
is(payRequest.shippingAddress.dependentLocality, "Test locality", "payRequest.shippingAddress.dependentLocality should be 'Test locality' from event.");
@@ -213,16 +219,76 @@ https://bugzilla.mozilla.org/show_bug.cg
});
}).catch( e => {
ok(false, "Unexpected error: " + e.name);
resolve();
});
});
}
+ function testShowWithFail() {
+ info("starting testShowWithFail");
+ gScript.sendAsyncMessage("set-dummy-ui-service");
+ gScript.sendAsyncMessage("set-complete-status-fail");
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ payRequest.show().then(response => {
+ response.complete("fail").then(() => {
+ resolve();
+ }).catch(e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ }).catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ });
+ }
+
+ function testShowWithUnknown() {
+ info("starting testShowWithUnknown");
+ gScript.sendAsyncMessage("set-dummy-ui-service");
+ gScript.sendAsyncMessage("set-complete-status-unknown");
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ payRequest.show().then(response => {
+ response.complete("unknown").then(() => {
+ resolve();
+ }).catch(e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ }).catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ });
+ }
+
+ function testShowWithEmpty() {
+ info("starting testShowWithEmpty");
+ gScript.sendAsyncMessage("set-dummy-ui-service");
+ gScript.sendAsyncMessage("set-complete-status-unknown");
+ return new Promise((resolve, reject) => {
+ const payRequest = new PaymentRequest(defaultMethods, defaultDetails, defaultOptions);
+ payRequest.show().then(response => {
+ response.complete().then(() => {
+ resolve();
+ }).catch(e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ }).catch( e => {
+ ok(false, "Unexpected error: " + e.name);
+ resolve();
+ });
+ });
+ }
+
function testCannotMakePaymentShow() {
return new Promise((resolve, reject) => {
const payRequest = new PaymentRequest(nonSupportedMethods, defaultDetails);
payRequest.canMakePayment().then(result => {
ok(!result, "canMakePayment() should return false, but got " + result + ".");
payRequest.show().then( () => {
ok(false, "Should be rejected with 'NotSupportedError', but got resolved");
resolve();
@@ -284,27 +350,31 @@ https://bugzilla.mozilla.org/show_bug.cg
});
gScript.sendAsyncMessage("test-init-data-and-response");
});
}
function teardown() {
gScript.addMessageListener("teardown-complete", function teardownCompleteHandler() {
gScript.removeMessageListener("teardown-complete", teardownCompleteHandler);
- gScript.removeMessageListener("test-fail", testFailHandler)
+ gScript.removeMessageListener("test-fail", testFailHandler);
+ gScript.removeMessageListener("test-pass", testPassHandler);
gScript.destroy();
SimpleTest.finish();
});
gScript.sendAsyncMessage("teardown");
}
function runTests() {
testCannotMakePaymentShow()
.then(testRejectShow)
- .then(testShow)
+ .then(testShowWithSuccess)
+ .then(testShowWithFail)
+ .then(testShowWithUnknown)
+ .then(testShowWithEmpty)
.then(testUpdateWithError)
.then(testNullDetailsResponse)
.then(teardown)
.catch( e => {
ok(false, "Unexpected error: " + e.name);
SimpleTest.finish();
});
}