summaryrefslogtreecommitdiffstats
path: root/browser/components/payments/test/mochitest/test_payment_dialog.html
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/payments/test/mochitest/test_payment_dialog.html')
-rw-r--r--browser/components/payments/test/mochitest/test_payment_dialog.html360
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>