--- a/browser/installer/allowed-dupes.mn
+++ b/browser/installer/allowed-dupes.mn
@@ -142,8 +142,13 @@ res/table-remove-column.gif
res/table-remove-row-active.gif
res/table-remove-row-hover.gif
res/table-remove-row.gif
res/multilocale.txt
update.locale
# Aurora branding
browser/chrome/browser/content/branding/icon64.png
browser/chrome/devtools/content/framework/dev-edition-promo/dev-edition-logo.png
+# Bug 1451016 - Nightly-only PaymentRequest & Form Autofill code sharing.
+browser/features/formautofill@mozilla.org/chrome/content/editCreditCard.xhtml
+chrome/toolkit/res/payments/formautofill/editCreditCard.xhtml
+browser/features/formautofill@mozilla.org/chrome/content/autofillEditForms.js
+chrome/toolkit/res/payments/formautofill/autofillEditForms.js
--- a/toolkit/components/payments/content/paymentDialogFrameScript.js
+++ b/toolkit/components/payments/content/paymentDialogFrameScript.js
@@ -16,16 +16,19 @@
*/
"use strict";
/* eslint-env mozilla/frame-script */
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
+ChromeUtils.defineModuleGetter(this, "FormAutofillUtils",
+ "resource://formautofill/FormAutofillUtils.jsm");
+
let PaymentFrameScript = {
init() {
XPCOMUtils.defineLazyGetter(this, "log", () => {
let {ConsoleAPI} = ChromeUtils.import("resource://gre/modules/Console.jsm", {});
return new ConsoleAPI({
maxLogLevelPref: "dom.payments.loglevel",
prefix: "paymentDialogFrameScript",
});
@@ -53,20 +56,36 @@ let PaymentFrameScript = {
let contentLogObject = Cu.waiveXrays(content).log;
for (let name of ["error", "warn", "info", "debug"]) {
Cu.exportFunction(privilegedLogger[name].bind(privilegedLogger), contentLogObject, {
defineAs: name,
});
}
},
+ /**
+ * Expose privileged utility functions to the unprivileged page.
+ */
+ exposeUtilityFunctions() {
+ let PaymentDialogUtils = {
+ isCCNumber(value) {
+ return FormAutofillUtils.isCCNumber(value);
+ },
+ };
+ let waivedContent = Cu.waiveXrays(content);
+ waivedContent.PaymentDialogUtils = Cu.cloneInto(PaymentDialogUtils, waivedContent, {
+ cloneFunctions: true,
+ });
+ },
+
sendToChrome({detail}) {
let {messageType} = detail;
if (messageType == "initializeRequest") {
this.setupContentConsole();
+ this.exposeUtilityFunctions();
}
this.log.debug("sendToChrome:", messageType, detail);
this.sendMessageToChrome(messageType, detail);
},
sendToContent(messageType, detail = {}) {
this.log.debug("sendToContent", messageType, detail);
let response = Object.assign({messageType}, detail);
--- a/toolkit/components/payments/jar.mn
+++ b/toolkit/components/payments/jar.mn
@@ -13,12 +13,14 @@ toolkit.jar:
res/payments (res/paymentRequest.*)
res/payments/components/ (res/components/*.css)
res/payments/components/ (res/components/*.js)
res/payments/containers/ (res/containers/*.js)
res/payments/containers/ (res/containers/*.css)
res/payments/debugging.css (res/debugging.css)
res/payments/debugging.html (res/debugging.html)
res/payments/debugging.js (res/debugging.js)
- res/payments/log.js (res/log.js)
+ res/payments/formautofill/autofillEditForms.js (../../../../browser/extensions/formautofill/content/autofillEditForms.js)
+ res/payments/formautofill/editCreditCard.xhtml (../../../../browser/extensions/formautofill/content/editCreditCard.xhtml)
+ res/payments/unprivileged-fallbacks.js (res/unprivileged-fallbacks.js)
res/payments/mixins/ (res/mixins/*.js)
res/payments/PaymentsStore.js (res/PaymentsStore.js)
res/payments/vendor/ (res/vendor/*)
--- a/toolkit/components/payments/moz.build
+++ b/toolkit/components/payments/moz.build
@@ -11,17 +11,20 @@ with Files('**'):
EXTRA_COMPONENTS += [
'payments.manifest',
'paymentUIService.js',
]
JAR_MANIFESTS += ['jar.mn']
-MOCHITEST_MANIFESTS += ['test/mochitest/mochitest.ini']
+MOCHITEST_MANIFESTS += [
+ 'test/mochitest/formautofill/mochitest.ini',
+ 'test/mochitest/mochitest.ini',
+]
SPHINX_TREES['docs'] = 'docs'
with Files('docs/**'):
SCHEDULES.exclusive = ['docs']
TESTING_JS_MODULES += [
'test/PaymentTestUtils.jsm',
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/res/containers/basic-card-form.js
@@ -0,0 +1,105 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* import-globals-from ../../../../../browser/extensions/formautofill/content/autofillEditForms.js*/
+/* import-globals-from ../mixins/PaymentStateSubscriberMixin.js */
+/* import-globals-from ../unprivileged-fallbacks.js */
+
+"use strict";
+
+/**
+ * <basic-card-form></basic-card-form>
+ *
+ * XXX: Bug 1446164 - This form isn't localized when used via this custom element
+ * as it will be much easier to share the logic once we switch to Fluent.
+ */
+
+class BasicCardForm extends PaymentStateSubscriberMixin(HTMLElement) {
+ constructor() {
+ super();
+
+ this.backButton = document.createElement("button");
+ this.backButton.addEventListener("click", this);
+
+ // The markup is shared with form autofill preferences.
+ let url = "formautofill/editCreditCard.xhtml";
+ this.promiseReady = this._fetchMarkup(url).then(doc => {
+ this.form = doc.getElementById("form");
+ return this.form;
+ });
+ }
+
+ _fetchMarkup(url) {
+ return new Promise((resolve, reject) => {
+ let xhr = new XMLHttpRequest();
+ xhr.responseType = "document";
+ xhr.addEventListener("error", reject);
+ xhr.addEventListener("load", evt => {
+ resolve(xhr.response);
+ });
+ xhr.open("GET", url);
+ xhr.send();
+ });
+ }
+
+ connectedCallback() {
+ this.promiseReady.then(form => {
+ this.appendChild(form);
+
+ let record = {};
+ this.formHandler = new EditCreditCard({
+ form,
+ }, record, {
+ isCCNumber: PaymentDialogUtils.isCCNumber,
+ });
+
+ this.appendChild(this.backButton);
+ // Only call the connected super callback(s) once our markup is fully
+ // connected, including the shared form fetched asynchronously.
+ super.connectedCallback();
+ });
+ }
+
+ render(state) {
+ this.backButton.textContent = this.dataset.backButtonLabel;
+
+ let record = {};
+ let {
+ selectedPaymentCard,
+ savedBasicCards,
+ } = state;
+
+ let editing = !!state.selectedPaymentCard;
+ this.form.querySelector("#cc-number").disabled = editing;
+
+ // If a card is selected we want to edit it.
+ if (editing) {
+ record = savedBasicCards[selectedPaymentCard];
+ if (!record) {
+ throw new Error("Trying to edit a non-existing card: " + selectedPaymentCard);
+ }
+ }
+
+ this.formHandler.loadRecord(record);
+ }
+
+ handleEvent(event) {
+ switch (event.type) {
+ case "click": {
+ this.onClick(event);
+ break;
+ }
+ }
+ }
+
+ onClick(evt) {
+ this.requestStore.setState({
+ page: {
+ id: "payment-summary",
+ },
+ });
+ }
+}
+
+customElements.define("basic-card-form", BasicCardForm);
--- a/toolkit/components/payments/res/containers/payment-dialog.js
+++ b/toolkit/components/payments/res/containers/payment-dialog.js
@@ -25,17 +25,19 @@ class PaymentDialog extends PaymentState
this._cancelButton.addEventListener("click", this.cancelRequest);
this._payButton = contents.querySelector("#pay");
this._payButton.addEventListener("click", this);
this._viewAllButton = contents.querySelector("#view-all");
this._viewAllButton.addEventListener("click", this);
+ this._mainContainer = contents.getElementById("main-container");
this._orderDetailsOverlay = contents.querySelector("#order-details-overlay");
+
this._shippingTypeLabel = contents.querySelector("#shipping-type-label");
this._shippingRelatedEls = contents.querySelectorAll(".shipping-related");
this._payerRelatedEls = contents.querySelectorAll(".payer-related");
this._payerAddressPicker = contents.querySelector("address-picker.payer-related");
this._errorText = contents.querySelector("#error-text");
this._disabledOverlay = contents.getElementById("disabled-overlay");
@@ -241,16 +243,20 @@ class PaymentDialog extends PaymentState
}
let shippingType = paymentOptions.shippingType || "shipping";
this._shippingTypeLabel.querySelector("label").textContent =
this._shippingTypeLabel.dataset[shippingType + "AddressLabel"];
this._renderPayButton(state);
+ for (let page of this._mainContainer.querySelectorAll(":scope > .page")) {
+ page.hidden = state.page.id != page.id;
+ }
+
let {
changesPrevented,
completionState,
} = state;
if (changesPrevented) {
this.setAttribute("changes-prevented", "");
} else {
this.removeAttribute("changes-prevented");
--- a/toolkit/components/payments/res/containers/payment-method-picker.js
+++ b/toolkit/components/payments/res/containers/payment-method-picker.js
@@ -3,36 +3,47 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* global PaymentStateSubscriberMixin */
"use strict";
/**
* <payment-method-picker></payment-method-picker>
- * Container around <rich-select> (eventually providing add/edit links) with
+ * Container around add/edit links and <rich-select> with
* <basic-card-option> listening to savedBasicCards.
*/
class PaymentMethodPicker extends PaymentStateSubscriberMixin(HTMLElement) {
constructor() {
super();
this.dropdown = document.createElement("rich-select");
this.dropdown.addEventListener("change", this);
this.spacerText = document.createTextNode(" ");
this.securityCodeInput = document.createElement("input");
this.securityCodeInput.autocomplete = "off";
this.securityCodeInput.size = 3;
this.securityCodeInput.addEventListener("change", this);
+ this.addLink = document.createElement("a");
+ this.addLink.href = "javascript:void(0)";
+ this.addLink.textContent = this.dataset.addLinkLabel;
+ this.addLink.addEventListener("click", this);
+ this.editLink = document.createElement("a");
+ this.editLink.href = "javascript:void(0)";
+ this.editLink.textContent = this.dataset.editLinkLabel;
+ this.editLink.addEventListener("click", this);
}
connectedCallback() {
this.appendChild(this.dropdown);
this.appendChild(this.spacerText);
this.appendChild(this.securityCodeInput);
+ this.appendChild(this.addLink);
+ this.append(" ");
+ this.appendChild(this.editLink);
super.connectedCallback();
}
render(state) {
let {savedBasicCards} = state;
let desiredOptions = [];
for (let [guid, basicCard] of Object.entries(savedBasicCards)) {
let optionEl = this.dropdown.getOptionByValue(guid);
@@ -69,16 +80,20 @@ class PaymentMethodPicker extends Paymen
}
handleEvent(event) {
switch (event.type) {
case "change": {
this.onChange(event);
break;
}
+ case "click": {
+ this.onClick(event);
+ break;
+ }
}
}
onChange({target}) {
let selectedKey = this.selectedStateKey;
let stateChange = {};
if (!selectedKey) {
@@ -99,11 +114,34 @@ class PaymentMethodPicker extends Paymen
}
default: {
return;
}
}
this.requestStore.setState(stateChange);
}
+
+ onClick({target}) {
+ let nextState = {
+ page: {
+ id: "basic-card-page",
+ },
+ };
+
+ switch (target) {
+ case this.addLink: {
+ nextState.selectedPaymentCard = null;
+ break;
+ }
+ case this.editLink: {
+ break;
+ }
+ default: {
+ throw new Error("Unexpected onClick");
+ }
+ }
+
+ this.requestStore.setState(nextState);
+ }
}
customElements.define("payment-method-picker", PaymentMethodPicker);
--- a/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js
+++ b/toolkit/components/payments/res/mixins/PaymentStateSubscriberMixin.js
@@ -12,16 +12,19 @@
/**
* State of the payment request dialog.
*/
let requestStore = new PaymentsStore({
changesPrevented: false,
completionState: "initial",
orderDetailsShowing: false,
+ page: {
+ id: "payment-summary",
+ },
request: {
tabId: null,
topLevelPrincipal: {URI: {displayHost: null}},
requestId: null,
paymentMethods: [],
paymentDetails: {
id: null,
totalItem: {label: null, amount: {currency: null, value: 0}},
--- a/toolkit/components/payments/res/paymentRequest.css
+++ b/toolkit/components/payments/res/paymentRequest.css
@@ -8,16 +8,20 @@ html {
}
body {
height: 100%;
margin: 0;
overflow: hidden;
}
+[hidden] {
+ display: none !important;
+}
+
#debugging-console {
float: right;
/* Float above the other overlays */
position: relative;
z-index: 99;
}
payment-dialog {
--- a/toolkit/components/payments/res/paymentRequest.js
+++ b/toolkit/components/payments/res/paymentRequest.js
@@ -3,17 +3,17 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* Loaded in the unprivileged frame of each payment dialog.
*
* Communicates with privileged code via DOM Events.
*/
-/* import-globals-from log.js */
+/* import-globals-from unprivileged-fallbacks.js */
"use strict";
var paymentRequest = {
domReadyPromise: null,
init() {
// listen to content
--- a/toolkit/components/payments/res/paymentRequest.xhtml
+++ b/toolkit/components/payments/res/paymentRequest.xhtml
@@ -1,62 +1,74 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html [
+ <!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
+ %globalDTD;
+
<!ENTITY viewAllItems "View All Items">
<!ENTITY paymentSummaryTitle "Your Payment">
<!ENTITY shippingAddressLabel "Shipping Address">
<!ENTITY deliveryAddressLabel "Delivery Address">
<!ENTITY pickupAddressLabel "Pickup Address">
<!ENTITY shippingOptionsLabel "Shipping Options">
<!ENTITY paymentMethodsLabel "Payment Method">
+ <!ENTITY basicCard.addLink.label "Add">
+ <!ENTITY basicCard.editLink.label "Edit">
<!ENTITY payerLabel "Contact Information">
<!ENTITY cancelPaymentButton.label "Cancel">
<!ENTITY approvePaymentButton.label "Pay">
<!ENTITY processingPaymentButton.label "Processing">
<!ENTITY successPaymentButton.label "Done">
<!ENTITY failPaymentButton.label "Fail">
<!ENTITY unknownPaymentButton.label "Unknown">
<!ENTITY orderDetailsLabel "Order Details">
<!ENTITY orderTotalLabel "Total">
+ <!ENTITY basicCardPage.backButton.label "Back">
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
- <meta http-equiv="Content-Security-Policy" content="default-src 'self'"/>
- <title></title>
+ <title>&paymentSummaryTitle;</title>
+
+ <!-- chrome: is needed for global.dtd -->
+ <meta http-equiv="Content-Security-Policy" content="default-src 'self' chrome:"/>
+
<link rel="stylesheet" href="paymentRequest.css"/>
<link rel="stylesheet" href="components/rich-select.css"/>
<link rel="stylesheet" href="components/address-option.css"/>
<link rel="stylesheet" href="components/basic-card-option.css"/>
<link rel="stylesheet" href="components/shipping-option.css"/>
<link rel="stylesheet" href="components/payment-details-item.css"/>
<link rel="stylesheet" href="containers/order-details.css"/>
<script src="vendor/custom-elements.min.js"></script>
- <script src="log.js"></script>
+ <script src="unprivileged-fallbacks.js"></script>
<script src="PaymentsStore.js"></script>
<script src="mixins/ObservedPropertiesMixin.js"></script>
<script src="mixins/PaymentStateSubscriberMixin.js"></script>
+ <script src="formautofill/autofillEditForms.js"></script>
+
<script src="components/currency-amount.js"></script>
<script src="containers/order-details.js"></script>
<script src="components/payment-details-item.js"></script>
<script src="components/rich-select.js"></script>
<script src="components/rich-option.js"></script>
<script src="components/address-option.js"></script>
<script src="components/shipping-option.js"></script>
<script src="containers/address-picker.js"></script>
<script src="components/basic-card-option.js"></script>
<script src="containers/shipping-option-picker.js"></script>
<script src="containers/payment-method-picker.js"></script>
+ <script src="containers/basic-card-form.js"></script>
<script src="containers/payment-dialog.js"></script>
<script src="paymentRequest.js"></script>
<template id="payment-dialog-template">
<header>
<div id="total">
<h2 class="label"></h2>
@@ -64,34 +76,37 @@
<div id="host-name"></div>
</div>
<div id="top-buttons" >
<button id="view-all" class="closed">&viewAllItems;</button>
</div>
</header>
<div id="main-container">
- <section id="payment-summary">
+ <section id="payment-summary" class="page">
<h1>&paymentSummaryTitle;</h1>
<section>
<div id="error-text"></div>
<div class="shipping-related"
id="shipping-type-label"
data-shipping-address-label="&shippingAddressLabel;"
data-delivery-address-label="&deliveryAddressLabel;"
data-pickup-address-label="&pickupAddressLabel;"><label></label></div>
<address-picker class="shipping-related" selected-state-key="selectedShippingAddress"></address-picker>
<div class="shipping-related"><label>&shippingOptionsLabel;</label></div>
<shipping-option-picker class="shipping-related"></shipping-option-picker>
<div><label>&paymentMethodsLabel;</label></div>
- <payment-method-picker selected-state-key="selectedPaymentCard"></payment-method-picker>
+ <payment-method-picker selected-state-key="selectedPaymentCard"
+ data-add-link-label="&basicCard.addLink.label;"
+ data-edit-link-label="&basicCard.editLink.label;">
+ </payment-method-picker>
<div class="payer-related"><label>&payerLabel;</label></div>
<address-picker class="payer-related"
selected-state-key="selectedPayerAddress"></address-picker>
<div id="error-text"></div>
</section>
<footer id="controls-container">
@@ -103,16 +118,21 @@
data-unknown-label="&unknownPaymentButton.label;"
data-success-label="&successPaymentButton.label;"></button>
</footer>
</section>
<section id="order-details-overlay" hidden="hidden">
<h1>&orderDetailsLabel;</h1>
<order-details></order-details>
</section>
+
+ <basic-card-form id="basic-card-page"
+ class="page"
+ data-back-button-label="&basicCardPage.backButton.label;"
+ hidden="hidden"></basic-card-form>
</div>
<div id="disabled-overlay" hidden="hidden">
<!-- overlay to prevent changes while waiting for a response from the merchant -->
</div>
</template>
<template id="order-details-template">
@@ -120,16 +140,16 @@
<ul class="footer-items-list"></ul>
<div class="details-total">
<h2 class="label">&orderTotalLabel;</h2>
<currency-amount></currency-amount>
</div>
</template>
</head>
-<body>
+<body dir="&locale.dir;">
<iframe id="debugging-console"
hidden="hidden"
height="400"
src="debugging.html"></iframe>
<payment-dialog></payment-dialog>
</body>
</html>
rename from toolkit/components/payments/res/log.js
rename to toolkit/components/payments/res/unprivileged-fallbacks.js
--- a/toolkit/components/payments/res/log.js
+++ b/toolkit/components/payments/res/unprivileged-fallbacks.js
@@ -1,20 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
- * This file defines a fallback log object to be used during development outside
+ * This file defines fallback objects to be used during development outside
* of the paymentDialogWrapper. When loaded in the wrapper, a frame script
- * providing pref-controlled logging overwrites these methods.
+ * overwrites these methods.
*/
/* eslint-disable no-console */
-/* exported log */
+/* exported log, PaymentDialogUtils */
"use strict";
var log = {
error(...args) {
console.error("log.js", ...args);
},
warn(...args) {
@@ -22,8 +22,14 @@ var log = {
},
info(...args) {
console.info("log.js", ...args);
},
debug(...args) {
console.debug("log.js", ...args);
},
};
+
+var PaymentDialogUtils = {
+ isCCNumber(str) {
+ return str.length > 0;
+ },
+};
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/test/mochitest/formautofill/mochitest.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+# This manifest mostly exists so that the support-files below can be referenced
+# from a relative path of formautofill/* from the tests in the above directory
+# to resemble the layout in the shipped JAR file.
+support-files =
+ ../../../../../../browser/extensions/formautofill/content/editCreditCard.xhtml
+
+[test_editCreditCard.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/test/mochitest/formautofill/test_editCreditCard.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that editCreditCard.xhtml is accessible for tests in the parent directory.
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test that editCreditCard.xhtml is accessible</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <p id="display">
+ <iframe id="editCreditCard" src="editCreditCard.xhtml"></iframe>
+ </p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+add_task(async function test_editCreditCard() {
+ let editCreditCard = document.getElementById("editCreditCard").contentWindow;
+ await SimpleTest.promiseFocus(editCreditCard);
+ ok(editCreditCard.document.getElementById("form"), "Check form is present");
+ ok(editCreditCard.document.getElementById("cc-number"), "Check cc-number is present");
+});
+
+</script>
+
+</body>
+</html>
--- a/toolkit/components/payments/test/mochitest/mochitest.ini
+++ b/toolkit/components/payments/test/mochitest/mochitest.ini
@@ -1,40 +1,44 @@
[DEFAULT]
prefs =
dom.webcomponents.customelements.enabled=false
support-files =
+ ../../../../../browser/extensions/formautofill/content/autofillEditForms.js
../../../../../testing/modules/sinon-2.3.2.js
../../res/paymentRequest.css
../../res/paymentRequest.xhtml
../../res/PaymentsStore.js
+ ../../res/unprivileged-fallbacks.js
../../res/components/currency-amount.js
../../res/components/address-option.js
../../res/components/address-option.css
../../res/components/basic-card-option.js
../../res/components/basic-card-option.css
../../res/components/payment-details-item.js
../../res/components/rich-option.js
../../res/components/rich-select.css
../../res/components/rich-select.js
../../res/components/shipping-option.js
../../res/components/shipping-option.css
../../res/containers/address-picker.js
+ ../../res/containers/basic-card-form.js
../../res/containers/shipping-option-picker.js
../../res/containers/order-details.js
../../res/containers/payment-dialog.js
../../res/containers/payment-method-picker.js
../../res/mixins/ObservedPropertiesMixin.js
../../res/mixins/PaymentStateSubscriberMixin.js
../../res/vendor/custom-elements.min.js
../../res/vendor/custom-elements.min.js.map
payments_common.js
skip-if = !e10s
[test_address_picker.html]
+[test_basic_card_form.html]
[test_currency_amount.html]
[test_order_details.html]
[test_payer_address_picker.html]
[test_payment_dialog.html]
[test_payment_details_item.html]
[test_payment_method_picker.html]
[test_rich_select.html]
[test_shipping_option_picker.html]
--- a/toolkit/components/payments/test/mochitest/payments_common.js
+++ b/toolkit/components/payments/test/mochitest/payments_common.js
@@ -1,11 +1,14 @@
"use strict";
-/* exported asyncElementRendered, promiseStateChange, deepClone */
+/* exported asyncElementRendered, promiseStateChange, deepClone, PTU */
+
+const PTU = SpecialPowers.Cu.import("resource://testing-common/PaymentTestUtils.jsm", {})
+ .PaymentTestUtils;
/**
* A helper to await on while waiting for an asynchronous rendering of a Custom
* Element.
* @returns {Promise}
*/
function asyncElementRendered() {
return Promise.resolve();
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/test/mochitest/test_basic_card_form.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test the basic-card-form element
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test the basic-card-form element</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="application/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+ <script src="sinon-2.3.2.js"></script>
+ <script src="payments_common.js"></script>
+ <script src="custom-elements.min.js"></script>
+ <script src="unprivileged-fallbacks.js"></script>
+ <script src="PaymentsStore.js"></script>
+ <script src="PaymentStateSubscriberMixin.js"></script>
+ <script src="autofillEditForms.js"></script>
+ <script src="basic-card-form.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" type="text/css" href="paymentRequest.css"/>
+</head>
+<body>
+ <p id="display">
+ </p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+/** Test the basic-card-form element **/
+
+/* global sinon */
+/* import-globals-from payments_common.js */
+/* import-globals-from ../../res/mixins/PaymentStateSubscriberMixin.js */
+
+let display = document.getElementById("display");
+
+function checkCCForm(customEl, expectedCard) {
+ const CC_PROPERTY_NAMES = [
+ "cc-number",
+ "cc-name",
+ "cc-exp-month",
+ "cc-exp-year",
+ ];
+ for (let propName of CC_PROPERTY_NAMES) {
+ let expectedVal = expectedCard[propName] || "";
+ is(document.getElementById(propName).value,
+ expectedVal.toString(),
+ `Check ${propName}`);
+ }
+}
+
+add_task(async function test_initialState() {
+ let form = document.createElement("basic-card-form");
+ let {page} = form.requestStore.getState();
+ is(page.id, "payment-summary", "Check initial page");
+ await form.promiseReady;
+ display.appendChild(form);
+ await asyncElementRendered();
+ is(page.id, "payment-summary", "Check initial page after appending");
+ form.remove();
+});
+
+add_task(async function test_backButton() {
+ let form = document.createElement("basic-card-form");
+ form.dataset.backButtonLabel = "Back";
+ await form.requestStore.setState({
+ page: {
+ id: "test-page",
+ },
+ });
+ await form.promiseReady;
+ display.appendChild(form);
+ await asyncElementRendered();
+
+ let stateChangePromise = promiseStateChange(form.requestStore);
+ is(form.backButton.textContent, "Back", "Check label");
+ synthesizeMouseAtCenter(form.backButton, {});
+
+ let {page} = await stateChangePromise;
+ is(page.id, "payment-summary", "Check initial page after appending");
+
+ form.remove();
+});
+
+add_task(async function test_record() {
+ let form = document.createElement("basic-card-form");
+ await form.promiseReady;
+ display.appendChild(form);
+ await asyncElementRendered();
+
+ info("test year before current");
+ let card1 = deepClone(PTU.BasicCards.JohnDoe);
+ card1.guid = "9864798564";
+ card1["cc-exp-year"] = 2011;
+
+ await form.requestStore.setState({
+ selectedPaymentCard: card1.guid,
+ savedBasicCards: {
+ [card1.guid]: deepClone(card1),
+ },
+ });
+ await asyncElementRendered();
+ checkCCForm(form, card1);
+
+ info("test future year");
+ card1["cc-exp-year"] = 2100;
+
+ await form.requestStore.setState({
+ savedBasicCards: {
+ [card1.guid]: deepClone(card1),
+ },
+ });
+ await asyncElementRendered();
+ checkCCForm(form, card1);
+
+ info("test change to minimal record");
+ let minimalCard = {
+ // no expiration date or name
+ "cc-number": "1234567690123",
+ guid: "9gnjdhen46",
+ };
+ await form.requestStore.setState({
+ selectedPaymentCard: minimalCard.guid,
+ savedBasicCards: {
+ [minimalCard.guid]: deepClone(minimalCard),
+ },
+ });
+ await asyncElementRendered();
+ checkCCForm(form, minimalCard);
+
+ info("change to no selected card");
+ await form.requestStore.setState({
+ selectedPaymentCard: null,
+ });
+ await asyncElementRendered();
+ checkCCForm(form, {});
+
+ form.remove();
+});
+</script>
+
+</body>
+</html>
--- a/toolkit/components/payments/test/mochitest/test_payment_dialog.html
+++ b/toolkit/components/payments/test/mochitest/test_payment_dialog.html
@@ -82,16 +82,17 @@ async function setup() {
add_task(async function test_initialState() {
await setup();
let initialState = el1.requestStore.getState();
let elDetails = el1._orderDetailsOverlay;
is(initialState.orderDetailsShowing, false, "orderDetailsShowing is initially false");
ok(elDetails.hasAttribute("hidden"), "Check details are hidden");
+ is(initialState.page.id, "payment-summary", "Check initial page");
});
add_task(async function test_viewAllButton() {
await setup();
let elDetails = el1._orderDetailsOverlay;
let button = el1._viewAllButton;