--- a/browser/extensions/formautofill/content/autofillEditForms.js
+++ b/browser/extensions/formautofill/content/autofillEditForms.js
@@ -24,17 +24,17 @@ class EditAutofillForm {
}
/**
* Get inputs from the form.
* @returns {object}
*/
buildFormObject() {
return Array.from(this._elements.form.elements).reduce((obj, input) => {
- if (input.value) {
+ if (input.value && !input.disabled) {
obj[input.id] = input.value;
}
return obj;
}, {});
}
/**
* Handle events
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -983,33 +983,33 @@
"is-fullwidth-code-point": "2.0.0"
}
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
"integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
},
- "string_decoder": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
- "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
"string-width": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
"integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
"requires": {
"is-fullwidth-code-point": "2.0.0",
"strip-ansi": "4.0.0"
}
},
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
"strip-ansi": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
"integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
"requires": {
"ansi-regex": "3.0.0"
},
"dependencies": {
--- a/toolkit/components/payments/content/paymentDialogWrapper.js
+++ b/toolkit/components/payments/content/paymentDialogWrapper.js
@@ -458,16 +458,53 @@ var paymentDialogWrapper = {
onChangeShippingOption({optionID}) {
// Note, failing here on browser_host_name.js because the test closes
// the dialog before the onChangeShippingOption is called, thus
// deleting the request and making the requestId invalid. Unclear
// why we aren't seeing the same issue with onChangeShippingAddress.
paymentSrv.changeShippingOption(this.request.requestId, optionID);
},
+ async onUpdateAutofillRecord(collectionName, record, guid, {
+ errorStateChange,
+ preserveOldProperties,
+ selectedStateKey,
+ successStateChange,
+ }) {
+ if (collectionName == "creditCards" && !guid) {
+ // We need to be logged in so we can encrypt the credit card number and
+ // that's only supported when we're adding a new record.
+ // TODO: "MasterPassword.ensureLoggedIn" can be removed after the storage
+ // APIs are refactored to be async functions (bug 1399367).
+ if (!await MasterPassword.ensureLoggedIn()) {
+ Cu.reportError("User canceled master password entry");
+ return;
+ }
+ }
+
+ try {
+ if (guid) {
+ await formAutofillStorage[collectionName].update(guid, record, preserveOldProperties);
+ } else {
+ guid = await formAutofillStorage[collectionName].add(record);
+ }
+
+ // Select the new record
+ if (selectedStateKey) {
+ Object.assign(successStateChange, {
+ [selectedStateKey]: guid,
+ });
+ }
+
+ this.sendMessageToContent("updateState", successStateChange);
+ } catch (ex) {
+ this.sendMessageToContent("updateState", errorStateChange);
+ }
+ },
+
/**
* @implements {nsIObserver}
* @param {nsISupports} subject
* @param {string} topic
* @param {string} data
*/
observe(subject, topic, data) {
switch (topic) {
@@ -504,16 +541,25 @@ var paymentDialogWrapper = {
case "paymentCancel": {
this.onPaymentCancel();
break;
}
case "pay": {
this.onPay(data);
break;
}
+ case "updateAutofillRecord": {
+ this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, {
+ errorStateChange: data.errorStateChange,
+ preserveOldProperties: data.preserveOldProperties,
+ selectedStateKey: data.selectedStateKey,
+ successStateChange: data.successStateChange,
+ });
+ break;
+ }
}
},
};
if ("document" in this) {
// Running in a browser, not a unit test
let frame = document.getElementById("paymentRequestFrame");
let requestId = (new URLSearchParams(window.location.search)).get("requestId");
--- a/toolkit/components/payments/res/containers/basic-card-form.js
+++ b/toolkit/components/payments/res/containers/basic-card-form.js
@@ -1,32 +1,38 @@
/* 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-globals-from ../../../../../browser/extensions/formautofill/content/autofillEditForms.js*/
/* import-globals-from ../mixins/PaymentStateSubscriberMixin.js */
/* import-globals-from ../unprivileged-fallbacks.js */
+/* import-globals-from ../paymentRequest.js */
"use strict";
/**
* <basic-card-form></basic-card-form>
*
* XXX: Bug 1446164 - This form isn't localized when used via this custom element
* as it will be much easier to share the logic once we switch to Fluent.
*/
class BasicCardForm extends PaymentStateSubscriberMixin(HTMLElement) {
constructor() {
super();
+ this.genericErrorText = document.createElement("div");
+
this.backButton = document.createElement("button");
this.backButton.addEventListener("click", this);
+ this.saveButton = document.createElement("button");
+ this.saveButton.addEventListener("click", this);
+
// The markup is shared with form autofill preferences.
let url = "formautofill/editCreditCard.xhtml";
this.promiseReady = this._fetchMarkup(url).then(doc => {
this.form = doc.getElementById("form");
return this.form;
});
}
@@ -49,57 +55,107 @@ class BasicCardForm extends PaymentState
let record = {};
this.formHandler = new EditCreditCard({
form,
}, record, {
isCCNumber: PaymentDialogUtils.isCCNumber,
});
+ this.appendChild(this.genericErrorText);
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) {
this.backButton.textContent = this.dataset.backButtonLabel;
+ this.saveButton.textContent = this.dataset.saveButtonLabel;
let record = {};
let {
- selectedPaymentCard,
+ page,
savedBasicCards,
} = state;
- let editing = !!state.selectedPaymentCard;
+ this.genericErrorText.textContent = page.error;
+
+ let editing = !!page.guid;
this.form.querySelector("#cc-number").disabled = editing;
// If a card is selected we want to edit it.
if (editing) {
- record = savedBasicCards[selectedPaymentCard];
+ record = savedBasicCards[page.guid];
if (!record) {
- throw new Error("Trying to edit a non-existing card: " + selectedPaymentCard);
+ throw new Error("Trying to edit a non-existing card: " + page.guid);
}
}
this.formHandler.loadRecord(record);
}
handleEvent(event) {
switch (event.type) {
case "click": {
this.onClick(event);
break;
}
}
}
onClick(evt) {
- this.requestStore.setState({
- page: {
- id: "payment-summary",
+ switch (evt.target) {
+ case this.backButton: {
+ this.requestStore.setState({
+ page: {
+ id: "payment-summary",
+ },
+ });
+ break;
+ }
+ case this.saveButton: {
+ this.saveRecord();
+ break;
+ }
+ default: {
+ throw new Error("Unexpected click target");
+ }
+ }
+ }
+
+ saveRecord() {
+ let record = this.formHandler.buildFormObject();
+ let {
+ page,
+ } = this.requestStore.getState();
+
+ 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 (!page.guid) {
+ record["cc-number"] = record["cc-number"] || "";
+ }
+
+ paymentRequest.updateAutofillRecord("creditCards", record, page.guid, {
+ errorStateChange: {
+ page: {
+ id: "basic-card-page",
+ error: this.dataset.errorGenericSave,
+ },
+ },
+ preserveOldProperties: true,
+ selectedStateKey: "selectedPaymentCard",
+ successStateChange: {
+ page: {
+ id: "payment-summary",
+ },
},
});
}
}
customElements.define("basic-card-form", BasicCardForm);
--- a/toolkit/components/payments/res/containers/payment-method-picker.js
+++ b/toolkit/components/payments/res/containers/payment-method-picker.js
@@ -130,20 +130,23 @@ class PaymentMethodPicker extends Paymen
let nextState = {
page: {
id: "basic-card-page",
},
};
switch (target) {
case this.addLink: {
- nextState.selectedPaymentCard = null;
+ nextState.page.guid = null;
break;
}
case this.editLink: {
+ let state = this.requestStore.getState();
+ let selectedPaymentCardGUID = state[this.selectedStateKey];
+ nextState.page.guid = selectedPaymentCardGUID;
break;
}
default: {
throw new Error("Unexpected onClick");
}
}
this.requestStore.setState(nextState);
--- a/toolkit/components/payments/res/paymentRequest.js
+++ b/toolkit/components/payments/res/paymentRequest.js
@@ -126,15 +126,40 @@ var paymentRequest = {
changeShippingAddress(data) {
this.sendMessageToChrome("changeShippingAddress", data);
},
changeShippingOption(data) {
this.sendMessageToChrome("changeShippingOption", data);
},
+ /**
+ * Add/update an autofill storage record.
+ *
+ * If the the `guid` argument is provided update the record; otherwise, add it.
+ * @param {string} collectionName The autofill collection that record belongs to.
+ * @param {object} record The autofill record to add/update
+ * @param {string} [guid] The guid of the autofill record to update
+ */
+ updateAutofillRecord(collectionName, record, guid, {
+ errorStateChange,
+ preserveOldProperties,
+ selectedStateKey,
+ successStateChange,
+ }) {
+ this.sendMessageToChrome("updateAutofillRecord", {
+ collectionName,
+ guid,
+ record,
+ errorStateChange,
+ preserveOldProperties,
+ selectedStateKey,
+ successStateChange,
+ });
+ },
+
onPaymentRequestUnload() {
// remove listeners that may be used multiple times here
window.removeEventListener("paymentChromeToContent", this);
},
};
paymentRequest.init();
--- a/toolkit/components/payments/res/paymentRequest.xhtml
+++ b/toolkit/components/payments/res/paymentRequest.xhtml
@@ -19,17 +19,19 @@
<!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.backButton.label "Back">
+ <!ENTITY basicCardPage.saveButton.label "Save">
]>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&paymentSummaryTitle;</title>
<!-- chrome: is needed for global.dtd -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self' chrome:"/>
@@ -121,17 +123,19 @@
</section>
<section id="order-details-overlay" hidden="hidden">
<h1>&orderDetailsLabel;</h1>
<order-details></order-details>
</section>
<basic-card-form id="basic-card-page"
class="page"
+ data-error-generic-save="&basicCardPage.error.genericSave;"
data-back-button-label="&basicCardPage.backButton.label;"
+ data-save-button-label="&basicCardPage.saveButton.label;"
hidden="hidden"></basic-card-form>
</div>
<div id="disabled-overlay" hidden="hidden">
<!-- overlay to prevent changes while waiting for a response from the merchant -->
</div>
</template>
--- a/toolkit/components/payments/test/PaymentTestUtils.jsm
+++ b/toolkit/components/payments/test/PaymentTestUtils.jsm
@@ -168,16 +168,27 @@ var PaymentTestUtils = {
// Waive the xray to access the untrusted `securityCodeInput` property
let picker = Cu.waiveXrays(content.document.querySelector("payment-method-picker"));
// Unwaive to access the ChromeOnly `setUserInput` API.
// setUserInput dispatches changes events.
Cu.unwaiveXrays(picker.securityCodeInput).setUserInput(securityCode);
},
},
+ DialogContentUtils: {
+ waitForState: async (content, stateCheckFn, msg) => {
+ const {
+ ContentTaskUtils,
+ } = ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm", {});
+ let {requestStore} = Cu.waiveXrays(content.document.querySelector("payment-dialog"));
+ await ContentTaskUtils.waitForCondition(() => stateCheckFn(requestStore.getState()), msg);
+ return requestStore.getState();
+ },
+ },
+
/**
* Common PaymentMethodData for testing
*/
MethodData: {
basicCard: {
supportedMethods: "basic-card",
},
bobPay: {
--- a/toolkit/components/payments/test/browser/browser.ini
+++ b/toolkit/components/payments/test/browser/browser.ini
@@ -1,16 +1,17 @@
[DEFAULT]
head = head.js
prefs =
dom.payments.request.enabled=true
skip-if = !e10s # Bug 1365964 - Payment Request isn't implemented for non-e10s
support-files =
blank_page.html
+[browser_card_edit.js]
[browser_change_shipping.js]
[browser_host_name.js]
[browser_profile_storage.js]
[browser_request_serialization.js]
[browser_request_shipping.js]
[browser_request_summary.js]
uses-unsafe-cpows = true
[browser_shippingaddresschange_error.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/payments/test/browser/browser_card_edit.js
@@ -0,0 +1,117 @@
+"use strict";
+
+add_task(async function test_add_link() {
+ const args = {
+ methodData: [PTU.MethodData.basicCard],
+ details: PTU.Details.total60USD,
+ };
+ await spawnInDialogForMerchantTask(PTU.ContentTasks.createRequest, async function check() {
+ let {
+ PaymentTestUtils: PTU,
+ } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+ let addLink = content.document.querySelector("payment-method-picker a");
+ 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;
+ },
+ "Check add page state");
+
+ 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`);
+ }
+
+ 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");
+
+ 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);
+ }
+
+ 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() {
+ const args = {
+ methodData: [PTU.MethodData.basicCard],
+ details: PTU.Details.total60USD,
+ };
+ await spawnInDialogForMerchantTask(PTU.ContentTasks.createRequest, async function check() {
+ let {
+ PaymentTestUtils: PTU,
+ } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+ let editLink = content.document.querySelector("payment-method-picker a:nth-of-type(2)");
+ 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;
+ },
+ "Check edit page state");
+
+ let nextYear = (new Date()).getFullYear() + 1;
+ let card = {
+ // cc-number cannot be modified
+ "cc-name": "A. Nonymous",
+ "cc-exp-month": 3,
+ "cc-exp-year": nextYear,
+ };
+
+ 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");
+
+ 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");
+
+ 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);
+});
+
--- a/toolkit/components/payments/test/mochitest/mochitest.ini
+++ b/toolkit/components/payments/test/mochitest/mochitest.ini
@@ -1,15 +1,16 @@
[DEFAULT]
prefs =
dom.webcomponents.customelements.enabled=false
support-files =
../../../../../browser/extensions/formautofill/content/autofillEditForms.js
../../../../../testing/modules/sinon-2.3.2.js
../../res/paymentRequest.css
+ ../../res/paymentRequest.js
../../res/paymentRequest.xhtml
../../res/PaymentsStore.js
../../res/unprivileged-fallbacks.js
../../res/components/currency-amount.js
../../res/components/address-option.js
../../res/components/address-option.css
../../res/components/basic-card-option.js
../../res/components/basic-card-option.css
--- a/toolkit/components/payments/test/mochitest/payments_common.js
+++ b/toolkit/components/payments/test/mochitest/payments_common.js
@@ -1,11 +1,12 @@
"use strict";
-/* exported asyncElementRendered, promiseStateChange, deepClone, PTU */
+/* exported asyncElementRendered, promiseStateChange, promiseContentToChromeMessage, deepClone,
+ PTU */
const PTU = SpecialPowers.Cu.import("resource://testing-common/PaymentTestUtils.jsm", {})
.PaymentTestUtils;
/**
* A helper to await on while waiting for an asynchronous rendering of a Custom
* Element.
* @returns {Promise}
@@ -20,11 +21,28 @@ function promiseStateChange(store) {
stateChangeCallback(state) {
store.unsubscribe(this);
resolve(state);
},
});
});
}
+/**
+ * Wait for a message of `messageType` from content to chrome and resolve with the event details.
+ * @param {string} messageType of the expected message
+ * @returns {Promise} when the message is dispatched
+ */
+function promiseContentToChromeMessage(messageType) {
+ return new Promise(resolve => {
+ document.addEventListener("paymentContentToChrome", function onCToC(event) {
+ if (event.detail.messageType != messageType) {
+ return;
+ }
+ document.removeEventListener("paymentContentToChrome", onCToC);
+ resolve(event.detail);
+ });
+ });
+}
+
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
--- a/toolkit/components/payments/test/mochitest/test_basic_card_form.html
+++ b/toolkit/components/payments/test/mochitest/test_basic_card_form.html
@@ -12,16 +12,17 @@ Test the basic-card-form element
<script src="sinon-2.3.2.js"></script>
<script src="payments_common.js"></script>
<script src="custom-elements.min.js"></script>
<script src="unprivileged-fallbacks.js"></script>
<script src="PaymentsStore.js"></script>
<script src="PaymentStateSubscriberMixin.js"></script>
<script src="autofillEditForms.js"></script>
<script src="basic-card-form.js"></script>
+ <script src="paymentRequest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<link rel="stylesheet" type="text/css" href="paymentRequest.css"/>
</head>
<body>
<p id="display">
</p>
<div id="content" style="display: none">
@@ -80,29 +81,100 @@ add_task(async function test_backButton(
synthesizeMouseAtCenter(form.backButton, {});
let {page} = await stateChangePromise;
is(page.id, "payment-summary", "Check initial page after appending");
form.remove();
});
+add_task(async function test_saveButton() {
+ let form = document.createElement("basic-card-form");
+ form.dataset.saveButtonLabel = "Save";
+ form.dataset.errorGenericSave = "Generic error";
+ await form.promiseReady;
+ display.appendChild(form);
+ await asyncElementRendered();
+
+ form.form.querySelector("#cc-number").focus();
+ sendString("4111111111111111");
+ form.form.querySelector("#cc-name").focus();
+ sendString("J. Smith");
+ form.form.querySelector("#cc-exp-month").focus();
+ sendString("11");
+ form.form.querySelector("#cc-exp-year").focus();
+ let year = (new Date()).getFullYear().toString();
+ sendString(year);
+
+ let messagePromise = promiseContentToChromeMessage("updateAutofillRecord");
+ is(form.saveButton.textContent, "Save", "Check label");
+ synthesizeMouseAtCenter(form.saveButton, {});
+
+ let details = await messagePromise;
+ is(details.collectionName, "creditCards", "Check collectionName");
+ isDeeply(details, {
+ collectionName: "creditCards",
+ errorStateChange: {
+ page: {
+ id: "basic-card-page",
+ error: "Generic error",
+ },
+ },
+ guid: undefined,
+ messageType: "updateAutofillRecord",
+ preserveOldProperties: true,
+ record: {
+ "cc-exp-month": "11",
+ "cc-exp-year": year,
+ "cc-name": "J. Smith",
+ "cc-number": "4111111111111111",
+ },
+ selectedStateKey: "selectedPaymentCard",
+ successStateChange: {
+ page: {
+ id: "payment-summary",
+ },
+ },
+ }, "Check event details for the message to chrome");
+ form.remove();
+});
+
+add_task(async function test_genericError() {
+ let form = document.createElement("basic-card-form");
+ await form.requestStore.setState({
+ page: {
+ id: "test-page",
+ error: "Generic Error",
+ },
+ });
+ await form.promiseReady;
+ display.appendChild(form);
+ await asyncElementRendered();
+
+ ok(!isHidden(form.genericErrorText), "Error message should be visible");
+ is(form.genericErrorText.textContent, "Generic Error", "Check error message");
+ form.remove();
+});
+
add_task(async function test_record() {
let form = document.createElement("basic-card-form");
await form.promiseReady;
display.appendChild(form);
await asyncElementRendered();
info("test year before current");
let card1 = deepClone(PTU.BasicCards.JohnDoe);
card1.guid = "9864798564";
card1["cc-exp-year"] = 2011;
await form.requestStore.setState({
- selectedPaymentCard: card1.guid,
+ page: {
+ id: "basic-card-page",
+ guid: card1.guid,
+ },
savedBasicCards: {
[card1.guid]: deepClone(card1),
},
});
await asyncElementRendered();
checkCCForm(form, card1);
info("test future year");
@@ -118,27 +190,32 @@ add_task(async function test_record() {
info("test change to minimal record");
let minimalCard = {
// no expiration date or name
"cc-number": "1234567690123",
guid: "9gnjdhen46",
};
await form.requestStore.setState({
- selectedPaymentCard: minimalCard.guid,
+ page: {
+ id: "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({
- selectedPaymentCard: null,
+ page: {
+ id: "basic-card-page",
+ },
});
await asyncElementRendered();
checkCCForm(form, {});
form.remove();
});
</script>