diff options
Diffstat (limited to 'browser/components/payments/test/mochitest/test_payment_dialog.html')
-rw-r--r-- | browser/components/payments/test/mochitest/test_payment_dialog.html | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/browser/components/payments/test/mochitest/test_payment_dialog.html b/browser/components/payments/test/mochitest/test_payment_dialog.html new file mode 100644 index 0000000000..200c91ec08 --- /dev/null +++ b/browser/components/payments/test/mochitest/test_payment_dialog.html @@ -0,0 +1,360 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test the payment-dialog custom element +--> +<head> + <meta charset="utf-8"> + <title>Test the payment-dialog element</title> + <script src="/tests/SimpleTest/SimpleTest.js"></script> + <script src="/tests/SimpleTest/EventUtils.js"></script> + <script src="sinon-7.2.7.js"></script> + <script src="payments_common.js"></script> + <script src="../../res/unprivileged-fallbacks.js"></script> + <script src="autofillEditForms.js"></script> + + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <link rel="stylesheet" type="text/css" href="../../res/paymentRequest.css"/> + <link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/> +</head> +<body> + <p id="display" style="height: 100vh; margin: 0;"> + <iframe id="templateFrame" src="paymentRequest.xhtml" width="0" height="0" + sandbox="allow-same-origin" + style="float: left;"></iframe> + </p> +<div id="content" style="display: none"> + +</div> +<pre id="test"> +</pre> +<script type="module"> +/** Test the payment-dialog element **/ + +/* global sinon */ + +import PaymentDialog from "../../res/containers/payment-dialog.js"; + +let el1; + +add_task(async function setup_once() { + let templateFrame = document.getElementById("templateFrame"); + await SimpleTest.promiseFocus(templateFrame.contentWindow); + let displayEl = document.getElementById("display"); + importDialogDependencies(templateFrame, displayEl); + + el1 = new PaymentDialog(); + displayEl.appendChild(el1); + + sinon.spy(el1, "render"); + sinon.spy(el1, "stateChangeCallback"); +}); + +async function setup() { + let {request} = el1.requestStore.getState(); + await el1.requestStore.setState({ + changesPrevented: false, + request: Object.assign({}, request, {completeStatus: ""}), + orderDetailsShowing: false, + page: { + id: "payment-summary", + }, + }); + + el1.render.reset(); + el1.stateChangeCallback.reset(); +} + +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_viewAllButtonVisibility() { + await setup(); + + let button = el1._viewAllButton; + ok(button.hidden, "Button is initially hidden when there are no items to show"); + ok(isHidden(button), "Button should be visibly hidden since bug 1469464"); + + // Add a display item. + let request = deepClone(el1.requestStore.getState().request); + request.paymentDetails.displayItems = [ + { + "label": "Triangle", + "amount": { + "currency": "CAD", + "value": "3", + }, + }, + ]; + await el1.requestStore.setState({ request }); + await asyncElementRendered(); + + // Check if the "View all items" button is visible. + ok(!button.hidden, "Button is visible"); +}); + +add_task(async function test_viewAllButton() { + await setup(); + + let elDetails = el1._orderDetailsOverlay; + let button = el1._viewAllButton; + + button.click(); + await asyncElementRendered(); + + ok(el1.stateChangeCallback.calledOnce, "stateChangeCallback called once"); + ok(el1.render.calledOnce, "render called once"); + + let state = el1.requestStore.getState(); + is(state.orderDetailsShowing, true, "orderDetailsShowing becomes true"); + ok(!elDetails.hasAttribute("hidden"), "Check details aren't hidden"); +}); + +add_task(async function test_changesPrevented() { + await setup(); + let state = el1.requestStore.getState(); + is(state.changesPrevented, false, "changesPrevented is initially false"); + let disabledOverlay = document.getElementById("disabled-overlay"); + ok(disabledOverlay.hidden, "Overlay should initially be hidden"); + await el1.requestStore.setState({changesPrevented: true}); + await asyncElementRendered(); + ok(!disabledOverlay.hidden, "Overlay should prevent changes"); +}); + +add_task(async function test_initial_completeStatus() { + await setup(); + let {request, page} = el1.requestStore.getState(); + is(request.completeStatus, "", "completeStatus is initially empty"); + + let payButton = document.getElementById("pay"); + is(payButton, document.querySelector(`#${page.id} button.primary`), + "Primary button is the pay button in the initial state"); + is(payButton.textContent, "Pay", "Check default label"); + ok(payButton.disabled, "Button is disabled by default"); +}); + +add_task(async function test_generic_errors() { + await setup(); + const SHIPPING_GENERIC_ERROR = "Can't ship to that address"; + el1._errorText.dataset.shippingGenericError = SHIPPING_GENERIC_ERROR; + el1.requestStore.setState({ + savedAddresses: { + "48bnds6854t": { + "address-level1": "MI", + "address-level2": "Some City", + "country": "US", + "guid": "48bnds6854t", + "name": "Mr. Foo", + "postal-code": "90210", + "street-address": "123 Sesame Street,\nApt 40", + "tel": "+1 519 555-5555", + }, + "68gjdh354j": { + "address-level1": "CA", + "address-level2": "Mountain View", + "country": "US", + "guid": "68gjdh354j", + "name": "Mrs. Bar", + "postal-code": "94041", + "street-address": "P.O. Box 123", + "tel": "+1 650 555-5555", + }, + }, + selectedShippingAddress: "48bnds6854t", + }); + await asyncElementRendered(); + + let picker = el1._shippingAddressPicker; + ok(picker.selectedOption, "Address picker should have a selected option"); + is(el1._errorText.textContent, SHIPPING_GENERIC_ERROR, + "Generic error message should be shown when no shipping options or error are provided"); +}); + +add_task(async function test_processing_completeStatus() { + // "processing": has overlay. Check button visibility + await setup(); + let {request} = el1.requestStore.getState(); + // this a transition state, set when waiting for a response from the merchant page + el1.requestStore.setState({ + changesPrevented: true, + request: Object.assign({}, request, {completeStatus: "processing"}), + }); + await asyncElementRendered(); + + let primaryButtons = document.querySelectorAll("footer button.primary"); + ok(Array.from(primaryButtons).every(el => isHidden(el) || el.disabled), + "all primary footer buttons are hidden or disabled"); + + info("Got an update from the parent process with an error from .retry()"); + request = el1.requestStore.getState().request; + let paymentDetails = deepClone(request.paymentDetails); + paymentDetails.error = "Sample retry error"; + await el1.setStateFromParent({ + request: Object.assign({}, request, { + completeStatus: "", + paymentDetails, + }), + }); + await asyncElementRendered(); + + let {changesPrevented, page} = el1.requestStore.getState(); + ok(!changesPrevented, "Changes should no longer be prevented"); + is(page.id, "payment-summary", "Check back on payment-summary"); + ok(el1.innerText.includes("Sample retry error"), "Check error text is visible"); +}); + +add_task(async function test_success_unknown_completeStatus() { + // in the "success" and "unknown" completion states the dialog would normally be closed + // so just ensure it is left in a good state + for (let completeStatus of ["success", "unknown"]) { + await setup(); + let {request} = el1.requestStore.getState(); + el1.requestStore.setState({ + request: Object.assign({}, request, {completeStatus}), + }); + await asyncElementRendered(); + + let {page} = el1.requestStore.getState(); + + // this status doesnt change page + let payButton = document.getElementById("pay"); + is(payButton, document.querySelector(`#${page.id} button.primary`), + `Primary button is the pay button in the ${completeStatus} state`); + + if (completeStatus == "success") { + is(payButton.textContent, "Done", "Check button label"); + } + if (completeStatus == "unknown") { + is(payButton.textContent, "Unknown", "Check button label"); + } + ok(payButton.disabled, "Button is disabled by default"); + } +}); + +add_task(async function test_timeout_fail_completeStatus() { + // in these states the dialog stays open and presents a single + // button for acknowledgement + for (let completeStatus of ["fail", "timeout"]) { + await setup(); + let {request} = el1.requestStore.getState(); + el1.requestStore.setState({ + request: Object.assign({}, request, {completeStatus}), + page: { + id: `completion-${completeStatus}-error`, + }, + }); + await asyncElementRendered(); + + let {page} = el1.requestStore.getState(); + let pageElem = document.querySelector(`#${page.id}`); + let payButton = document.getElementById("pay"); + let primaryButton = pageElem.querySelector("button.primary"); + + ok(pageElem && !isHidden(pageElem, `page element for ${page.id} exists and is visible`)); + ok(!isHidden(primaryButton), "Primary button is visible"); + ok(payButton != primaryButton, + `Primary button is the not pay button in the ${completeStatus} state`); + ok(isHidden(payButton), "Pay button is not visible"); + is(primaryButton.textContent, "Close", "Check button label"); + + let rect = primaryButton.getBoundingClientRect(); + let visibleElement = + document.elementFromPoint(rect.x + rect.width / 2, rect.y + rect.height / 2); + ok(primaryButton === visibleElement, "Primary button is on top of the overlay"); + } +}); + +add_task(async function test_scrollPaymentRequestPage() { + await setup(); + info("making the payment-dialog container small to require scrolling"); + el1.parentElement.style.height = "100px"; + let summaryPageBody = document.querySelector("#payment-summary .page-body"); + is(summaryPageBody.scrollTop, 0, "Page body not scrolled initially"); + let securityCodeInput = summaryPageBody.querySelector("payment-method-picker input"); + securityCodeInput.focus(); + await new Promise(resolve => SimpleTest.executeSoon(resolve)); + ok(summaryPageBody.scrollTop > 0, "Page body scrolled after focusing the CVV field"); + el1.parentElement.style.height = ""; +}); + +add_task(async function test_acceptedCards() { + let initialState = el1.requestStore.getState(); + let paymentMethods = [{ + supportedMethods: "basic-card", + data: { + supportedNetworks: ["visa", "mastercard"], + }, + }]; + el1.requestStore.setState({ + request: Object.assign({}, initialState.request, { + paymentMethods, + }), + }); + await asyncElementRendered(); + + let acceptedCards = el1._acceptedCardsList; + ok(acceptedCards && !isHidden(acceptedCards), "Accepted cards list is present and visible"); + + paymentMethods = [{ + supportedMethods: "basic-card", + }]; + el1.requestStore.setState({ + request: Object.assign({}, initialState.request, { + paymentMethods, + }), + }); + await asyncElementRendered(); + + acceptedCards = el1._acceptedCardsList; + ok(acceptedCards && isHidden(acceptedCards), "Accepted cards list is present but hidden"); +}); + +add_task(async function test_picker_labels() { + await setup(); + let picker = el1._shippingOptionPicker; + + const SHIPPING_OPTIONS_LABEL = "Shipping options"; + const DELIVERY_OPTIONS_LABEL = "Delivery options"; + const PICKUP_OPTIONS_LABEL = "Pickup options"; + picker.dataset.shippingOptionsLabel = SHIPPING_OPTIONS_LABEL; + picker.dataset.deliveryOptionsLabel = DELIVERY_OPTIONS_LABEL; + picker.dataset.pickupOptionsLabel = PICKUP_OPTIONS_LABEL; + + for (let [shippingType, label] of [ + ["shipping", SHIPPING_OPTIONS_LABEL], + ["delivery", DELIVERY_OPTIONS_LABEL], + ["pickup", PICKUP_OPTIONS_LABEL], + ]) { + let request = deepClone(el1.requestStore.getState().request); + request.paymentOptions.requestShipping = true; + request.paymentOptions.shippingType = shippingType; + await el1.requestStore.setState({ request }); + await asyncElementRendered(); + is(picker.labelElement.textContent, label, + `Label should be appropriate for ${shippingType}`); + } +}); + +add_task(async function test_disconnect() { + await setup(); + + el1.remove(); + await el1.requestStore.setState({orderDetailsShowing: true}); + await asyncElementRendered(); + ok(el1.stateChangeCallback.notCalled, "stateChangeCallback not called"); + ok(el1.render.notCalled, "render not called"); + + let elDetails = el1._orderDetailsOverlay; + ok(elDetails.hasAttribute("hidden"), "details overlay remains hidden"); +}); +</script> + +</body> +</html> |