forked from mirrors/gecko-dev
Differential Revision: https://phabricator.services.mozilla.com/D22046 --HG-- extra : moz-landing-system : lando
361 lines
12 KiB
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-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>
|