mirror of
https://github.com/Feodor2/Mypal68.git
synced 2025-06-18 14:55:44 -04:00
613 lines
17 KiB
JavaScript
613 lines
17 KiB
JavaScript
"use strict";
|
|
|
|
/* global info */
|
|
|
|
var EXPORTED_SYMBOLS = ["PaymentTestUtils"];
|
|
|
|
var PaymentTestUtils = {
|
|
/**
|
|
* Common content tasks functions to be used with ContentTask.spawn.
|
|
*/
|
|
ContentTasks: {
|
|
/* eslint-env mozilla/frame-script */
|
|
/**
|
|
* Add a completion handler to the existing `showPromise` to call .complete().
|
|
* @returns {Object} representing the PaymentResponse
|
|
*/
|
|
addCompletionHandler: async ({ result, delayMs = 0 }) => {
|
|
let response = await content.showPromise;
|
|
let completeException;
|
|
|
|
// delay the given # milliseconds
|
|
await new Promise(resolve => content.setTimeout(resolve, delayMs));
|
|
|
|
try {
|
|
await response.complete(result);
|
|
} catch (ex) {
|
|
info(`Complete error: ${ex}`);
|
|
completeException = {
|
|
name: ex.name,
|
|
message: ex.message,
|
|
};
|
|
}
|
|
return {
|
|
completeException,
|
|
response: response.toJSON(),
|
|
// XXX: Bug NNN: workaround for `details` not being included in `toJSON`.
|
|
methodDetails: response.details,
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Add a retry handler to the existing `showPromise` to call .retry().
|
|
* @returns {Object} representing the PaymentResponse
|
|
*/
|
|
addRetryHandler: async ({ validationErrors, delayMs = 0 }) => {
|
|
let response = await content.showPromise;
|
|
let retryException;
|
|
|
|
// delay the given # milliseconds
|
|
await new Promise(resolve => content.setTimeout(resolve, delayMs));
|
|
|
|
try {
|
|
await response.retry(Cu.cloneInto(validationErrors, content));
|
|
} catch (ex) {
|
|
info(`Retry error: ${ex}`);
|
|
retryException = {
|
|
name: ex.name,
|
|
message: ex.message,
|
|
};
|
|
}
|
|
return {
|
|
retryException,
|
|
response: response.toJSON(),
|
|
// XXX: Bug NNN: workaround for `details` not being included in `toJSON`.
|
|
methodDetails: response.details,
|
|
};
|
|
},
|
|
|
|
ensureNoPaymentRequestEvent: ({ eventName }) => {
|
|
content.rq.addEventListener(eventName, event => {
|
|
ok(false, `Unexpected ${eventName}`);
|
|
});
|
|
},
|
|
|
|
promisePaymentRequestEvent: ({ eventName }) => {
|
|
content[eventName + "Promise"] = new Promise(resolve => {
|
|
content.rq.addEventListener(eventName, () => {
|
|
info(`Received event: ${eventName}`);
|
|
resolve();
|
|
});
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Used for PaymentRequest and PaymentResponse event promises.
|
|
*/
|
|
awaitPaymentEventPromise: async ({ eventName }) => {
|
|
await content[eventName + "Promise"];
|
|
return true;
|
|
},
|
|
|
|
promisePaymentResponseEvent: async ({ eventName }) => {
|
|
let response = await content.showPromise;
|
|
content[eventName + "Promise"] = new Promise(resolve => {
|
|
response.addEventListener(eventName, () => {
|
|
info(`Received event: ${eventName}`);
|
|
resolve();
|
|
});
|
|
});
|
|
},
|
|
|
|
updateWith: async ({ eventName, details }) => {
|
|
/* globals ok */
|
|
if (
|
|
details.error &&
|
|
(!details.shippingOptions || details.shippingOptions.length)
|
|
) {
|
|
ok(false, "Need to clear the shipping options to show error text");
|
|
}
|
|
if (!details.total) {
|
|
ok(
|
|
false,
|
|
"`total: { label, amount: { value, currency } }` is required for updateWith"
|
|
);
|
|
}
|
|
|
|
content[eventName + "Promise"] = new Promise(resolve => {
|
|
content.rq.addEventListener(
|
|
eventName,
|
|
event => {
|
|
event.updateWith(details);
|
|
resolve();
|
|
},
|
|
{ once: true }
|
|
);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Create a new payment request cached as `rq` and then show it.
|
|
*
|
|
* @param {Object} args
|
|
* @param {PaymentMethodData[]} methodData
|
|
* @param {PaymentDetailsInit} details
|
|
* @param {PaymentOptions} options
|
|
* @returns {Object}
|
|
*/
|
|
createAndShowRequest: ({ methodData, details, options }) => {
|
|
const rq = new content.PaymentRequest(
|
|
Cu.cloneInto(methodData, content),
|
|
details,
|
|
options
|
|
);
|
|
content.rq = rq; // assign it so we can retrieve it later
|
|
|
|
const handle = content.windowUtils.setHandlingUserInput(true);
|
|
content.showPromise = rq.show();
|
|
|
|
handle.destruct();
|
|
return {
|
|
requestId: rq.id,
|
|
};
|
|
},
|
|
},
|
|
|
|
DialogContentTasks: {
|
|
getShippingOptions: () => {
|
|
let picker = content.document.querySelector("shipping-option-picker");
|
|
let popupBox = Cu.waiveXrays(picker).dropdown.popupBox;
|
|
let selectedOptionIndex = popupBox.selectedIndex;
|
|
let selectedOption = Cu.waiveXrays(picker).dropdown.selectedOption;
|
|
|
|
let result = {
|
|
optionCount: popupBox.children.length,
|
|
selectedOptionIndex,
|
|
};
|
|
if (!selectedOption) {
|
|
return result;
|
|
}
|
|
|
|
return Object.assign(result, {
|
|
selectedOptionID: selectedOption.getAttribute("value"),
|
|
selectedOptionLabel: selectedOption.getAttribute("label"),
|
|
selectedOptionCurrency: selectedOption.getAttribute("amount-currency"),
|
|
selectedOptionValue: selectedOption.getAttribute("amount-value"),
|
|
});
|
|
},
|
|
|
|
getShippingAddresses: () => {
|
|
let doc = content.document;
|
|
let addressPicker = doc.querySelector(
|
|
"address-picker[selected-state-key='selectedShippingAddress']"
|
|
);
|
|
let popupBox = Cu.waiveXrays(addressPicker).dropdown.popupBox;
|
|
let options = Array.from(popupBox.children).map(option => {
|
|
return {
|
|
guid: option.getAttribute("guid"),
|
|
country: option.getAttribute("country"),
|
|
selected: option.selected,
|
|
};
|
|
});
|
|
let selectedOptionIndex = options.findIndex(item => item.selected);
|
|
return {
|
|
selectedOptionIndex,
|
|
options,
|
|
};
|
|
},
|
|
|
|
selectShippingAddressByCountry: country => {
|
|
let doc = content.document;
|
|
let addressPicker = doc.querySelector(
|
|
"address-picker[selected-state-key='selectedShippingAddress']"
|
|
);
|
|
let select = Cu.waiveXrays(addressPicker).dropdown.popupBox;
|
|
let option = select.querySelector(`[country="${country}"]`);
|
|
content.fillField(select, option.value);
|
|
},
|
|
|
|
selectPayerAddressByGuid: guid => {
|
|
let doc = content.document;
|
|
let addressPicker = doc.querySelector(
|
|
"address-picker[selected-state-key='selectedPayerAddress']"
|
|
);
|
|
let select = Cu.waiveXrays(addressPicker).dropdown.popupBox;
|
|
content.fillField(select, guid);
|
|
},
|
|
|
|
selectShippingAddressByGuid: guid => {
|
|
let doc = content.document;
|
|
let addressPicker = doc.querySelector(
|
|
"address-picker[selected-state-key='selectedShippingAddress']"
|
|
);
|
|
let select = Cu.waiveXrays(addressPicker).dropdown.popupBox;
|
|
content.fillField(select, guid);
|
|
},
|
|
|
|
selectShippingOptionById: value => {
|
|
let doc = content.document;
|
|
let optionPicker = doc.querySelector("shipping-option-picker");
|
|
let select = Cu.waiveXrays(optionPicker).dropdown.popupBox;
|
|
content.fillField(select, value);
|
|
},
|
|
|
|
selectPaymentOptionByGuid: guid => {
|
|
let doc = content.document;
|
|
let methodPicker = doc.querySelector("payment-method-picker");
|
|
let select = Cu.waiveXrays(methodPicker).dropdown.popupBox;
|
|
content.fillField(select, guid);
|
|
},
|
|
|
|
/**
|
|
* Click the primary button for the current page
|
|
*
|
|
* Don't await on this method from a ContentTask when expecting the dialog to close
|
|
*
|
|
* @returns {undefined}
|
|
*/
|
|
clickPrimaryButton: () => {
|
|
let { requestStore } = Cu.waiveXrays(
|
|
content.document.querySelector("payment-dialog")
|
|
);
|
|
let { page } = requestStore.getState();
|
|
let button = content.document.querySelector(`#${page.id} button.primary`);
|
|
ok(
|
|
!button.disabled,
|
|
`#${page.id} primary button should not be disabled when clicking it`
|
|
);
|
|
button.click();
|
|
},
|
|
|
|
/**
|
|
* Click the cancel button
|
|
*
|
|
* Don't await on this task since the cancel can close the dialog before
|
|
* ContentTask can resolve the promise.
|
|
*
|
|
* @returns {undefined}
|
|
*/
|
|
manuallyClickCancel: () => {
|
|
content.document.getElementById("cancel").click();
|
|
},
|
|
|
|
/**
|
|
* Do the minimum possible to complete the payment succesfully.
|
|
*
|
|
* Don't await on this task since the cancel can close the dialog before
|
|
* ContentTask can resolve the promise.
|
|
*
|
|
* @returns {undefined}
|
|
*/
|
|
completePayment: async () => {
|
|
let { PaymentTestUtils: PTU } = ChromeUtils.import(
|
|
"resource://testing-common/PaymentTestUtils.jsm"
|
|
);
|
|
|
|
await PTU.DialogContentUtils.waitForState(
|
|
content,
|
|
state => {
|
|
return state.page.id == "payment-summary";
|
|
},
|
|
"Wait for change to payment-summary before clicking Pay"
|
|
);
|
|
|
|
let button = content.document.getElementById("pay");
|
|
ok(
|
|
!button.disabled,
|
|
"Pay button should not be disabled when clicking it"
|
|
);
|
|
button.click();
|
|
},
|
|
|
|
setSecurityCode: ({ securityCode }) => {
|
|
// Waive the xray to access the untrusted `securityCodeInput` property
|
|
let picker = Cu.waiveXrays(
|
|
content.document.querySelector("payment-method-picker")
|
|
);
|
|
// Unwaive to access the ChromeOnly `setUserInput` API.
|
|
// setUserInput dispatches changes events.
|
|
Cu.unwaiveXrays(picker.securityCodeInput)
|
|
.querySelector("input")
|
|
.setUserInput(securityCode);
|
|
},
|
|
},
|
|
|
|
DialogContentUtils: {
|
|
waitForState: async (content, stateCheckFn, msg) => {
|
|
const { ContentTaskUtils } = ChromeUtils.import(
|
|
"resource://testing-common/ContentTaskUtils.jsm"
|
|
);
|
|
let { requestStore } = Cu.waiveXrays(
|
|
content.document.querySelector("payment-dialog")
|
|
);
|
|
await ContentTaskUtils.waitForCondition(
|
|
() => stateCheckFn(requestStore.getState()),
|
|
msg
|
|
);
|
|
return requestStore.getState();
|
|
},
|
|
|
|
getCurrentState: async content => {
|
|
let { requestStore } = Cu.waiveXrays(
|
|
content.document.querySelector("payment-dialog")
|
|
);
|
|
return requestStore.getState();
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Common PaymentMethodData for testing
|
|
*/
|
|
MethodData: {
|
|
basicCard: {
|
|
supportedMethods: "basic-card",
|
|
},
|
|
bobPay: {
|
|
supportedMethods: "https://www.example.com/bobpay",
|
|
},
|
|
},
|
|
|
|
/**
|
|
* Common PaymentDetailsInit for testing
|
|
*/
|
|
Details: {
|
|
total2USD: {
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "USD", value: "2.00" },
|
|
},
|
|
},
|
|
total32USD: {
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "USD", value: "32.00" },
|
|
},
|
|
},
|
|
total60USD: {
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "USD", value: "60.00" },
|
|
},
|
|
},
|
|
total1pt75EUR: {
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "EUR", value: "1.75" },
|
|
},
|
|
},
|
|
total60EUR: {
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "EUR", value: "75.00" },
|
|
},
|
|
},
|
|
twoDisplayItems: {
|
|
displayItems: [
|
|
{
|
|
label: "First",
|
|
amount: { currency: "USD", value: "1" },
|
|
},
|
|
{
|
|
label: "Second",
|
|
amount: { currency: "USD", value: "2" },
|
|
},
|
|
],
|
|
},
|
|
twoDisplayItemsEUR: {
|
|
displayItems: [
|
|
{
|
|
label: "First",
|
|
amount: { currency: "EUR", value: "0.85" },
|
|
},
|
|
{
|
|
label: "Second",
|
|
amount: { currency: "EUR", value: "1.70" },
|
|
},
|
|
],
|
|
},
|
|
twoShippingOptions: {
|
|
shippingOptions: [
|
|
{
|
|
id: "1",
|
|
label: "Meh Unreliable Shipping",
|
|
amount: { currency: "USD", value: "1" },
|
|
},
|
|
{
|
|
id: "2",
|
|
label: "Premium Slow Shipping",
|
|
amount: { currency: "USD", value: "2" },
|
|
selected: true,
|
|
},
|
|
],
|
|
},
|
|
twoShippingOptionsEUR: {
|
|
shippingOptions: [
|
|
{
|
|
id: "1",
|
|
label: "Sloth Shipping",
|
|
amount: { currency: "EUR", value: "1.01" },
|
|
},
|
|
{
|
|
id: "2",
|
|
label: "Velociraptor Shipping",
|
|
amount: { currency: "EUR", value: "63545.65" },
|
|
selected: true,
|
|
},
|
|
],
|
|
},
|
|
noShippingOptions: {
|
|
shippingOptions: [],
|
|
},
|
|
bobPayPaymentModifier: {
|
|
modifiers: [
|
|
{
|
|
additionalDisplayItems: [
|
|
{
|
|
label: "Credit card fee",
|
|
amount: { currency: "USD", value: "0.50" },
|
|
},
|
|
],
|
|
supportedMethods: "basic-card",
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "USD", value: "2.50" },
|
|
},
|
|
data: {},
|
|
},
|
|
{
|
|
additionalDisplayItems: [
|
|
{
|
|
label: "Bob-pay fee",
|
|
amount: { currency: "USD", value: "1.50" },
|
|
},
|
|
],
|
|
supportedMethods: "https://www.example.com/bobpay",
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "USD", value: "3.50" },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
additionalDisplayItemsEUR: {
|
|
modifiers: [
|
|
{
|
|
additionalDisplayItems: [
|
|
{
|
|
label: "Handling fee",
|
|
amount: { currency: "EUR", value: "1.00" },
|
|
},
|
|
],
|
|
supportedMethods: "basic-card",
|
|
total: {
|
|
label: "Total due",
|
|
amount: { currency: "EUR", value: "2.50" },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
noError: {
|
|
error: "",
|
|
},
|
|
genericShippingError: {
|
|
error: "Cannot ship with option 1 on days that end with Y",
|
|
},
|
|
fieldSpecificErrors: {
|
|
error: "There are errors related to specific parts of the address",
|
|
shippingAddressErrors: {
|
|
addressLine:
|
|
"Can only ship to ROADS, not DRIVES, BOULEVARDS, or STREETS",
|
|
city: "Can only ship to CITIES, not TOWNSHIPS or VILLAGES",
|
|
country: "Can only ship to USA, not CA",
|
|
dependentLocality: "Can only be SUBURBS, not NEIGHBORHOODS",
|
|
organization: "Can only ship to CORPORATIONS, not CONSORTIUMS",
|
|
phone: "Only allowed to ship to area codes that start with 9",
|
|
postalCode: "Only allowed to ship to postalCodes that start with 0",
|
|
recipient: "Can only ship to names that start with J",
|
|
region: "Can only ship to regions that start with M",
|
|
regionCode:
|
|
"Regions must be 1 to 3 characters in length (sometimes ;) )",
|
|
},
|
|
},
|
|
},
|
|
|
|
Options: {
|
|
requestShippingOption: {
|
|
requestShipping: true,
|
|
},
|
|
requestPayerNameAndEmail: {
|
|
requestPayerName: true,
|
|
requestPayerEmail: true,
|
|
},
|
|
requestPayerNameEmailAndPhone: {
|
|
requestPayerName: true,
|
|
requestPayerEmail: true,
|
|
requestPayerPhone: true,
|
|
},
|
|
},
|
|
|
|
Addresses: {
|
|
TimBR: {
|
|
"given-name": "Timothy",
|
|
"additional-name": "João",
|
|
"family-name": "Berners-Lee",
|
|
organization: "World Wide Web Consortium",
|
|
"street-address": "Rua Adalberto Pajuaba, 404",
|
|
"address-level3": "Campos Elísios",
|
|
"address-level2": "Ribeirão Preto",
|
|
"address-level1": "SP",
|
|
"postal-code": "14055-220",
|
|
country: "BR",
|
|
tel: "+0318522222222",
|
|
email: "timbr@example.org",
|
|
},
|
|
TimBL: {
|
|
"given-name": "Timothy",
|
|
"additional-name": "John",
|
|
"family-name": "Berners-Lee",
|
|
organization: "World Wide Web Consortium",
|
|
"street-address": "32 Vassar Street\nMIT Room 32-G524",
|
|
"address-level2": "Cambridge",
|
|
"address-level1": "MA",
|
|
"postal-code": "02139",
|
|
country: "US",
|
|
tel: "+16172535702",
|
|
email: "timbl@example.org",
|
|
},
|
|
TimBL2: {
|
|
"given-name": "Timothy",
|
|
"additional-name": "Johann",
|
|
"family-name": "Berners-Lee",
|
|
organization: "World Wide Web Consortium",
|
|
"street-address": "1 Pommes Frittes Place",
|
|
"address-level2": "Berlin",
|
|
// address-level1 isn't used in our forms for Germany
|
|
"postal-code": "02138",
|
|
country: "DE",
|
|
tel: "+16172535702",
|
|
email: "timbl@example.com",
|
|
},
|
|
/* Used as a temporary (not persisted in autofill storage) address in tests */
|
|
Temp: {
|
|
"given-name": "Temp",
|
|
"family-name": "McTempFace",
|
|
organization: "Temps Inc.",
|
|
"street-address": "1a Temporary Ave.",
|
|
"address-level2": "Temp Town",
|
|
"address-level1": "CA",
|
|
"postal-code": "31337",
|
|
country: "US",
|
|
tel: "+15032541000",
|
|
email: "tempie@example.com",
|
|
},
|
|
},
|
|
|
|
BasicCards: {
|
|
JohnDoe: {
|
|
"cc-exp-month": 1,
|
|
"cc-exp-year": new Date().getFullYear() + 9,
|
|
"cc-name": "John Doe",
|
|
"cc-number": "4111111111111111",
|
|
"cc-type": "visa",
|
|
},
|
|
JaneMasterCard: {
|
|
"cc-exp-month": 12,
|
|
"cc-exp-year": new Date().getFullYear() + 9,
|
|
"cc-name": "Jane McMaster-Card",
|
|
"cc-number": "5555555555554444",
|
|
"cc-type": "mastercard",
|
|
},
|
|
MissingFields: {
|
|
"cc-name": "Missy Fields",
|
|
"cc-number": "340000000000009",
|
|
},
|
|
Temp: {
|
|
"cc-exp-month": 12,
|
|
"cc-exp-year": new Date().getFullYear() + 9,
|
|
"cc-name": "Temp Name",
|
|
"cc-number": "5105105105105100",
|
|
"cc-type": "mastercard",
|
|
},
|
|
},
|
|
};
|