fune/browser/components/payments/test/mochitest/test_payment_dialog.html

361 lines
12 KiB
HTML

<!DOCTYPE HTML>
<html>
<!--
Test the payment-dialog custom element
-->
<head>
<meta charset="utf-8">
<title>Test the payment-dialog element</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/AddTask.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script src="sinon-2.3.2.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>