author | Matthew Noorenberghe <mozilla@noorenberghe.ca> |
Thu, 22 Mar 2018 20:57:13 -0700 | |
changeset 411560 | b045a7779f4ddc2a2dcd93cbfe187d4b880a1f57 |
parent 411559 | 4fa0e33621e398a08243851c72c6b5415decc722 |
child 411561 | 6045035ad2edad1ed5a8c336883db01aa73bd463 |
push id | 101686 |
push user | aciure@mozilla.com |
push date | Tue, 03 Apr 2018 21:59:31 +0000 |
treeherder | mozilla-inbound@8d846598d35d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jaws |
bugs | 1428414 |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- 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/icon128.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;