зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1482689 - Use AddressPicker for the card billing address UI. r=MattN
* New BillingAddressPicker subclass of AddressPicker which just overrides some of the state-related behavior that * Allow the RichSelect's popupBox (<select>) to be assigned after the constructor * A couple new mochitests for the new/different behavior * Update the test to expect 'edit' to be hidden when the empty option is selected Differential Revision: https://phabricator.services.mozilla.com/D9321 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
04e84ad672
Коммит
aa95912b8b
|
@ -24,10 +24,12 @@ export default class RichSelect extends ObservedPropertiesMixin(HTMLElement) {
|
|||
constructor() {
|
||||
super();
|
||||
this.popupBox = document.createElement("select");
|
||||
this.popupBox.addEventListener("change", this);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
// the popupBox element may change in between constructor and being connected
|
||||
// so wait until connected before listening to events on it
|
||||
this.popupBox.addEventListener("change", this);
|
||||
this.appendChild(this.popupBox);
|
||||
this.render();
|
||||
}
|
||||
|
@ -74,7 +76,7 @@ export default class RichSelect extends ObservedPropertiesMixin(HTMLElement) {
|
|||
|
||||
if (this.value) {
|
||||
let optionType = this.getAttribute("option-type");
|
||||
if (selectedRichOption.localName != optionType) {
|
||||
if (!selectedRichOption || selectedRichOption.localName != optionType) {
|
||||
selectedRichOption = document.createElement(optionType);
|
||||
}
|
||||
|
||||
|
|
|
@ -33,7 +33,10 @@ export default class AddressPicker extends RichPicker {
|
|||
|
||||
attributeChangedCallback(name, oldValue, newValue) {
|
||||
super.attributeChangedCallback(name, oldValue, newValue);
|
||||
if (AddressPicker.pickerAttributes.includes(name) && oldValue !== newValue) {
|
||||
// connectedCallback may add and adjust elements & values
|
||||
// so avoid calling render before the element is connected
|
||||
if (this.isConnected &&
|
||||
AddressPicker.pickerAttributes.includes(name) && oldValue !== newValue) {
|
||||
this.render(this.requestStore.getState());
|
||||
}
|
||||
}
|
||||
|
@ -89,7 +92,26 @@ export default class AddressPicker extends RichPicker {
|
|||
return result;
|
||||
}
|
||||
|
||||
get options() {
|
||||
return this.dropdown.popupBox.options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} state - See `PaymentsStore.setState`
|
||||
* The value of the picker is retrieved from state store rather than the DOM
|
||||
* @returns {string} guid
|
||||
*/
|
||||
getCurrentValue(state) {
|
||||
let [selectedKey, selectedLeaf] = this.selectedStateKey.split("|");
|
||||
let guid = state[selectedKey];
|
||||
if (selectedLeaf) {
|
||||
guid = guid[selectedLeaf];
|
||||
}
|
||||
return guid;
|
||||
}
|
||||
|
||||
render(state) {
|
||||
let selectedAddressGUID = this.getCurrentValue(state) || "";
|
||||
let addresses = paymentRequest.getAddresses(state);
|
||||
let desiredOptions = [];
|
||||
let filteredAddresses = this.filterAddresses(addresses, this.fieldNames);
|
||||
|
@ -131,12 +153,18 @@ export default class AddressPicker extends RichPicker {
|
|||
}
|
||||
|
||||
this.dropdown.popupBox.textContent = "";
|
||||
|
||||
if (this._allowEmptyOption) {
|
||||
let optionEl = document.createElement("option");
|
||||
optionEl.value = "";
|
||||
desiredOptions.unshift(optionEl);
|
||||
}
|
||||
|
||||
for (let option of desiredOptions) {
|
||||
this.dropdown.popupBox.appendChild(option);
|
||||
}
|
||||
|
||||
// Update selectedness after the options are updated
|
||||
let selectedAddressGUID = state[this.selectedStateKey];
|
||||
this.dropdown.value = selectedAddressGUID;
|
||||
|
||||
if (selectedAddressGUID && selectedAddressGUID !== this.dropdown.value) {
|
||||
|
@ -161,8 +189,8 @@ export default class AddressPicker extends RichPicker {
|
|||
return "";
|
||||
}
|
||||
|
||||
let merchantFieldErrors = AddressForm.merchantFieldErrorsForForm(state,
|
||||
[this.selectedStateKey]);
|
||||
let merchantFieldErrors = AddressForm.merchantFieldErrorsForForm(
|
||||
state, this.selectedStateKey.split("|"));
|
||||
// TODO: errors in priority order.
|
||||
return Object.values(merchantFieldErrors).find(msg => {
|
||||
return typeof(msg) == "string" && msg.length;
|
||||
|
@ -182,12 +210,23 @@ export default class AddressPicker extends RichPicker {
|
|||
}
|
||||
|
||||
onChange(event) {
|
||||
let selectedKey = this.selectedStateKey;
|
||||
if (selectedKey) {
|
||||
this.requestStore.setState({
|
||||
[selectedKey]: this.dropdown.value,
|
||||
});
|
||||
let [selectedKey, selectedLeaf] = this.selectedStateKey.split("|");
|
||||
if (!selectedKey) {
|
||||
return;
|
||||
}
|
||||
// selectedStateKey can be a '|' delimited string indicating a path into the state object
|
||||
// to update with the new value
|
||||
let newState = {};
|
||||
|
||||
if (selectedLeaf) {
|
||||
let currentState = this.requestStore.getState();
|
||||
newState[selectedKey] = Object.assign({},
|
||||
currentState[selectedKey],
|
||||
{ [selectedLeaf]: this.dropdown.value });
|
||||
} else {
|
||||
newState[selectedKey] = this.dropdown.value;
|
||||
}
|
||||
this.requestStore.setState(newState);
|
||||
}
|
||||
|
||||
onClick({target}) {
|
||||
|
@ -197,7 +236,7 @@ export default class AddressPicker extends RichPicker {
|
|||
},
|
||||
"address-page": {
|
||||
addressFields: this.getAttribute("address-fields"),
|
||||
selectedStateKey: [this.selectedStateKey],
|
||||
selectedStateKey: this.selectedStateKey.split("|"),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -208,9 +247,8 @@ export default class AddressPicker extends RichPicker {
|
|||
break;
|
||||
}
|
||||
case this.editLink: {
|
||||
let state = this.requestStore.getState();
|
||||
let selectedAddressGUID = state[this.selectedStateKey];
|
||||
nextState["address-page"].guid = selectedAddressGUID;
|
||||
let currentState = this.requestStore.getState();
|
||||
nextState["address-page"].guid = this.getCurrentValue(currentState);
|
||||
nextState["address-page"].title = this.dataset.editAddressTitle;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -31,11 +31,6 @@ basic-card-form .editCreditCardForm .persist-checkbox {
|
|||
display: grid;
|
||||
}
|
||||
|
||||
#billingAddressGUID {
|
||||
/* XXX: temporary until converted to a rich-picker in bug 1482689 */
|
||||
margin: 14px 0;
|
||||
}
|
||||
|
||||
basic-card-form > footer > .cancel-button {
|
||||
/* When cancel is shown (during onboarding), it should always be on the left with a space after it */
|
||||
margin-right: auto;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
/* import-globals-from ../../../../../browser/extensions/formautofill/content/autofillEditForms.js*/
|
||||
import AcceptedCards from "../components/accepted-cards.js";
|
||||
import BillingAddressPicker from "./billing-address-picker.js";
|
||||
import CscInput from "../components/csc-input.js";
|
||||
import LabelledCheckbox from "../components/labelled-checkbox.js";
|
||||
import PaymentRequestPage from "../components/payment-request-page.js";
|
||||
|
@ -27,15 +28,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
this.genericErrorText.setAttribute("aria-live", "polite");
|
||||
this.genericErrorText.classList.add("page-error");
|
||||
|
||||
this.addressAddLink = document.createElement("a");
|
||||
this.addressAddLink.className = "add-link";
|
||||
this.addressAddLink.href = "javascript:void(0)";
|
||||
this.addressAddLink.addEventListener("click", this);
|
||||
this.addressEditLink = document.createElement("a");
|
||||
this.addressEditLink.className = "edit-link";
|
||||
this.addressEditLink.href = "javascript:void(0)";
|
||||
this.addressEditLink.addEventListener("click", this);
|
||||
|
||||
this.cscInput = new CscInput({
|
||||
useAlwaysVisiblePlaceholder: true,
|
||||
inputId: "cc-csc",
|
||||
|
@ -85,6 +77,38 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
});
|
||||
}
|
||||
|
||||
_upgradeBillingAddressPicker() {
|
||||
let addressRow = this.form.querySelector(".billingAddressRow");
|
||||
let addressPicker = this.billingAddressPicker = new BillingAddressPicker();
|
||||
|
||||
// Wrap the existing <select> that the formHandler manages
|
||||
if (addressPicker.dropdown.popupBox) {
|
||||
addressPicker.dropdown.popupBox.remove();
|
||||
}
|
||||
addressPicker.dropdown.popupBox = this.form.querySelector("#billingAddressGUID");
|
||||
|
||||
// Hide the original label as the address picker provide its own,
|
||||
// but we'll copy the localized textContent from it when rendering
|
||||
addressRow.querySelector(".label-text").hidden = true;
|
||||
|
||||
addressPicker.dataset.addLinkLabel = this.dataset.addressAddLinkLabel;
|
||||
addressPicker.dataset.editLinkLabel = this.dataset.addressEditLinkLabel;
|
||||
addressPicker.dataset.fieldSeparator = this.dataset.addressFieldSeparator;
|
||||
addressPicker.dataset.addAddressTitle = this.dataset.billingAddressTitleAdd;
|
||||
addressPicker.dataset.editAddressTitle = this.dataset.billingAddressTitleEdit;
|
||||
addressPicker.dataset.invalidLabel = this.dataset.invalidAddressLabel;
|
||||
// break-after-nth-field, address-fields not needed here
|
||||
|
||||
// this state is only used to carry the selected guid between pages;
|
||||
// the select#billingAddressGUID is the source of truth for the current value
|
||||
addressPicker.setAttribute("selected-state-key", "basic-card-page|billingAddressGUID");
|
||||
|
||||
addressPicker.addLink.addEventListener("click", this);
|
||||
addressPicker.editLink.addEventListener("click", this);
|
||||
|
||||
addressRow.appendChild(addressPicker);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
this.promiseReady.then(form => {
|
||||
this.body.appendChild(form);
|
||||
|
@ -106,6 +130,8 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
form.addEventListener("input", this);
|
||||
form.addEventListener("invalid", this);
|
||||
|
||||
this._upgradeBillingAddressPicker();
|
||||
|
||||
// The "invalid" event does not bubble and needs to be listened for on each
|
||||
// form element.
|
||||
for (let field of this.form.elements) {
|
||||
|
@ -117,18 +143,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
cscContainer.textContent = "";
|
||||
cscContainer.appendChild(this.cscInput);
|
||||
|
||||
let fragment = document.createDocumentFragment();
|
||||
fragment.append(" ");
|
||||
fragment.append(this.addressEditLink);
|
||||
fragment.append(this.addressAddLink);
|
||||
let billingAddressRow = this.form.querySelector(".billingAddressRow");
|
||||
|
||||
// XXX: Bug 1482689 - Remove the label-text class from the billing field
|
||||
// which will be removed when switching to <rich-select>.
|
||||
billingAddressRow.querySelector(".label-text").classList.remove("label-text");
|
||||
|
||||
billingAddressRow.appendChild(fragment);
|
||||
|
||||
form.insertBefore(this.persistCheckbox, billingAddressRow);
|
||||
form.insertBefore(this.acceptedCardsList, billingAddressRow);
|
||||
this.body.appendChild(this.genericErrorText);
|
||||
|
@ -167,10 +182,13 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
this.cscInput.frontTooltip = this.dataset.cscFrontInfoTooltip;
|
||||
this.cscInput.backTooltip = this.dataset.cscBackInfoTooltip;
|
||||
|
||||
// The label text from the form isn't available until render() time.
|
||||
let labelText = this.form.querySelector(".billingAddressRow .label-text").textContent;
|
||||
this.billingAddressPicker.setAttribute("label", labelText);
|
||||
|
||||
this.persistCheckbox.label = this.dataset.persistCheckboxLabel;
|
||||
this.persistCheckbox.infoTooltip = this.dataset.persistCheckboxInfoTooltip;
|
||||
this.addressAddLink.textContent = this.dataset.addressAddLinkLabel;
|
||||
this.addressEditLink.textContent = this.dataset.addressEditLinkLabel;
|
||||
|
||||
this.acceptedCardsList.label = this.dataset.acceptedCardsLabel;
|
||||
|
||||
// The next line needs an onboarding check since we don't set previousId
|
||||
|
@ -227,7 +245,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
|
||||
this.form.querySelector(".billingAddressRow").hidden = false;
|
||||
|
||||
let billingAddressSelect = this.form.querySelector("#billingAddressGUID");
|
||||
let billingAddressSelect = this.billingAddressPicker.dropdown;
|
||||
if (basicCardPage.billingAddressGUID) {
|
||||
billingAddressSelect.value = basicCardPage.billingAddressGUID;
|
||||
} else if (!editing) {
|
||||
|
@ -244,7 +262,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
}
|
||||
// Need to recalculate the populated state since
|
||||
// billingAddressSelect is updated after loadRecord.
|
||||
this.formHandler.updatePopulatedState(billingAddressSelect);
|
||||
this.formHandler.updatePopulatedState(billingAddressSelect.popupBox);
|
||||
|
||||
this.updateRequiredState();
|
||||
this.updateSaveButtonState();
|
||||
|
@ -289,21 +307,18 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
paymentRequest.cancel();
|
||||
break;
|
||||
}
|
||||
case this.addressAddLink:
|
||||
case this.addressEditLink: {
|
||||
case this.billingAddressPicker.addLink:
|
||||
case this.billingAddressPicker.editLink: {
|
||||
// The address-picker has set state for the page to advance to, now set up the
|
||||
// necessary state for returning to and re-rendering this page
|
||||
let {
|
||||
"basic-card-page": basicCardPage,
|
||||
page,
|
||||
} = this.requestStore.getState();
|
||||
let nextState = {
|
||||
page: {
|
||||
id: "address-page",
|
||||
page: Object.assign({}, page, {
|
||||
previousId: "basic-card-page",
|
||||
},
|
||||
"address-page": {
|
||||
guid: null,
|
||||
selectedStateKey: ["basic-card-page", "billingAddressGUID"],
|
||||
title: this.dataset.billingAddressTitleAdd,
|
||||
},
|
||||
}),
|
||||
"basic-card-page": {
|
||||
preserveFieldValues: true,
|
||||
guid: basicCardPage.guid,
|
||||
|
@ -311,24 +326,18 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
selectedStateKey: basicCardPage.selectedStateKey,
|
||||
},
|
||||
};
|
||||
let billingAddressGUID = this.form.querySelector("#billingAddressGUID");
|
||||
let selectedOption = billingAddressGUID.selectedOptions.length &&
|
||||
billingAddressGUID.selectedOptions[0];
|
||||
if (evt.target == this.addressEditLink && selectedOption && selectedOption.value) {
|
||||
nextState["address-page"].title = this.dataset.billingAddressTitleEdit;
|
||||
nextState["address-page"].guid = selectedOption.value;
|
||||
}
|
||||
this.requestStore.setState(nextState);
|
||||
break;
|
||||
}
|
||||
case this.backButton: {
|
||||
let currentState = this.requestStore.getState();
|
||||
let {
|
||||
page,
|
||||
request,
|
||||
"address-page": addressPage,
|
||||
"basic-card-page": basicCardPage,
|
||||
selectedShippingAddress,
|
||||
} = this.requestStore.getState();
|
||||
} = currentState;
|
||||
|
||||
let nextState = {
|
||||
page: {
|
||||
|
@ -391,7 +400,10 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
|
|||
}
|
||||
|
||||
updateSaveButtonState() {
|
||||
this.saveButton.disabled = !this.form.checkValidity();
|
||||
const INVALID_CLASS_NAME = "invalid-selected-option";
|
||||
let isValid = this.form.checkValidity() &&
|
||||
!this.billingAddressPicker.classList.contains(INVALID_CLASS_NAME);
|
||||
this.saveButton.disabled = !isValid;
|
||||
}
|
||||
|
||||
updateRequiredState() {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import AddressPicker from "./address-picker.js";
|
||||
/* import-globals-from ../unprivileged-fallbacks.js */
|
||||
|
||||
/**
|
||||
* <billing-address-picker></billing-address-picker>
|
||||
* Extends AddressPicker to treat the <select>'s value as the source of truth
|
||||
*/
|
||||
|
||||
export default class BillingAddressPicker extends AddressPicker {
|
||||
constructor() {
|
||||
super();
|
||||
this._allowEmptyOption = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object?} state - See `PaymentsStore.setState`
|
||||
* The value of the picker is the child dropdown element's value
|
||||
* @returns {string} guid
|
||||
*/
|
||||
getCurrentValue() {
|
||||
return this.dropdown.value;
|
||||
}
|
||||
|
||||
onChange(event) {
|
||||
this.render(this.requestStore.getState());
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("billing-address-picker", BillingAddressPicker);
|
|
@ -16,29 +16,30 @@ export default class RichPicker extends PaymentStateSubscriberMixin(HTMLElement)
|
|||
|
||||
this.dropdown = new RichSelect();
|
||||
this.dropdown.addEventListener("change", this);
|
||||
this.dropdown.popupBox.id = "select-" + Math.floor(Math.random() * 1000000);
|
||||
|
||||
this.labelElement = document.createElement("label");
|
||||
this.labelElement.setAttribute("for", this.dropdown.popupBox.id);
|
||||
|
||||
this.addLink = document.createElement("a");
|
||||
this.addLink.className = "add-link";
|
||||
this.addLink.href = "javascript:void(0)";
|
||||
this.addLink.textContent = this.dataset.addLinkLabel;
|
||||
this.addLink.addEventListener("click", this);
|
||||
|
||||
this.editLink = document.createElement("a");
|
||||
this.editLink.className = "edit-link";
|
||||
this.editLink.href = "javascript:void(0)";
|
||||
this.editLink.textContent = this.dataset.editLinkLabel;
|
||||
this.editLink.addEventListener("click", this);
|
||||
|
||||
this.invalidLabel = document.createElement("label");
|
||||
this.invalidLabel.className = "invalid-label";
|
||||
this.invalidLabel.setAttribute("for", this.dropdown.popupBox.id);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
if (!this.dropdown.popupBox.id) {
|
||||
this.dropdown.popupBox.id = "select-" + Math.floor(Math.random() * 1000000);
|
||||
}
|
||||
this.labelElement.setAttribute("for", this.dropdown.popupBox.id);
|
||||
this.invalidLabel.setAttribute("for", this.dropdown.popupBox.id);
|
||||
|
||||
// The document order, by default, controls tab order so keep that in mind if changing this.
|
||||
this.appendChild(this.labelElement);
|
||||
this.appendChild(this.dropdown);
|
||||
|
@ -61,6 +62,8 @@ export default class RichPicker extends PaymentStateSubscriberMixin(HTMLElement)
|
|||
this.classList.toggle("invalid-selected-option",
|
||||
!!errorText);
|
||||
this.invalidLabel.textContent = errorText;
|
||||
this.addLink.textContent = this.dataset.addLinkLabel;
|
||||
this.editLink.textContent = this.dataset.editLinkLabel;
|
||||
}
|
||||
|
||||
get selectedOption() {
|
||||
|
@ -99,7 +102,8 @@ export default class RichPicker extends PaymentStateSubscriberMixin(HTMLElement)
|
|||
return [];
|
||||
}
|
||||
|
||||
let fieldNames = this.selectedRichOption.requiredFields;
|
||||
let fieldNames = this.selectedRichOption.requiredFields || [];
|
||||
|
||||
// Return all field names that are empty or missing from the option.
|
||||
return fieldNames.filter(name => !selectedOption.getAttribute(name));
|
||||
}
|
||||
|
|
|
@ -193,8 +193,12 @@
|
|||
data-add-basic-card-title="&basicCard.addPage.title;"
|
||||
data-edit-basic-card-title="&basicCard.editPage.title;"
|
||||
data-error-generic-save="&basicCardPage.error.genericSave;"
|
||||
|
||||
data-address-add-link-label="&basicCardPage.addressAddLink.label;"
|
||||
data-address-edit-link-label="&basicCardPage.addressEditLink.label;"
|
||||
|
||||
data-invalid-address-label="&invalidOption.label;"
|
||||
data-address-field-separator="&address.fieldSeparator;"
|
||||
data-billing-address-title-add="&billingAddress.addPage.title;"
|
||||
data-billing-address-title-edit="&billingAddress.editPage.title;"
|
||||
data-back-button-label="&basicCardPage.backButton.label;"
|
||||
|
|
|
@ -36,6 +36,7 @@ async function add_link(aOptions = {}) {
|
|||
checkboxSelector: "basic-card-form .persist-checkbox",
|
||||
expectPersist: aOptions.expectDefaultCardPersist,
|
||||
});
|
||||
|
||||
await spawnPaymentDialogTask(frame, async function checkState(testArgs = {}) {
|
||||
let {
|
||||
PaymentTestUtils: PTU,
|
||||
|
@ -163,12 +164,12 @@ async function add_link(aOptions = {}) {
|
|||
ok(state["basic-card-page"].billingAddressGUID,
|
||||
"billingAddressGUID should be set when coming back from address-page");
|
||||
|
||||
let billingAddressSelect = content.document.querySelector("#billingAddressGUID");
|
||||
let billingAddressPicker = Cu.waiveXrays(
|
||||
content.document.querySelector("basic-card-form billing-address-picker"));
|
||||
|
||||
is(billingAddressSelect.childElementCount, 3,
|
||||
"Three options should exist in the billingAddressSelect");
|
||||
let selectedOption =
|
||||
billingAddressSelect.children[billingAddressSelect.selectedIndex];
|
||||
is(billingAddressPicker.options.length, 3,
|
||||
"Three options should exist in the billingAddressPicker");
|
||||
let selectedOption = billingAddressPicker.dropdown.selectedOption;
|
||||
let selectedAddressGuid = selectedOption.value;
|
||||
let lastAddress = Object.values(addressColn)[Object.keys(addressColn).length - 1];
|
||||
is(selectedAddressGuid, lastAddress.guid, "The select should have the new address selected");
|
||||
|
@ -411,152 +412,163 @@ add_task(async function test_edit_link() {
|
|||
const args = {
|
||||
methodData: [PTU.MethodData.basicCard],
|
||||
details: PTU.Details.total60USD,
|
||||
prefilledGuids,
|
||||
};
|
||||
await spawnInDialogForMerchantTask(PTU.ContentTasks.createAndShowRequest, async function check() {
|
||||
let {
|
||||
PaymentTestUtils: PTU,
|
||||
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
|
||||
await spawnInDialogForMerchantTask(
|
||||
PTU.ContentTasks.createAndShowRequest,
|
||||
async function check({prefilledGuids}) {
|
||||
let {
|
||||
PaymentTestUtils: PTU,
|
||||
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
|
||||
|
||||
let editLink = content.document.querySelector("payment-method-picker .edit-link");
|
||||
is(editLink.textContent, "Edit", "Edit link text");
|
||||
let editLink = content.document.querySelector("payment-method-picker .edit-link");
|
||||
is(editLink.textContent, "Edit", "Edit link text");
|
||||
|
||||
editLink.click();
|
||||
editLink.click();
|
||||
|
||||
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid;
|
||||
}, "Check edit page state");
|
||||
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid;
|
||||
}, "Check edit page state");
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return Object.keys(state.savedBasicCards).length == 1 &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check card and address present at beginning of test");
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return Object.keys(state.savedBasicCards).length == 1 &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check card and address present at beginning of test");
|
||||
|
||||
let title = content.document.querySelector("basic-card-form h2");
|
||||
is(title.textContent, "Edit Credit Card", "Edit title should be set");
|
||||
let title = content.document.querySelector("basic-card-form h2");
|
||||
is(title.textContent, "Edit Credit Card", "Edit title should be set");
|
||||
|
||||
let saveButton = content.document.querySelector("basic-card-form .save-button");
|
||||
is(saveButton.textContent, "Update", "Save button has the correct label");
|
||||
let saveButton = content.document.querySelector("basic-card-form .save-button");
|
||||
is(saveButton.textContent, "Update", "Save button has the correct label");
|
||||
|
||||
let card = Object.assign({}, PTU.BasicCards.JohnDoe);
|
||||
// cc-number cannot be modified
|
||||
delete card["cc-number"];
|
||||
card["cc-exp-year"]++;
|
||||
card["cc-exp-month"]++;
|
||||
let card = Object.assign({}, PTU.BasicCards.JohnDoe);
|
||||
// cc-number cannot be modified
|
||||
delete card["cc-number"];
|
||||
card["cc-exp-year"]++;
|
||||
card["cc-exp-month"]++;
|
||||
|
||||
info("overwriting field values");
|
||||
for (let [key, val] of Object.entries(card)) {
|
||||
let field = content.document.getElementById(key);
|
||||
field.value = val;
|
||||
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
|
||||
}
|
||||
ok(content.document.getElementById("cc-number").disabled, "cc-number field should be disabled");
|
||||
|
||||
let billingAddressSelect = content.document.querySelector("#billingAddressGUID");
|
||||
is(billingAddressSelect.childElementCount, 2,
|
||||
"Two options should exist in the billingAddressSelect");
|
||||
is(billingAddressSelect.selectedIndex, 1,
|
||||
"The prefilled billing address should be selected by default");
|
||||
|
||||
info("Test clicking 'edit' on the empty option first");
|
||||
billingAddressSelect.selectedIndex = 0;
|
||||
|
||||
let addressEditLink = content.document.querySelector(".billingAddressRow .edit-link");
|
||||
addressEditLink.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" && !state["address-page"].guid;
|
||||
}, "Clicking edit button when the empty option is selected will go to 'add' page (no guid)");
|
||||
|
||||
let addressTitle = content.document.querySelector("address-form h2");
|
||||
is(addressTitle.textContent, "Add Billing Address",
|
||||
"Address on add address page should be correct");
|
||||
|
||||
let addressBackButton = content.document.querySelector("address-form .back-button");
|
||||
addressBackButton.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check we're back at basic-card page with no state changed after adding");
|
||||
|
||||
info("Go back to previously selected option before clicking 'edit' now");
|
||||
billingAddressSelect.selectedIndex = 1;
|
||||
|
||||
let selectedOption = billingAddressSelect.selectedOptions.length &&
|
||||
billingAddressSelect.selectedOptions[0];
|
||||
ok(selectedOption && selectedOption.value, "select should have a selected option value");
|
||||
|
||||
addressEditLink.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" && state["address-page"].guid;
|
||||
}, "Check address page state (editing)");
|
||||
|
||||
is(addressTitle.textContent, "Edit Billing Address",
|
||||
"Address on edit address page should be correct");
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return Object.keys(state.savedBasicCards).length == 1;
|
||||
}, "Check card was not added again when clicking the 'edit' address button");
|
||||
|
||||
addressBackButton.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check we're back at basic-card page with no state changed after editing");
|
||||
|
||||
for (let [key, val] of Object.entries(card)) {
|
||||
let field = content.document.getElementById(key);
|
||||
is(field.value, val, "Field should still have previous value entered");
|
||||
}
|
||||
|
||||
selectedOption = billingAddressSelect.selectedOptions.length &&
|
||||
billingAddressSelect.selectedOptions[0];
|
||||
ok(selectedOption && selectedOption.value, "select should have a selected option value");
|
||||
|
||||
addressEditLink.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" && state["address-page"].guid;
|
||||
}, "Check address page state (editing)");
|
||||
|
||||
info("modify some address fields");
|
||||
for (let key of ["given-name", "tel", "organization", "street-address"]) {
|
||||
let field = content.document.getElementById(key);
|
||||
if (!field) {
|
||||
ok(false, `${key} field not found`);
|
||||
info("overwriting field values");
|
||||
for (let [key, val] of Object.entries(card)) {
|
||||
let field = content.document.getElementById(key);
|
||||
field.value = val;
|
||||
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
|
||||
}
|
||||
field.focus();
|
||||
EventUtils.sendKey("BACK_SPACE", content.window);
|
||||
EventUtils.sendString("7", content.window);
|
||||
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
|
||||
}
|
||||
ok(content.document.getElementById("cc-number").disabled,
|
||||
"cc-number field should be disabled");
|
||||
|
||||
content.document.querySelector("address-form button.save-button").click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check still only one address and we're back on basic-card page");
|
||||
let billingAddressPicker = Cu.waiveXrays(
|
||||
content.document.querySelector("basic-card-form billing-address-picker"));
|
||||
|
||||
is(Object.values(state.savedAddresses)[0].tel, PTU.Addresses.TimBL.tel.slice(0, -1) + "7",
|
||||
"Check that address was edited and saved");
|
||||
let initialSelectedAddressGuid = billingAddressPicker.dropdown.value;
|
||||
is(billingAddressPicker.options.length, 2,
|
||||
"Two options should exist in the billingAddressPicker");
|
||||
is(initialSelectedAddressGuid, prefilledGuids.address1GUID,
|
||||
"The prefilled billing address should be selected by default");
|
||||
|
||||
content.document.querySelector("basic-card-form button.save-button").click();
|
||||
info("Test clicking 'add' on the empty option first");
|
||||
billingAddressPicker.dropdown.popupBox.focus();
|
||||
content.fillField(billingAddressPicker.dropdown.popupBox, "");
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
let cards = Object.entries(state.savedBasicCards);
|
||||
return cards.length == 1 &&
|
||||
cards[0][1]["cc-name"] == card["cc-name"];
|
||||
}, "Check card was edited");
|
||||
let addressEditLink = content.document.querySelector(".billingAddressRow .edit-link");
|
||||
ok(addressEditLink && !content.isVisible(addressEditLink),
|
||||
"The edit link is hidden when empty option is selected");
|
||||
|
||||
let cardGUIDs = Object.keys(state.savedBasicCards);
|
||||
is(cardGUIDs.length, 1, "Check there is still one card");
|
||||
let savedCard = state.savedBasicCards[cardGUIDs[0]];
|
||||
is(savedCard["cc-number"], "************1111", "Card number should be masked and unmodified.");
|
||||
for (let [key, val] of Object.entries(card)) {
|
||||
is(savedCard[key], val, "Check updated " + key);
|
||||
}
|
||||
let addressAddLink = content.document.querySelector(".billingAddressRow .add-link");
|
||||
addressAddLink.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" && !state["address-page"].guid;
|
||||
}, "Clicking add button when the empty option is selected will go to 'add' page (no guid)");
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "payment-summary";
|
||||
}, "Switched back to payment-summary");
|
||||
}, args);
|
||||
let addressTitle = content.document.querySelector("address-form h2");
|
||||
is(addressTitle.textContent, "Add Billing Address",
|
||||
"Address on add address page should be correct");
|
||||
|
||||
let addressBackButton = content.document.querySelector("address-form .back-button");
|
||||
addressBackButton.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check we're back at basic-card page with no state changed after adding");
|
||||
|
||||
info("Go back to previously selected option before clicking 'edit' now");
|
||||
billingAddressPicker.dropdown.value = initialSelectedAddressGuid;
|
||||
|
||||
let selectedOption = billingAddressPicker.dropdown.selectedOption;
|
||||
ok(selectedOption && selectedOption.value, "select should have a selected option value");
|
||||
|
||||
addressEditLink.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" && state["address-page"].guid;
|
||||
}, "Check address page state (editing)");
|
||||
|
||||
is(addressTitle.textContent, "Edit Billing Address",
|
||||
"Address on edit address page should be correct");
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return Object.keys(state.savedBasicCards).length == 1;
|
||||
}, "Check card was not added again when clicking the 'edit' address button");
|
||||
|
||||
addressBackButton.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check we're back at basic-card page with no state changed after editing");
|
||||
|
||||
for (let [key, val] of Object.entries(card)) {
|
||||
let field = content.document.getElementById(key);
|
||||
is(field.value, val, "Field should still have previous value entered");
|
||||
}
|
||||
|
||||
selectedOption = billingAddressPicker.dropdown.selectedOption;
|
||||
ok(selectedOption && selectedOption.value, "select should have a selected option value");
|
||||
|
||||
addressEditLink.click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "address-page" && state["address-page"].guid;
|
||||
}, "Check address page state (editing)");
|
||||
|
||||
info("modify some address fields");
|
||||
for (let key of ["given-name", "tel", "organization", "street-address"]) {
|
||||
let field = content.document.getElementById(key);
|
||||
if (!field) {
|
||||
ok(false, `${key} field not found`);
|
||||
}
|
||||
field.focus();
|
||||
EventUtils.sendKey("BACK_SPACE", content.window);
|
||||
EventUtils.sendString("7", content.window);
|
||||
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
|
||||
}
|
||||
|
||||
content.document.querySelector("address-form button.save-button").click();
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
|
||||
Object.keys(state.savedAddresses).length == 1;
|
||||
}, "Check still only one address and we're back on basic-card page");
|
||||
|
||||
is(Object.values(state.savedAddresses)[0].tel, PTU.Addresses.TimBL.tel.slice(0, -1) + "7",
|
||||
"Check that address was edited and saved");
|
||||
|
||||
content.document.querySelector("basic-card-form button.save-button").click();
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
let cards = Object.entries(state.savedBasicCards);
|
||||
return cards.length == 1 &&
|
||||
cards[0][1]["cc-name"] == card["cc-name"];
|
||||
}, "Check card was edited");
|
||||
|
||||
let cardGUIDs = Object.keys(state.savedBasicCards);
|
||||
is(cardGUIDs.length, 1, "Check there is still one card");
|
||||
let savedCard = state.savedBasicCards[cardGUIDs[0]];
|
||||
is(savedCard["cc-number"], "************1111",
|
||||
"Card number should be masked and unmodified.");
|
||||
for (let [key, val] of Object.entries(card)) {
|
||||
is(savedCard[key], val, "Check updated " + key);
|
||||
}
|
||||
|
||||
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
|
||||
return state.page.id == "payment-summary";
|
||||
}, "Switched back to payment-summary");
|
||||
}, args);
|
||||
});
|
||||
|
||||
add_task(async function test_invalid_network_card_edit() {
|
||||
|
|
|
@ -20,6 +20,7 @@ skip-if = os == "linux" || os == "win" # Bug 1493216
|
|||
[test_basic_card_form.html]
|
||||
skip-if = debug || asan # Bug 1493349
|
||||
[test_basic_card_option.html]
|
||||
[test_billing_address_picker.html]
|
||||
[test_completion_error_page.html]
|
||||
[test_currency_amount.html]
|
||||
[test_labelled_checkbox.html]
|
||||
|
|
|
@ -90,6 +90,9 @@ add_task(async function test_initialState() {
|
|||
add_task(async function test_backButton() {
|
||||
let form = new AddressForm();
|
||||
form.dataset.backButtonLabel = "Back";
|
||||
await form.promiseReady;
|
||||
display.appendChild(form);
|
||||
|
||||
await form.requestStore.setState({
|
||||
page: {
|
||||
id: "address-page",
|
||||
|
@ -99,9 +102,6 @@ add_task(async function test_backButton() {
|
|||
title: "Sample page title",
|
||||
},
|
||||
});
|
||||
|
||||
await form.promiseReady;
|
||||
display.appendChild(form);
|
||||
await asyncElementRendered();
|
||||
|
||||
let stateChangePromise = promiseStateChange(form.requestStore);
|
||||
|
|
|
@ -61,6 +61,14 @@ function checkCCForm(customEl, expectedCard) {
|
|||
}
|
||||
}
|
||||
|
||||
function createAddressRecord(source, props = {}) {
|
||||
let address = Object.assign({}, source, props);
|
||||
if (!address.name) {
|
||||
address.name = `${address["given-name"]} ${address["family-name"]}`;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
add_task(async function setup_once() {
|
||||
let templateFrame = document.getElementById("templateFrame");
|
||||
await SimpleTest.promiseFocus(templateFrame.contentWindow);
|
||||
|
@ -70,6 +78,13 @@ add_task(async function setup_once() {
|
|||
|
||||
add_task(async function test_initialState() {
|
||||
let form = new BasicCardForm();
|
||||
|
||||
await form.requestStore.setState({
|
||||
savedAddresses: {
|
||||
"TimBLGUID": createAddressRecord(PTU.Addresses.TimBL),
|
||||
},
|
||||
});
|
||||
|
||||
let {page} = form.requestStore.getState();
|
||||
is(page.id, "payment-summary", "Check initial page");
|
||||
await form.promiseReady;
|
||||
|
@ -79,6 +94,9 @@ add_task(async function test_initialState() {
|
|||
|
||||
// :-moz-ui-invalid, unlike :invalid, only applies to fields showing the error outline.
|
||||
let fieldsVisiblyInvalid = form.querySelectorAll(":-moz-ui-invalid");
|
||||
for (let field of fieldsVisiblyInvalid) {
|
||||
info("invalid field: " + field.localName + "#" + field.id + "." + field.className);
|
||||
}
|
||||
is(fieldsVisiblyInvalid.length, 0, "Check no fields are visibly invalid on an empty 'add' form");
|
||||
|
||||
form.remove();
|
||||
|
@ -116,18 +134,18 @@ add_task(async function test_saveButton() {
|
|||
let form = new BasicCardForm();
|
||||
form.dataset.nextButtonLabel = "Next";
|
||||
form.dataset.errorGenericSave = "Generic error";
|
||||
form.dataset.invalidAddressLabel = "Invalid";
|
||||
|
||||
await form.promiseReady;
|
||||
display.appendChild(form);
|
||||
|
||||
let address1 = deepClone(PTU.Addresses.TimBL);
|
||||
address1.guid = "TimBLGUID";
|
||||
let address2 = deepClone(PTU.Addresses.TimBL2);
|
||||
address2.guid = "TimBL2GUID";
|
||||
|
||||
let address1 = createAddressRecord(PTU.Addresses.TimBL, {guid: "TimBLGUID"});
|
||||
let address2 = createAddressRecord(PTU.Addresses.TimBL2, {guid: "TimBL2GUID"});
|
||||
|
||||
await form.requestStore.setState({
|
||||
request: {
|
||||
paymentMethods,
|
||||
paymentDetails: {},
|
||||
},
|
||||
savedAddresses: {
|
||||
[address1.guid]: deepClone(address1),
|
||||
|
@ -274,10 +292,8 @@ add_task(async function test_add_selectedShippingAddress() {
|
|||
card1.guid = "9864798564";
|
||||
card1["cc-exp-year"] = 2011;
|
||||
|
||||
let address1 = deepClone(PTU.Addresses.TimBL);
|
||||
address1.guid = "TimBLGUID";
|
||||
let address2 = deepClone(PTU.Addresses.TimBL2);
|
||||
address2.guid = "TimBL2GUID";
|
||||
let address1 = createAddressRecord(PTU.Addresses.TimBL, { guid: "TimBLGUID" });
|
||||
let address2 = createAddressRecord(PTU.Addresses.TimBL2, { guid: "TimBL2GUID" });
|
||||
|
||||
await form.requestStore.setState({
|
||||
page: {
|
||||
|
@ -312,8 +328,7 @@ add_task(async function test_add_noSelectedShippingAddress() {
|
|||
card1.guid = "9864798564";
|
||||
card1["cc-exp-year"] = 2011;
|
||||
|
||||
let address1 = deepClone(PTU.Addresses.TimBL);
|
||||
address1.guid = "TimBLGUID";
|
||||
let address1 = createAddressRecord(PTU.Addresses.TimBL, { guid: "TimBLGUID" });
|
||||
|
||||
await form.requestStore.setState({
|
||||
page: {
|
||||
|
@ -352,8 +367,7 @@ add_task(async function test_edit() {
|
|||
display.appendChild(form);
|
||||
await asyncElementRendered();
|
||||
|
||||
let address1 = deepClone(PTU.Addresses.TimBL);
|
||||
address1.guid = "TimBLGUID";
|
||||
let address1 = createAddressRecord(PTU.Addresses.TimBL, { guid: "TimBLGUID" });
|
||||
|
||||
info("test year before current");
|
||||
let card1 = deepClone(PTU.BasicCards.JohnDoe);
|
||||
|
@ -364,6 +378,7 @@ add_task(async function test_edit() {
|
|||
await form.requestStore.setState({
|
||||
request: {
|
||||
paymentMethods,
|
||||
paymentDetails: {},
|
||||
},
|
||||
page: {
|
||||
id: "basic-card-page",
|
||||
|
@ -383,6 +398,7 @@ add_task(async function test_edit() {
|
|||
is(form.saveButton.textContent, "Update", "Check label");
|
||||
is(form.querySelectorAll(":-moz-ui-invalid").length, 0,
|
||||
"Check no fields are visibly invalid on an 'edit' form with a complete card");
|
||||
|
||||
checkCCForm(form, card1);
|
||||
ok(!form.saveButton.disabled, "Save button should be enabled upon edit for a valid card");
|
||||
ok(!form.acceptedCardsList.hidden, "Accepted card list should be visible when editing a card");
|
||||
|
@ -460,6 +476,17 @@ add_task(async function test_field_validity_updates() {
|
|||
form.dataset.updateButtonLabel = "Update";
|
||||
await form.promiseReady;
|
||||
display.appendChild(form);
|
||||
|
||||
let address1 = createAddressRecord(PTU.Addresses.TimBL, {guid: "TimBLGUID"});
|
||||
await form.requestStore.setState({
|
||||
request: {
|
||||
paymentMethods,
|
||||
paymentDetails: {},
|
||||
},
|
||||
savedAddresses: {
|
||||
[address1.guid]: deepClone(address1),
|
||||
},
|
||||
});
|
||||
await asyncElementRendered();
|
||||
|
||||
let ccNumber = form.form.querySelector("#cc-number");
|
||||
|
@ -468,6 +495,7 @@ add_task(async function test_field_validity_updates() {
|
|||
let cscInput = form.form.querySelector("csc-input input");
|
||||
let monthInput = form.form.querySelector("#cc-exp-month");
|
||||
let yearInput = form.form.querySelector("#cc-exp-year");
|
||||
let addressPicker = form.querySelector("#billingAddressGUID");
|
||||
|
||||
info("test with valid cc-number but missing cc-name");
|
||||
fillField(ccNumber, "4111111111111111");
|
||||
|
@ -487,6 +515,16 @@ add_task(async function test_field_validity_updates() {
|
|||
ok(monthInput.checkValidity(), "cc-exp-month field is valid with a value");
|
||||
ok(yearInput.checkValidity(), "cc-exp-year field is valid with a value");
|
||||
ok(typeInput.checkValidity(), "cc-type field is valid with a value");
|
||||
|
||||
// should auto-select the first billing address
|
||||
ok(addressPicker.value, "An address is selected: " + addressPicker.value);
|
||||
|
||||
let fieldsVisiblyInvalid = form.querySelectorAll(":-moz-ui-invalid");
|
||||
for (let field of fieldsVisiblyInvalid) {
|
||||
info("invalid field: " + field.localName + "#" + field.id + "." + field.className);
|
||||
}
|
||||
is(fieldsVisiblyInvalid.length, 0, "No fields are visibly invalid");
|
||||
|
||||
ok(!form.saveButton.disabled, "Save button should not be disabled with good input");
|
||||
|
||||
info("edit to make the cc-number invalid");
|
||||
|
@ -518,6 +556,17 @@ add_task(async function test_numberCustomValidityReset() {
|
|||
form.dataset.updateButtonLabel = "Update";
|
||||
await form.promiseReady;
|
||||
display.appendChild(form);
|
||||
|
||||
let address1 = createAddressRecord(PTU.Addresses.TimBL, {guid: "TimBLGUID"});
|
||||
await form.requestStore.setState({
|
||||
request: {
|
||||
paymentMethods,
|
||||
paymentDetails: {},
|
||||
},
|
||||
savedAddresses: {
|
||||
[address1.guid]: deepClone(address1),
|
||||
},
|
||||
});
|
||||
await asyncElementRendered();
|
||||
|
||||
fillField(form.querySelector("#cc-number"), "junk");
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
Test the address-picker component
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test the billing-address-picker component</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="payments_common.js"></script>
|
||||
<script src="../../res/unprivileged-fallbacks.js"></script>
|
||||
<script src="autofillEditForms.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../../res/containers/rich-picker.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display">
|
||||
<billing-address-picker id="picker1"
|
||||
data-field-separator=", "
|
||||
data-invalid-label="Picker1: Missing or Invalid"
|
||||
selected-state-key="basic-card-page|billingAddressGUID"></billing-address-picker>
|
||||
<select id="theOptions">
|
||||
<option></option>
|
||||
<option value="48bnds6854t">48bnds6854t</option>
|
||||
<option value="68gjdh354j" selected="">68gjdh354j</option>
|
||||
</select>
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="module">
|
||||
/** Test the billing-address-picker component **/
|
||||
|
||||
import BillingAddressPicker from "../../res/containers/billing-address-picker.js";
|
||||
|
||||
let picker1 = document.getElementById("picker1");
|
||||
let addresses = {
|
||||
"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",
|
||||
timeLastUsed: 200,
|
||||
},
|
||||
"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",
|
||||
timeLastUsed: 300,
|
||||
},
|
||||
"abcde12345": {
|
||||
"address-level2": "Mountain View",
|
||||
"country": "US",
|
||||
"guid": "abcde12345",
|
||||
"name": "Mrs. Fields",
|
||||
timeLastUsed: 100,
|
||||
},
|
||||
};
|
||||
|
||||
add_task(async function test_empty() {
|
||||
ok(picker1, "Check picker1 exists");
|
||||
let {savedAddresses} = picker1.requestStore.getState();
|
||||
is(Object.keys(savedAddresses).length, 0, "Check empty initial state");
|
||||
is(picker1.editLink.hidden, true, "Check that picker edit link is hidden");
|
||||
is(picker1.options.length, 1, "Check only the empty option is present");
|
||||
ok(picker1.dropdown.selectedOption, "Has a selectedOption");
|
||||
is(picker1.dropdown.value, "", "Has empty value");
|
||||
|
||||
// update state to trigger render without changing available addresses
|
||||
picker1.requestStore.setState({
|
||||
"basic-card-page": {
|
||||
"someKey": "someValue",
|
||||
},
|
||||
});
|
||||
await asyncElementRendered();
|
||||
|
||||
is(picker1.dropdown.popupBox.children.length, 1, "Check only the empty option is present");
|
||||
ok(picker1.dropdown.selectedOption, "Has a selectedOption");
|
||||
is(picker1.dropdown.value, "", "Has empty value");
|
||||
});
|
||||
|
||||
add_task(async function test_getCurrentValue() {
|
||||
picker1.requestStore.setState({
|
||||
"basic-card-page": {
|
||||
"billingAddressGUID": "68gjdh354j",
|
||||
},
|
||||
savedAddresses: addresses,
|
||||
});
|
||||
await asyncElementRendered();
|
||||
|
||||
picker1.dropdown.popupBox.value = "abcde12345";
|
||||
|
||||
is(picker1.options.length, 4, "Check we have options for each address + empty one");
|
||||
is(picker1.getCurrentValue(picker1.requestStore.getState()), "abcde12345",
|
||||
"Initial/current value reflects the <select>.value, " +
|
||||
"not whatever is in the state at the selectedStateKey");
|
||||
});
|
||||
|
||||
add_task(async function test_wrapPopupBox() {
|
||||
let picker = new BillingAddressPicker();
|
||||
picker.dropdown.popupBox = document.querySelector("#theOptions");
|
||||
picker.dataset.invalidLabel = "Invalid";
|
||||
picker.setAttribute("label", "The label");
|
||||
picker.setAttribute("selected-state-key", "basic-card-page|billingAddressGUID");
|
||||
|
||||
document.querySelector("#display").appendChild(picker);
|
||||
|
||||
is(picker.labelElement.getAttribute("for"), "theOptions",
|
||||
"The label points at the right element");
|
||||
is(picker.invalidLabel.getAttribute("for"), "theOptions",
|
||||
"The invalidLabel points at the right element");
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче