--- a/browser/components/payments/content/paymentDialogWrapper.js
+++ b/browser/components/payments/content/paymentDialogWrapper.js
@@ -577,16 +577,21 @@ var paymentDialogWrapper = {
// Select the new record
if (selectedStateKey) {
Object.assign(successStateChange, {
[selectedStateKey]: guid,
});
}
+ const pageId = collectionName == "creditCards" ?
+ "basic-card-page" :
+ "address-page";
+ successStateChange[pageId].guid = guid;
+
this.sendMessageToContent("updateState", successStateChange);
} catch (ex) {
this.sendMessageToContent("updateState", errorStateChange);
}
},
/**
* @implements {nsIObserver}
--- a/browser/components/payments/res/containers/address-form.js
+++ b/browser/components/payments/res/containers/address-form.js
@@ -81,48 +81,49 @@ export default class AddressForm extends
super.connectedCallback();
});
}
render(state) {
let record = {};
let {
page,
+ "address-page": addressPage,
} = state;
if (this.id && page && page.id !== this.id) {
log.debug(`AddressForm: no need to further render inactive page: ${page.id}`);
return;
}
this.cancelButton.textContent = this.dataset.cancelButtonLabel;
this.backButton.textContent = this.dataset.backButtonLabel;
this.saveButton.textContent = this.dataset.saveButtonLabel;
this.persistCheckbox.label = this.dataset.persistCheckboxLabel;
this.backButton.hidden = page.onboardingWizard;
this.cancelButton.hidden = !page.onboardingWizard;
- if (page.addressFields) {
- this.setAttribute("address-fields", page.addressFields);
+ if (addressPage.addressFields) {
+ this.setAttribute("address-fields", addressPage.addressFields);
} else {
this.removeAttribute("address-fields");
}
- this.pageTitle.textContent = page.title;
+ this.pageTitle.textContent = addressPage.title;
this.genericErrorText.textContent = page.error;
- let editing = !!page.guid;
+ let editing = !!addressPage.guid;
let addresses = paymentRequest.getAddresses(state);
// If an address is selected we want to edit it.
if (editing) {
- record = addresses[page.guid];
+ record = addresses[addressPage.guid];
if (!record) {
- throw new Error("Trying to edit a non-existing address: " + page.guid);
+ throw new Error("Trying to edit a non-existing address: " + addressPage.guid);
}
// When editing an existing record, prevent changes to persistence
this.persistCheckbox.hidden = true;
} else {
// Adding a new record: default persistence to checked when in a not-private session
this.persistCheckbox.hidden = false;
this.persistCheckbox.checked = !state.isPrivate;
}
@@ -141,21 +142,29 @@ export default class AddressForm extends
onClick(evt) {
switch (evt.target) {
case this.cancelButton: {
paymentRequest.cancel();
break;
}
case this.backButton: {
- this.requestStore.setState({
+ let currentState = this.requestStore.getState();
+ const previousId = currentState.page.previousId;
+ let state = {
page: {
- id: "payment-summary",
+ id: previousId || "payment-summary",
},
- });
+ };
+ if (previousId) {
+ state[previousId] = Object.assign({}, currentState[previousId], {
+ preserveFieldValues: true,
+ });
+ }
+ this.requestStore.setState(state);
break;
}
case this.saveButton: {
this.saveRecord();
break;
}
default: {
throw new Error("Unexpected click target");
@@ -164,48 +173,61 @@ export default class AddressForm extends
}
saveRecord() {
let record = this.formHandler.buildFormObject();
let {
page,
tempAddresses,
savedBasicCards,
+ "address-page": addressPage,
+ "basic-card-page": basicCardPage,
} = this.requestStore.getState();
- let editing = !!page.guid;
+ let editing = !!addressPage.guid;
- if (editing ? (page.guid in tempAddresses) : !this.persistCheckbox.checked) {
+ if (editing ? (addressPage.guid in tempAddresses) : !this.persistCheckbox.checked) {
record.isTemporary = true;
}
let state = {
errorStateChange: {
page: {
id: "address-page",
onboardingWizard: page.onboardingWizard,
error: this.dataset.errorGenericSave,
},
+ "address-page": addressPage,
},
preserveOldProperties: true,
selectedStateKey: page.selectedStateKey,
};
if (page.onboardingWizard && !Object.keys(savedBasicCards).length) {
state.successStateChange = {
page: {
id: "basic-card-page",
- onboardingWizard: true,
- guid: null,
+ previousId: "address-page",
+ onboardingWizard: page.onboardingWizard,
},
};
} else {
state.successStateChange = {
page: {
- id: "payment-summary",
+ id: page.previousId || "payment-summary",
+ onboardingWizard: page.onboardingWizard,
},
};
}
- paymentRequest.updateAutofillRecord("addresses", record, page.guid, state);
+ state.successStateChange["address-page"] = addressPage;
+ state.successStateChange["basic-card-page"] = basicCardPage;
+
+ const previousId = page.previousId;
+ if (previousId) {
+ state.successStateChange[previousId].preserveFieldValues = true;
+ state.successStateChange[previousId].addressesModified = true;
+ }
+
+ paymentRequest.updateAutofillRecord("addresses", record, addressPage.guid, state);
}
}
customElements.define("address-form", AddressForm);
--- a/browser/components/payments/res/containers/address-picker.js
+++ b/browser/components/payments/res/containers/address-picker.js
@@ -163,32 +163,34 @@ export default class AddressPicker exten
});
}
}
onClick({target}) {
let nextState = {
page: {
id: "address-page",
+ },
+ "address-page": {
+ addressFields: this.getAttribute("address-fields"),
selectedStateKey: this.selectedStateKey,
- addressFields: this.getAttribute("address-fields"),
},
};
switch (target) {
case this.addLink: {
- nextState.page.guid = null;
- nextState.page.title = this.dataset.addAddressTitle;
+ nextState["address-page"].guid = null;
+ nextState["address-page"].title = this.dataset.addAddressTitle;
break;
}
case this.editLink: {
let state = this.requestStore.getState();
let selectedAddressGUID = state[this.selectedStateKey];
- nextState.page.guid = selectedAddressGUID;
- nextState.page.title = this.dataset.editAddressTitle;
+ nextState["address-page"].guid = selectedAddressGUID;
+ nextState["address-page"].title = this.dataset.editAddressTitle;
break;
}
default: {
throw new Error("Unexpected onClick");
}
}
this.requestStore.setState(nextState);
--- a/browser/components/payments/res/containers/basic-card-form.js
+++ b/browser/components/payments/res/containers/basic-card-form.js
@@ -22,16 +22,25 @@ export default class BasicCardForm exten
this.pageTitle = document.createElement("h1");
this.genericErrorText = document.createElement("div");
this.cancelButton = document.createElement("button");
this.cancelButton.className = "cancel-button";
this.cancelButton.addEventListener("click", this);
+ 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.backButton = document.createElement("button");
this.backButton.className = "back-button";
this.backButton.addEventListener("click", this);
this.saveButton = document.createElement("button");
this.saveButton.className = "save-button";
this.saveButton.addEventListener("click", this);
@@ -67,77 +76,95 @@ export default class BasicCardForm exten
let addresses = [];
this.formHandler = new EditCreditCard({
form,
}, record, addresses, {
isCCNumber: PaymentDialogUtils.isCCNumber,
getAddressLabel: PaymentDialogUtils.getAddressLabel,
});
+ let fragment = document.createDocumentFragment();
+ fragment.append(this.addressAddLink);
+ fragment.append(" ");
+ fragment.append(this.addressEditLink);
+ let billingAddressRow = this.form.querySelector(".billingAddressRow");
+ billingAddressRow.appendChild(fragment);
+
this.appendChild(this.persistCheckbox);
this.appendChild(this.genericErrorText);
this.appendChild(this.cancelButton);
this.appendChild(this.backButton);
this.appendChild(this.saveButton);
// Only call the connected super callback(s) once our markup is fully
// connected, including the shared form fetched asynchronously.
super.connectedCallback();
});
}
render(state) {
let {
page,
selectedShippingAddress,
+ "basic-card-page": basicCardPage,
} = state;
if (this.id && page && page.id !== this.id) {
log.debug(`BasicCardForm: no need to further render inactive page: ${page.id}`);
return;
}
this.cancelButton.textContent = this.dataset.cancelButtonLabel;
this.backButton.textContent = this.dataset.backButtonLabel;
this.saveButton.textContent = this.dataset.saveButtonLabel;
this.persistCheckbox.label = this.dataset.persistCheckboxLabel;
+ this.addressAddLink.textContent = this.dataset.addressAddLinkLabel;
+ this.addressEditLink.textContent = this.dataset.addressEditLinkLabel;
// The back button is temporarily hidden(See Bug 1462461).
this.backButton.hidden = !!page.onboardingWizard;
this.cancelButton.hidden = !page.onboardingWizard;
let record = {};
let basicCards = paymentRequest.getBasicCards(state);
let addresses = paymentRequest.getAddresses(state);
this.genericErrorText.textContent = page.error;
- let editing = !!page.guid;
+ let editing = !!basicCardPage.guid;
this.form.querySelector("#cc-number").disabled = editing;
// If a card is selected we want to edit it.
if (editing) {
this.pageTitle.textContent = this.dataset.editBasicCardTitle;
- record = basicCards[page.guid];
+ record = basicCards[basicCardPage.guid];
if (!record) {
- throw new Error("Trying to edit a non-existing card: " + page.guid);
+ throw new Error("Trying to edit a non-existing card: " + basicCardPage.guid);
}
// When editing an existing record, prevent changes to persistence
this.persistCheckbox.hidden = true;
} else {
this.pageTitle.textContent = this.dataset.addBasicCardTitle;
// Use a currently selected shipping address as the default billing address
- if (selectedShippingAddress) {
+ if (!record.billingAddressGUID && selectedShippingAddress) {
record.billingAddressGUID = selectedShippingAddress;
}
// Adding a new record: default persistence to checked when in a not-private session
this.persistCheckbox.hidden = false;
this.persistCheckbox.checked = !state.isPrivate;
}
- this.formHandler.loadRecord(record, addresses);
+ this.formHandler.loadRecord(record, addresses, basicCardPage.preserveFieldValues);
+
+ this.form.querySelector(".billingAddressRow").hidden = false;
+
+ if (basicCardPage.addressesModified) {
+ let addressGuid = state["address-page"].guid;
+ let billingAddressSelect = this.form.querySelector("#billingAddressGUID");
+ billingAddressSelect.value = addressGuid;
+ }
}
handleEvent(event) {
switch (event.type) {
case "click": {
this.onClick(event);
break;
}
@@ -145,16 +172,45 @@ export default class BasicCardForm exten
}
onClick(evt) {
switch (evt.target) {
case this.cancelButton: {
paymentRequest.cancel();
break;
}
+ case this.addressAddLink:
+ case this.addressEditLink: {
+ let {
+ "basic-card-page": basicCardPage,
+ } = this.requestStore.getState();
+ let nextState = {
+ page: {
+ id: "address-page",
+ previousId: "basic-card-page",
+ },
+ "address-page": {
+ guid: null,
+ title: this.dataset.billingAddressTitleAdd,
+ },
+ "basic-card-page": {
+ preserveFieldValues: true,
+ guid: basicCardPage.guid,
+ },
+ };
+ 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: {
this.requestStore.setState({
page: {
id: "payment-summary",
},
});
break;
}
@@ -165,47 +221,56 @@ export default class BasicCardForm exten
default: {
throw new Error("Unexpected click target");
}
}
}
saveRecord() {
let record = this.formHandler.buildFormObject();
+ let currentState = this.requestStore.getState();
let {
page,
tempBasicCards,
- } = this.requestStore.getState();
- let editing = !!page.guid;
+ "basic-card-page": basicCardPage,
+ } = currentState;
+ let editing = !!basicCardPage.guid;
- if (editing ? (page.guid in tempBasicCards) : !this.persistCheckbox.checked) {
+ if (editing ? (basicCardPage.guid in tempBasicCards) : !this.persistCheckbox.checked) {
record.isTemporary = true;
}
for (let editableFieldName of ["cc-name", "cc-exp-month", "cc-exp-year"]) {
record[editableFieldName] = record[editableFieldName] || "";
}
// Only save the card number if we're saving a new record, otherwise we'd
// overwrite the unmasked card number with the masked one.
if (!editing) {
record["cc-number"] = record["cc-number"] || "";
}
- paymentRequest.updateAutofillRecord("creditCards", record, page.guid, {
+ let state = {
errorStateChange: {
page: {
id: "basic-card-page",
error: this.dataset.errorGenericSave,
},
},
preserveOldProperties: true,
selectedStateKey: "selectedPaymentCard",
successStateChange: {
page: {
id: "payment-summary",
},
},
- });
+ };
+
+ const previousId = page.previousId;
+ if (previousId) {
+ state.successStateChange[previousId] = Object.assign({}, currentState[previousId]);
+ }
+
+ paymentRequest.updateAutofillRecord("creditCards", record, basicCardPage.guid, state);
}
}
customElements.define("basic-card-form", BasicCardForm);
--- a/browser/components/payments/res/containers/payment-method-picker.js
+++ b/browser/components/payments/res/containers/payment-method-picker.js
@@ -128,27 +128,28 @@ export default class PaymentMethodPicker
this.requestStore.setState(stateChange);
}
onClick({target}) {
let nextState = {
page: {
id: "basic-card-page",
},
+ "basic-card-page": {},
};
switch (target) {
case this.addLink: {
- nextState.page.guid = null;
+ nextState["basic-card-page"].guid = null;
break;
}
case this.editLink: {
let state = this.requestStore.getState();
let selectedPaymentCardGUID = state[this.selectedStateKey];
- nextState.page.guid = selectedPaymentCardGUID;
+ nextState["basic-card-page"].guid = selectedPaymentCardGUID;
break;
}
default: {
throw new Error("Unexpected onClick");
}
}
this.requestStore.setState(nextState);
--- a/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js
+++ b/browser/components/payments/res/mixins/PaymentStateSubscriberMixin.js
@@ -10,20 +10,31 @@ import PaymentsStore from "../PaymentsSt
/**
* State of the payment request dialog.
*/
export let requestStore = new PaymentsStore({
changesPrevented: false,
completionState: "initial",
orderDetailsShowing: false,
+ "basic-card-page": {
+ guid: null,
+ // preserveFieldValues: true,
+ },
+ "address-page": {
+ guid: null,
+ title: "",
+ },
+ "payment-summary": {
+ },
page: {
id: "payment-summary",
+ previousId: null,
// onboardingWizard: true,
- // error: "My error",
+ // error: "",
},
request: {
tabId: null,
topLevelPrincipal: {URI: {displayHost: null}},
requestId: null,
paymentMethods: [],
paymentDetails: {
id: null,
--- a/browser/components/payments/res/paymentRequest.js
+++ b/browser/components/payments/res/paymentRequest.js
@@ -129,17 +129,16 @@ var paymentRequest = {
state.page = {
id: "address-page",
onboardingWizard: true,
};
} else if (Object.keys(detail.savedBasicCards).length == 0) {
state.page = {
id: "basic-card-page",
onboardingWizard: true,
- guid: null,
};
}
document.querySelector("payment-dialog").setStateFromParent(state);
},
cancel() {
this.sendMessageToChrome("paymentCancel");
--- a/browser/components/payments/res/paymentRequest.xhtml
+++ b/browser/components/payments/res/paymentRequest.xhtml
@@ -20,30 +20,34 @@
<!ENTITY payer.addLink.label "Add">
<!ENTITY payer.editLink.label "Edit">
<!ENTITY shippingAddress.addPage.title "Add Shipping Address">
<!ENTITY shippingAddress.editPage.title "Edit Shipping Address">
<!ENTITY deliveryAddress.addPage.title "Add Delivery Address">
<!ENTITY deliveryAddress.editPage.title "Edit Delivery Address">
<!ENTITY pickupAddress.addPage.title "Add Pickup Address">
<!ENTITY pickupAddress.editPage.title "Edit Pickup Address">
+ <!ENTITY billingAddress.addPage.title "Add Billing Address">
+ <!ENTITY billingAddress.editPage.title "Edit Billing Address">
<!ENTITY basicCard.addPage.title "Add Credit Card">
<!ENTITY basicCard.editPage.title "Edit Credit Card">
<!ENTITY payer.addPage.title "Add Payer Contact">
<!ENTITY payer.editPage.title "Edit Payer Contact">
<!ENTITY payerLabel "Contact Information">
<!ENTITY cancelPaymentButton.label "Cancel">
<!ENTITY approvePaymentButton.label "Pay">
<!ENTITY processingPaymentButton.label "Processing">
<!ENTITY successPaymentButton.label "Done">
<!ENTITY failPaymentButton.label "Fail">
<!ENTITY unknownPaymentButton.label "Unknown">
<!ENTITY orderDetailsLabel "Order Details">
<!ENTITY orderTotalLabel "Total">
<!ENTITY basicCardPage.error.genericSave "There was an error saving the payment card.">
+ <!ENTITY basicCardPage.addressAddLink.label "Add">
+ <!ENTITY basicCardPage.addressEditLink.label "Edit">
<!ENTITY basicCardPage.backButton.label "Back">
<!ENTITY basicCardPage.saveButton.label "Save">
<!ENTITY basicCardPage.persistCheckbox.label "Save credit card to Firefox (Security code will not be saved)">
<!ENTITY addressPage.error.genericSave "There was an error saving the address.">
<!ENTITY addressPage.cancelButton.label "Cancel">
<!ENTITY addressPage.backButton.label "Back">
<!ENTITY addressPage.saveButton.label "Save">
<!ENTITY addressPage.persistCheckbox.label "Save address to Firefox">
@@ -133,16 +137,20 @@
<order-details></order-details>
</section>
<basic-card-form id="basic-card-page"
class="page"
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-billing-address-title-add="&billingAddress.addPage.title;"
+ data-billing-address-title-edit="&billingAddress.editPage.title;"
data-back-button-label="&basicCardPage.backButton.label;"
data-save-button-label="&basicCardPage.saveButton.label;"
data-cancel-button-label="&cancelPaymentButton.label;"
data-persist-checkbox-label="&basicCardPage.persistCheckbox.label;"
hidden="hidden"></basic-card-form>
<address-form id="address-page"
class="page"
--- a/browser/components/payments/test/browser/browser_address_edit.js
+++ b/browser/components/payments/test/browser/browser_address_edit.js
@@ -49,25 +49,25 @@ add_task(async function test_add_link()
}, "No saved addresses when starting test");
let addLink = content.document.querySelector("address-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
addLink.click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "address-page" && !state.page.guid;
+ return state.page.id == "address-page" && !state["address-page"].guid;
}, "Check add page state");
let title = content.document.querySelector("address-form h1");
is(title.textContent, "Add Shipping Address", "Page title should be set");
- let persistInput = content.document.querySelector("address-form labelled-checkbox");
- ok(!persistInput.hidden, "checkbox should be visible when adding a new address");
- ok(Cu.waiveXrays(persistInput).checked, "persist checkbox should be checked by default");
+ let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
+ ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
+ ok(Cu.waiveXrays(persistCheckbox).checked, "persist checkbox should be checked by default");
info("filling fields");
for (let [key, val] of Object.entries(address)) {
let field = content.document.getElementById(key);
if (!field) {
ok(false, `${key} field not found`);
}
field.value = val;
@@ -140,24 +140,24 @@ add_task(async function test_edit_link()
Cu.waiveXrays(picker).dropdown.popupBox.children[0].click();
let editLink = content.document.querySelector("address-picker .edit-link");
is(editLink.textContent, "Edit", "Edit link text");
editLink.click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "address-page" && !!state.page.guid;
+ return state.page.id == "address-page" && !!state["address-page"].guid;
}, "Check edit page state");
let title = content.document.querySelector("address-form h1");
is(title.textContent, "Edit Shipping Address", "Page title should be set");
- let persistInput = content.document.querySelector("address-form labelled-checkbox");
- ok(persistInput.hidden, "checkbox should be hidden when editing an address");
+ let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
+ ok(persistCheckbox.hidden, "checkbox should be hidden when editing an address");
info("overwriting field values");
for (let [key, val] of Object.entries(address)) {
let field = content.document.getElementById(key);
field.value = val;
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
}
@@ -225,25 +225,25 @@ add_task(async function test_add_payer_c
}, "No saved addresses when starting test");
let addLink = content.document.querySelector("address-picker.payer-related .add-link");
is(addLink.textContent, "Add", "Add link text");
addLink.click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "address-page" && !state.page.guid;
+ return state.page.id == "address-page" && !state["address-page"].guid;
}, "Check add page state");
let title = content.document.querySelector("address-form h1");
is(title.textContent, "Add Payer Contact", "Page title should be set");
- let persistInput = content.document.querySelector("address-form labelled-checkbox");
- ok(!persistInput.hidden, "checkbox should be visible when adding a new address");
- ok(Cu.waiveXrays(persistInput).checked, "persist checkbox should be checked by default");
+ let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
+ ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
+ ok(Cu.waiveXrays(persistCheckbox).checked, "persist checkbox should be checked by default");
info("filling fields");
for (let [key, val] of Object.entries(address)) {
let field = content.document.getElementById(key);
if (!field) {
ok(false, `${key} field not found`);
}
field.value = val;
@@ -312,25 +312,24 @@ add_task(async function test_edit_payer_
let editLink =
content.document.querySelector("address-picker.payer-related .edit-link");
is(editLink.textContent, "Edit", "Edit link text");
editLink.click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- info("state.page.id: " + state.page.id + "; state.page.guid: " + state.page.guid);
- return state.page.id == "address-page" && !!state.page.guid;
+ return state.page.id == "address-page" && !!state["address-page"].guid;
}, "Check edit page state");
let title = content.document.querySelector("address-form h1");
is(title.textContent, "Edit Payer Contact", "Page title should be set");
- let persistInput = content.document.querySelector("address-form labelled-checkbox");
- ok(persistInput.hidden, "checkbox should be hidden when editing an address");
+ let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
+ ok(persistCheckbox.hidden, "checkbox should be hidden when editing an address");
info("overwriting field values");
for (let [key, val] of Object.entries(address)) {
let field = content.document.getElementById(key);
field.value = val + "1";
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
}
@@ -432,19 +431,20 @@ add_task(async function test_private_per
// add an address
// (return to summary view)
info("add an address");
await spawnPaymentDialogTask(frame, async () => {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
- let persistInput = content.document.querySelector("address-form labelled-checkbox");
- ok(!persistInput.hidden, "checkbox should be visible when adding a new address");
- ok(!Cu.waiveXrays(persistInput).checked, "persist checkbox should be unchecked by default");
+ let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
+ ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
+ ok(!Cu.waiveXrays(persistCheckbox).checked,
+ "persist checkbox should be unchecked by default");
info("add the temp address");
let addressToAdd = PTU.Addresses.Temp;
for (let [key, val] of Object.entries(addressToAdd)) {
let field = content.document.getElementById(key);
field.value = val;
}
content.document.querySelector("address-form button:last-of-type").click();
--- a/browser/components/payments/test/browser/browser_card_edit.js
+++ b/browser/components/payments/test/browser/browser_card_edit.js
@@ -13,55 +13,128 @@ add_task(async function test_add_link()
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
let addLink = content.document.querySelector("payment-method-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
addLink.click();
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "basic-card-page" && !state.page.guid;
+ return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
}, "Check add page state");
+ state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+ return Object.keys(state.savedBasicCards).length == 0 &&
+ Object.keys(state.savedAddresses).length == 0;
+ }, "Check no cards or addresses present at beginning of test");
+
let title = content.document.querySelector("basic-card-form h1");
is(title.textContent, "Add Credit Card", "Add title should be set");
ok(!state.isPrivate,
"isPrivate flag is not set when paymentrequest is shown from a non-private session");
- let persistInput = content.document.querySelector("basic-card-form labelled-checkbox");
- ok(Cu.waiveXrays(persistInput).checked, "persist checkbox should be checked by default");
+ let persistCheckbox = content.document.querySelector("basic-card-form labelled-checkbox");
+ ok(Cu.waiveXrays(persistCheckbox).checked, "persist checkbox should be checked by default");
let year = (new Date()).getFullYear();
let card = {
"cc-number": "4111111111111111",
"cc-name": "J. Smith",
"cc-exp-month": 11,
"cc-exp-year": year,
};
info("filling fields");
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`);
}
+ let billingAddressSelect = content.document.querySelector("#billingAddressGUID");
+ isnot(billingAddressSelect.getBoundingClientRect().height, 0,
+ "The billing address selector should always be visible");
+ is(billingAddressSelect.childElementCount, 1,
+ "Only one child option should exist by default");
+ is(billingAddressSelect.children[0].value, "",
+ "The only option should be the blank/empty option");
+
+ 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;
+ }, "Check address page state");
+
+ let addressTitle = content.document.querySelector("address-form h1");
+ is(addressTitle.textContent, "Add Billing Address",
+ "Address on add address page should be correct");
+
+ state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+ return Object.keys(state.savedBasicCards).length == 0;
+ }, "Check card was not added when clicking the 'add' address button");
+
+ 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 == 0;
+ }, "Check basic-card page, but card should not be saved and no addresses present");
+
+ is(title.textContent, "Add Credit Card", "Add title should be still be on credit card page");
+
+ 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");
+ ok(!field.disabled, "Fields should still be enabled for editing");
+ }
+
+ addressAddLink.click();
+ state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+ return state.page.id == "address-page" && !state["address-page"].guid;
+ }, "Check address page state");
+
+ info("filling address fields");
+ for (let [key, val] of Object.entries(PTU.Addresses.TimBL)) {
+ let field = content.document.getElementById(key);
+ if (!field) {
+ ok(false, `${key} field not found`);
+ }
+ field.value = val;
+ ok(!field.disabled, `Field #${key} shouldn't be disabled`);
+ }
+
+ content.document.querySelector("address-form button:last-of-type").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 address was added and we're back on basic-card page (add)");
+
+ is(billingAddressSelect.childElementCount, 2,
+ "Two options should exist in the billingAddressSelect");
+ let selectedOption =
+ billingAddressSelect.children[billingAddressSelect.selectedIndex];
+ let selectedAddressGuid = selectedOption.value;
+ is(selectedAddressGuid, Object.values(state.savedAddresses)[0].guid,
+ "The select should have the new address selected");
+
content.document.querySelector("basic-card-form button:last-of-type").click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
return Object.keys(state.savedBasicCards).length == 1;
- }, "Check card was added");
+ }, "Check card was not added again");
let cardGUIDs = Object.keys(state.savedBasicCards);
is(cardGUIDs.length, 1, "Check there is one card");
let savedCard = state.savedBasicCards[cardGUIDs[0]];
card["cc-number"] = "************1111"; // Card should be masked
for (let [key, val] of Object.entries(card)) {
is(savedCard[key], val, "Check " + key);
}
+ is(savedCard.billingAddressGUID, selectedAddressGuid,
+ "The saved card should be associated with the billing address");
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "payment-summary";
}, "Switched back to payment-summary");
}, args);
});
add_task(async function test_edit_link() {
@@ -75,19 +148,24 @@ add_task(async function test_edit_link()
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
let editLink = content.document.querySelector("payment-method-picker .edit-link");
is(editLink.textContent, "Edit", "Edit link text");
editLink.click();
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "basic-card-page" && !!state.page.guid;
+ 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");
+
let title = content.document.querySelector("basic-card-form h1");
is(title.textContent, "Edit Credit Card", "Edit title should be set");
let nextYear = (new Date()).getFullYear() + 1;
let card = {
// cc-number cannot be modified
"cc-name": "A. Nonymous",
"cc-exp-month": 3,
@@ -97,16 +175,100 @@ add_task(async function test_edit_link()
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 billing address set by the previous test 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 h1");
+ 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("filling address fields");
+ for (let [key, val] of Object.entries(PTU.Addresses.TimBL)) {
+ let field = content.document.getElementById(key);
+ if (!field) {
+ ok(false, `${key} field not found`);
+ }
+ field.value = val + "1";
+ ok(!field.disabled, `Field #${key} shouldn't be disabled`);
+ }
+
+ content.document.querySelector("address-form button:last-of-type").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 + "1",
+ "Check that address was edited and saved");
+
content.document.querySelector("basic-card-form button:last-of-type").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");
@@ -135,47 +297,47 @@ add_task(async function test_private_per
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
let addLink = content.document.querySelector("payment-method-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
addLink.click();
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "basic-card-page" && !state.page.guid;
+ return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
},
"Check add page state");
ok(!state.isPrivate,
"isPrivate flag is not set when paymentrequest is shown from a non-private session");
- let persistInput = content.document.querySelector("basic-card-form labelled-checkbox");
- ok(Cu.waiveXrays(persistInput).checked,
+ let persistCheckbox = content.document.querySelector("basic-card-form labelled-checkbox");
+ ok(Cu.waiveXrays(persistCheckbox).checked,
"checkbox is checked by default from a non-private session");
}, args);
let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
await spawnInDialogForMerchantTask(PTU.ContentTasks.createAndShowRequest, async function check() {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
let addLink = content.document.querySelector("payment-method-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
addLink.click();
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "basic-card-page" && !state.page.guid;
+ return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
},
"Check add page state");
ok(state.isPrivate,
"isPrivate flag is set when paymentrequest is shown from a private session");
- let persistInput = content.document.querySelector("labelled-checkbox");
- ok(!Cu.waiveXrays(persistInput).checked,
+ let persistCheckbox = content.document.querySelector("labelled-checkbox");
+ ok(!Cu.waiveXrays(persistCheckbox).checked,
"checkbox is not checked by default from a private session");
}, args, {
browser: privateWin.gBrowser,
});
await BrowserTestUtils.closeWindow(privateWin);
});
add_task(async function test_private_card_adding() {
@@ -190,17 +352,17 @@ add_task(async function test_private_car
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
let addLink = content.document.querySelector("payment-method-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
addLink.click();
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
- return state.page.id == "basic-card-page" && !state.page.guid;
+ return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
},
"Check add page state");
let savedCardCount = Object.keys(state.savedBasicCards).length;
let tempCardCount = Object.keys(state.tempBasicCards).length;
let year = (new Date()).getFullYear();
let card = {
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -69,17 +69,19 @@ add_task(async function test_initialStat
form.remove();
});
add_task(async function test_backButton() {
let form = new AddressForm();
form.dataset.backButtonLabel = "Back";
await form.requestStore.setState({
page: {
- id: "test-page",
+ id: "address-page",
+ },
+ "address-page": {
title: "Sample page title",
},
});
await form.promiseReady;
display.appendChild(form);
await asyncElementRendered();
let stateChangePromise = promiseStateChange(form.requestStore);
@@ -133,16 +135,19 @@ add_task(async function test_saveButton(
isDeeply(details, {
collectionName: "addresses",
errorStateChange: {
page: {
id: "address-page",
error: "Generic error",
onboardingWizard: undefined,
},
+ "address-page": {
+ title: "Sample page title",
+ },
},
guid: undefined,
messageType: "updateAutofillRecord",
preserveOldProperties: true,
record: {
"given-name": "Jaws",
"family-name": "Swaj",
"organization": "Allizom",
@@ -153,16 +158,23 @@ add_task(async function test_saveButton(
"country": "US",
"email": "test@example.com",
"tel": "+15555551212",
},
selectedStateKey: undefined,
successStateChange: {
page: {
id: "payment-summary",
+ onboardingWizard: undefined,
+ },
+ "address-page": {
+ title: "Sample page title",
+ },
+ "basic-card-page": {
+ guid: null,
},
},
}, "Check event details for the message to chrome");
form.remove();
});
add_task(async function test_genericError() {
let form = new AddressForm();
@@ -188,16 +200,18 @@ add_task(async function test_edit() {
await asyncElementRendered();
let address1 = deepClone(PTU.Addresses.TimBL);
address1.guid = "9864798564";
await form.requestStore.setState({
page: {
id: "address-page",
+ },
+ "address-page": {
guid: address1.guid,
},
savedAddresses: {
[address1.guid]: deepClone(address1),
},
});
await asyncElementRendered();
checkAddressForm(form, address1);
@@ -205,30 +219,33 @@ add_task(async function test_edit() {
info("test change to minimal record");
let minimalAddress = {
"given-name": address1["given-name"],
guid: "9gnjdhen46",
};
await form.requestStore.setState({
page: {
id: "address-page",
+ },
+ "address-page": {
guid: minimalAddress.guid,
},
savedAddresses: {
[minimalAddress.guid]: deepClone(minimalAddress),
},
});
await asyncElementRendered();
checkAddressForm(form, minimalAddress);
info("change to no selected address");
await form.requestStore.setState({
page: {
id: "address-page",
},
+ "address-page": {},
});
await asyncElementRendered();
checkAddressForm(form, {});
form.remove();
});
add_task(async function test_restricted_address_fields() {
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -64,17 +64,19 @@ add_task(async function test_initialStat
});
add_task(async function test_backButton() {
let form = new BasicCardForm();
form.dataset.backButtonLabel = "Back";
form.dataset.addBasicCardTitle = "Sample page title 2";
await form.requestStore.setState({
page: {
- id: "test-page",
+ id: "basic-card-page",
+ },
+ "basic-card-page": {
},
});
await form.promiseReady;
display.appendChild(form);
await asyncElementRendered();
let stateChangePromise = promiseStateChange(form.requestStore);
is(form.pageTitle.textContent, "Sample page title 2", "Check title");
@@ -239,16 +241,18 @@ add_task(async function test_edit() {
info("test year before current");
let card1 = deepClone(PTU.BasicCards.JohnDoe);
card1.guid = "9864798564";
card1["cc-exp-year"] = 2011;
await form.requestStore.setState({
page: {
id: "basic-card-page",
+ },
+ "basic-card-page": {
guid: card1.guid,
},
savedBasicCards: {
[card1.guid]: deepClone(card1),
},
});
await asyncElementRendered();
checkCCForm(form, card1);
@@ -268,30 +272,35 @@ add_task(async function test_edit() {
let minimalCard = {
// no expiration date or name
"cc-number": "1234567690123",
guid: "9gnjdhen46",
};
await form.requestStore.setState({
page: {
id: "basic-card-page",
+ },
+ "basic-card-page": {
guid: minimalCard.guid,
},
savedBasicCards: {
[minimalCard.guid]: deepClone(minimalCard),
},
});
await asyncElementRendered();
checkCCForm(form, minimalCard);
info("change to no selected card");
await form.requestStore.setState({
page: {
id: "basic-card-page",
},
+ "basic-card-page": {
+ guid: null,
+ },
});
await asyncElementRendered();
checkCCForm(form, {});
form.remove();
});
</script>
--- a/browser/extensions/formautofill/content/autofillEditForms.js
+++ b/browser/extensions/formautofill/content/autofillEditForms.js
@@ -192,23 +192,26 @@ class EditCreditCard extends EditAutofil
billingAddress: this._elements.form.querySelector("#billingAddressGUID"),
billingAddressRow: this._elements.form.querySelector(".billingAddressRow"),
});
this.loadRecord(record, addresses);
this.attachEventListeners();
}
- loadRecord(record, addresses) {
+ loadRecord(record, addresses, preserveFieldValues) {
// _record must be updated before generateYears and generateBillingAddressOptions are called.
this._record = record;
this._addresses = addresses;
- this.generateYears();
this.generateBillingAddressOptions();
- super.loadRecord(record);
+ if (!preserveFieldValues) {
+ // Re-generating the years will reset the selected option.
+ this.generateYears();
+ super.loadRecord(record);
+ }
}
generateYears() {
const count = 11;
const currentYear = new Date().getFullYear();
const ccExpYear = this._record && this._record["cc-exp-year"];
// Clear the list