Backed out 5 changesets (bug 1476204) for browser chrome failures on browser_editCreditCardDialog. CLOSED TREE

Backed out changeset 10f3d1014592 (bug 1476204)
Backed out changeset a88879ea32e8 (bug 1476204)
Backed out changeset 001f13f2dd21 (bug 1476204)
Backed out changeset f0f464e3c27c (bug 1476204)
Backed out changeset ed92000eca20 (bug 1476204)
This commit is contained in:
Cosmin Sabou 2018-07-26 04:12:56 +03:00
parent 46813c4815
commit 80a830bce7
32 changed files with 252 additions and 414 deletions

View File

@ -560,52 +560,69 @@ var paymentDialogWrapper = {
window.close();
},
async onUpdateAutofillRecord(collectionName, record, guid, messageID) {
let responseMessage = {
guid,
messageID,
stateChange: {},
};
try {
if (collectionName == "creditCards" && !guid && !record.isTemporary) {
// 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()) {
throw new Error("User canceled master password entry");
}
async onUpdateAutofillRecord(collectionName, record, guid, {
errorStateChange,
preserveOldProperties,
selectedStateKey,
successStateChange,
}) {
if (collectionName == "creditCards" && !guid && !record.isTemporary) {
// 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;
}
}
let isTemporary = record.isTemporary;
let collection = isTemporary ? this.temporaryStore[collectionName] :
formAutofillStorage[collectionName];
let isTemporary = record.isTemporary;
let collection = isTemporary ? this.temporaryStore[collectionName] :
formAutofillStorage[collectionName];
try {
if (guid) {
let preserveOldProperties = true;
await collection.update(guid, record, preserveOldProperties);
} else {
responseMessage.guid = await collection.add(record);
guid = await collection.add(record);
}
if (isTemporary && collectionName == "addresses") {
// there will be no formautofill-storage-changed event to update state
// so add updated collection here
Object.assign(responseMessage.stateChange, {
Object.assign(successStateChange, {
tempAddresses: this.temporaryStore.addresses.getAll(),
});
}
if (isTemporary && collectionName == "creditCards") {
// there will be no formautofill-storage-changed event to update state
// so add updated collection here
Object.assign(responseMessage.stateChange, {
Object.assign(successStateChange, {
tempBasicCards: this.temporaryStore.creditCards.getAll(),
});
}
// Select the new record
if (selectedStateKey) {
if (selectedStateKey.length == 1) {
Object.assign(successStateChange, {
[selectedStateKey[0]]: guid,
});
} else if (selectedStateKey.length == 2) {
// Need to keep properties like preserveFieldValues from getting removed.
let subObj = Object.assign({}, successStateChange[selectedStateKey[0]]);
subObj[selectedStateKey[1]] = guid;
Object.assign(successStateChange, {
[selectedStateKey[0]]: subObj,
});
} else {
throw new Error(`selectedStateKey not supported: '${selectedStateKey}'`);
}
}
this.sendMessageToContent("updateState", successStateChange);
} catch (ex) {
responseMessage.error = true;
} finally {
this.sendMessageToContent("updateAutofillRecord:Response", responseMessage);
this.sendMessageToContent("updateState", errorStateChange);
}
},
@ -678,7 +695,12 @@ var paymentDialogWrapper = {
break;
}
case "updateAutofillRecord": {
this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, data.messageID);
this.onUpdateAutofillRecord(data.collectionName, data.record, data.guid, {
errorStateChange: data.errorStateChange,
preserveOldProperties: data.preserveOldProperties,
selectedStateKey: data.selectedStateKey,
successStateChange: data.successStateChange,
});
break;
}
}

View File

@ -3,9 +3,9 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
basic-card-option {
grid-column-gap: 1ch;
grid-template-areas: "type cc-number cc-exp cc-name";
justify-content: start;
grid-row-gap: 5px;
grid-column-gap: 10px;
grid-template-areas: "type cc-number cc-name cc-exp";
}
basic-card-option > .cc-number {

View File

@ -243,7 +243,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
}
}
async saveRecord() {
saveRecord() {
let record = this.formHandler.buildFormObject();
let currentState = this.requestStore.getState();
let {
@ -258,15 +258,22 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
record.isTemporary = true;
}
let successStateChange;
let state = {
errorStateChange: {
page: {
id: "address-page",
onboardingWizard: page.onboardingWizard,
error: this.dataset.errorGenericSave,
},
"address-page": addressPage,
},
preserveOldProperties: true,
selectedStateKey: page.selectedStateKey,
};
const previousId = page.previousId;
if (page.onboardingWizard && !Object.keys(savedBasicCards).length) {
successStateChange = {
"basic-card-page": {
// Preserve field values as the user may have already edited the card
// page and went back to the address page to make a correction.
preserveFieldValues: true,
},
state.successStateChange = {
page: {
id: "basic-card-page",
previousId: "address-page",
@ -274,7 +281,7 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
},
};
} else {
successStateChange = {
state.successStateChange = {
page: {
id: previousId || "payment-summary",
onboardingWizard: page.onboardingWizard,
@ -283,40 +290,11 @@ export default class AddressForm extends PaymentStateSubscriberMixin(PaymentRequ
}
if (previousId) {
successStateChange[previousId] = Object.assign({}, currentState[previousId]);
successStateChange[previousId].preserveFieldValues = true;
state.successStateChange[previousId] = Object.assign({}, currentState[previousId]);
state.successStateChange[previousId].preserveFieldValues = true;
}
try {
let {guid} = await paymentRequest.updateAutofillRecord("addresses", record, addressPage.guid);
let selectedStateKey = addressPage.selectedStateKey;
if (selectedStateKey.length == 1) {
Object.assign(successStateChange, {
[selectedStateKey[0]]: guid,
});
} else if (selectedStateKey.length == 2) {
// Need to keep properties like preserveFieldValues from getting removed.
let subObj = Object.assign({}, successStateChange[selectedStateKey[0]]);
subObj[selectedStateKey[1]] = guid;
Object.assign(successStateChange, {
[selectedStateKey[0]]: subObj,
});
} else {
throw new Error(`selectedStateKey not supported: '${selectedStateKey}'`);
}
this.requestStore.setState(successStateChange);
} catch (ex) {
log.warn("saveRecord: error:", ex);
this.requestStore.setState({
page: {
id: "address-page",
onboardingWizard: page.onboardingWizard,
error: this.dataset.errorGenericSave,
},
});
}
paymentRequest.updateAutofillRecord("addresses", record, addressPage.guid, state);
}
}

View File

@ -144,10 +144,10 @@ export default class AddressPicker extends RichPicker {
let nextState = {
page: {
id: "address-page",
selectedStateKey: [this.selectedStateKey],
},
"address-page": {
addressFields: this.getAttribute("address-fields"),
selectedStateKey: [this.selectedStateKey],
},
};

View File

@ -22,8 +22,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
super();
this.genericErrorText = document.createElement("div");
this.genericErrorText.setAttribute("aria-live", "polite");
this.genericErrorText.classList.add("page-error");
this.addressAddLink = document.createElement("a");
this.addressAddLink.className = "add-link";
@ -56,8 +54,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
let url = "formautofill/editCreditCard.xhtml";
this.promiseReady = this._fetchMarkup(url).then(doc => {
this.form = doc.getElementById("form");
this.form.addEventListener("input", this);
this.form.addEventListener("invalid", this);
return this.form;
});
}
@ -167,8 +163,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
} else if (!editing) {
billingAddressSelect.value = Object.keys(addresses)[0];
}
this.updateSaveButtonState();
}
handleEvent(event) {
@ -177,14 +171,6 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
this.onClick(event);
break;
}
case "input": {
this.onInput(event);
break;
}
case "invalid": {
this.onInvalid(event);
break;
}
}
}
@ -203,10 +189,10 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
page: {
id: "address-page",
previousId: "basic-card-page",
selectedStateKey: ["basic-card-page", "billingAddressGUID"],
},
"address-page": {
guid: null,
selectedStateKey: ["basic-card-page", "billingAddressGUID"],
title: this.dataset.billingAddressTitleAdd,
},
"basic-card-page": {
@ -261,9 +247,7 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
break;
}
case this.saveButton: {
if (this.form.checkValidity()) {
this.saveRecord();
}
this.saveRecord();
break;
}
default: {
@ -272,22 +256,11 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
}
}
onInput(event) {
this.updateSaveButtonState();
}
onInvalid(event) {
this.saveButton.disabled = true;
}
updateSaveButtonState() {
this.saveButton.disabled = !this.form.checkValidity();
}
async saveRecord() {
saveRecord() {
let record = this.formHandler.buildFormObject();
let currentState = this.requestStore.getState();
let {
page,
tempBasicCards,
"basic-card-page": basicCardPage,
} = currentState;
@ -307,24 +280,28 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
record["cc-number"] = record["cc-number"] || "";
}
try {
let {guid} = await paymentRequest.updateAutofillRecord("creditCards", record,
basicCardPage.guid);
this.requestStore.setState({
page: {
id: "payment-summary",
},
selectedPaymentCard: guid,
});
} catch (ex) {
log.warn("saveRecord: error:", ex);
this.requestStore.setState({
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);
}
}

View File

@ -53,7 +53,7 @@ export default class PaymentDialog extends PaymentStateSubscriberMixin(HTMLEleme
this._header = contents.querySelector("header");
this._errorText = contents.querySelector("header > .page-error");
this._errorText = contents.querySelector("#error-text");
this._disabledOverlay = contents.getElementById("disabled-overlay");

View File

@ -59,7 +59,6 @@
<section class="group">
<fieldset>
<legend>User Data Errors</legend>
<button id="saveVisibleForm" title="Bypasses field validation">Save Visible Form</button>
<button id="setShippingError">Shipping Error</button>
<button id="setAddressErrors">Address Errors</button>
</fieldset>

View File

@ -333,11 +333,6 @@ let buttonActions = {
requestStore.setState({});
},
saveVisibleForm() {
// Bypasses field validation which is useful to test error handling.
paymentDialog.querySelector("#main-container > .page:not([hidden])").saveRecord();
},
setAddresses1() {
paymentDialog.setStateFromParent({savedAddresses: ADDRESSES_1});
},

View File

@ -20,7 +20,6 @@ export let requestStore = new PaymentsStore({
},
"address-page": {
guid: null,
selectedStateKey: null,
title: "",
},
"payment-summary": {
@ -30,6 +29,7 @@ export let requestStore = new PaymentsStore({
previousId: null,
// onboardingWizard: true,
// error: "",
// selectedStateKey: "",
},
request: {
completeStatus: "",

View File

@ -45,25 +45,11 @@ payment-dialog > header,
payment-dialog > header {
border-bottom: 1px solid rgba(0,0,0,0.1);
display: flex;
/* Wrap so that the error text appears full-width above the rest of the contents */
flex-wrap: wrap;
/* from visual spec: */
padding-bottom: 19px;
padding-top: 19px;
}
payment-dialog > header > .page-error:empty {
display: none;
}
payment-dialog > header > .page-error {
background: #D70022;
border-radius: 3px;
color: white;
padding: 6px;
width: 100%;
}
#main-container {
display: flex;
grid-area: main;
@ -92,10 +78,6 @@ payment-dialog > header > .page-error {
display: none;
}
.page-error {
color: #D70022;
}
.page > footer {
align-items: center;
background-color: #eaeaee;
@ -105,6 +87,10 @@ payment-dialog > header > .page-error {
padding-bottom: 18px;
}
#error-text {
text-align: center;
}
#order-details-overlay {
background-color: var(--in-content-page-background);
overflow: auto;

View File

@ -11,7 +11,6 @@
/* import-globals-from unprivileged-fallbacks.js */
var paymentRequest = {
_nextMessageID: 1,
domReadyPromise: null,
init() {
@ -55,23 +54,15 @@ var paymentRequest = {
}
},
/**
* @param {string} messageType
* @param {[object]} detail
* @returns {number} message ID to be able to identify a reply (where applicable).
*/
sendMessageToChrome(messageType, detail = {}) {
let messageID = this._nextMessageID++;
log.debug("sendMessageToChrome:", messageType, messageID, detail);
log.debug("sendMessageToChrome:", messageType, detail);
let event = new CustomEvent("paymentContentToChrome", {
bubbles: true,
detail: Object.assign({
messageType,
messageID,
}, detail),
});
document.dispatchEvent(event);
return messageID;
},
toggleDebuggingConsole() {
@ -152,14 +143,14 @@ var paymentRequest = {
if (shippingRequested) {
Object.assign(state["address-page"], {
selectedStateKey: ["selectedShippingAddress"],
title: paymentDialog.dataset.shippingAddressTitleAdd,
});
state.page.selectedStateKey = ["selectedShippingAddress"];
} else {
Object.assign(state["address-page"], {
selectedStateKey: ["basic-card-page", "billingAddressGUID"],
title: paymentDialog.dataset.billingAddressTitleAdd,
});
state.page.selectedStateKey = ["basic-card-page", "billingAddressGUID"];
}
} else if (!hasSavedCards) {
state.page = {
@ -198,30 +189,21 @@ var paymentRequest = {
* @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
* @returns {Promise} when the update response is received
*/
updateAutofillRecord(collectionName, record, guid) {
return new Promise((resolve, reject) => {
let messageID = this.sendMessageToChrome("updateAutofillRecord", {
collectionName,
guid,
record,
});
window.addEventListener("paymentChromeToContent", function onMsg({detail}) {
if (detail.messageType != "updateAutofillRecord:Response"
|| detail.messageID != messageID) {
return;
}
log.debug("updateAutofillRecord: response:", detail);
window.removeEventListener("paymentChromeToContent", onMsg);
document.querySelector("payment-dialog").setStateFromParent(detail.stateChange);
if (detail.error) {
reject(detail);
} else {
resolve(detail);
}
});
updateAutofillRecord(collectionName, record, guid, {
errorStateChange,
preserveOldProperties,
selectedStateKey,
successStateChange,
}) {
this.sendMessageToChrome("updateAutofillRecord", {
collectionName,
guid,
record,
errorStateChange,
preserveOldProperties,
selectedStateKey,
successStateChange,
});
},

View File

@ -94,7 +94,6 @@
<template id="payment-dialog-template">
<header>
<div class="page-error" aria-live="polite"></div>
<div id="total">
<currency-amount display-code="display-code"></currency-amount>
<div>&header.payTo; <span id="host-name"></span></div>
@ -107,6 +106,7 @@
<div id="main-container">
<payment-request-page id="payment-summary">
<div class="page-body">
<div id="error-text"></div>
<address-picker class="shipping-related"
data-add-link-label="&address.addLink.label;"
data-edit-link-label="&address.editLink.label;"
@ -128,6 +128,7 @@
data-add-link-label="&payer.addLink.label;"
data-edit-link-label="&payer.editLink.label;"
selected-state-key="selectedPayerAddress"></address-picker>
<div id="error-text"></div>
</div>
<footer>

View File

@ -26,7 +26,7 @@ var PaymentDialogUtils = {
return `${address.name} (${address.guid})`;
},
isCCNumber(str) {
return !!str.replace(/[-\s]/g, "").match(/^\d{9,}$/);
return str.length > 0;
},
DEFAULT_REGION: "US",
supportedCountries: ["US", "CA"],

View File

@ -172,7 +172,6 @@ var PaymentTestUtils = {
let {requestStore} = Cu.waiveXrays(content.document.querySelector("payment-dialog"));
let {page} = requestStore.getState();
let button = content.document.querySelector(`#${page.id} button.primary`);
ok(!button.disabled, "Primary button should not be disabled when clicking it");
button.click();
},

View File

@ -113,13 +113,13 @@ async function add_link(aOptions = {}) {
await verifyPersistCheckbox(frame, addressOptions);
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
await spawnPaymentDialogTask(frame, async (testArgs = {}) => {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
content.document.querySelector("address-form button:last-of-type").click();
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
}, "Check address was added and we're back on basic-card page (add)");
@ -152,19 +152,18 @@ async function add_link(aOptions = {}) {
checkboxSelector: "basic-card-form .persist-checkbox",
});
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
await spawnPaymentDialogTask(frame, async (testArgs = {}) => {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
content.document.querySelector("basic-card-form button:last-of-type").click();
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "payment-summary";
}, "Check we are back on the summary page");
}, "Check we are back on the sumamry page");
});
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
securityCode: "123",
});
@ -371,7 +370,7 @@ add_task(async function test_edit_link() {
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
}
content.document.querySelector("address-form button.save-button").click();
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;
@ -380,7 +379,7 @@ add_task(async function test_edit_link() {
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();
content.document.querySelector("basic-card-form button:last-of-type").click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
let cards = Object.entries(state.savedBasicCards);
@ -404,73 +403,65 @@ add_task(async function test_edit_link() {
add_task(async function test_private_card_adding() {
await setup([PTU.Addresses.TimBL], [PTU.BasicCards.JohnDoe]);
const args = {
methodData: [PTU.MethodData.basicCard],
details: PTU.Details.total60USD,
};
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", {});
await BrowserTestUtils.withNewTab({
gBrowser: privateWin.gBrowser,
url: BLANK_PAGE_URL,
}, async browser => {
let {win, frame} = await setupPaymentDialog(browser, {
methodData: [PTU.MethodData.basicCard],
details: PTU.Details.total60USD,
merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
});
let addLink = content.document.querySelector("payment-method-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
await spawnPaymentDialogTask(frame, async function check() {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
addLink.click();
let addLink = content.document.querySelector("payment-method-picker .add-link");
is(addLink.textContent, "Add", "Add link text");
let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
},
"Check add page state");
addLink.click();
let savedCardCount = Object.keys(state.savedBasicCards).length;
let tempCardCount = Object.keys(state.tempBasicCards).length;
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
},
"Check card page state");
});
let card = Object.assign({}, PTU.BasicCards.JohnDoe);
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
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`);
}
await spawnPaymentDialogTask(frame, async function() {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
content.document.querySelector("basic-card-form button:last-of-type").click();
let card = Object.assign({}, PTU.BasicCards.JohnDoe);
let state = await PTU.DialogContentUtils.getCurrentState(content);
let savedCardCount = Object.keys(state.savedBasicCards).length;
let tempCardCount = Object.keys(state.tempBasicCards).length;
content.document.querySelector("basic-card-form button.save-button").click();
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
return Object.keys(state.tempBasicCards).length > tempCardCount;
},
"Check card was added to temp collection");
state = await PTU.DialogContentUtils.waitForState(content, (state) => {
return Object.keys(state.tempBasicCards).length > tempCardCount;
},
"Check card was added to temp collection");
is(savedCardCount, Object.keys(state.savedBasicCards).length, "No card was saved in state");
is(Object.keys(state.tempBasicCards).length, 1, "Card was added temporarily");
is(savedCardCount, Object.keys(state.savedBasicCards).length, "No card was saved in state");
is(Object.keys(state.tempBasicCards).length, 1, "Card was added temporarily");
let cardGUIDs = Object.keys(state.tempBasicCards);
is(cardGUIDs.length, 1, "Check there is one card");
let cardGUIDs = Object.keys(state.tempBasicCards);
is(cardGUIDs.length, 1, "Check there is one card");
let tempCard = state.tempBasicCards[cardGUIDs[0]];
// Card number should be masked, so skip cc-number in the compare loop below
delete card["cc-number"];
for (let [key, val] of Object.entries(card)) {
is(tempCard[key], val, "Check " + key + ` ${tempCard[key]} matches ${val}`);
}
// check computed fields
is(tempCard["cc-number"], "************1111", "cc-number is masked");
is(tempCard["cc-given-name"], "John", "cc-given-name was computed");
is(tempCard["cc-family-name"], "Doe", "cc-family-name was computed");
ok(tempCard["cc-exp"], "cc-exp was computed");
ok(tempCard["cc-number-encrypted"], "cc-number-encrypted was computed");
});
spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
let tempCard = state.tempBasicCards[cardGUIDs[0]];
// Card number should be masked, so skip cc-number in the compare loop below
delete card["cc-number"];
for (let [key, val] of Object.entries(card)) {
is(tempCard[key], val, "Check " + key + ` ${tempCard[key]} matches ${val}`);
}
// check computed fields
is(tempCard["cc-number"], "************1111", "cc-number is masked");
is(tempCard["cc-given-name"], "John", "cc-given-name was computed");
is(tempCard["cc-family-name"], "Doe", "cc-family-name was computed");
ok(tempCard["cc-exp"], "cc-exp was computed");
ok(tempCard["cc-number-encrypted"], "cc-number-encrypted was computed");
}, args, {
browser: privateWin.gBrowser,
});
await BrowserTestUtils.closeWindow(privateWin);
});

View File

@ -37,7 +37,7 @@ add_task(async function test_onboarding_wizard_without_saved_addresses_and_saved
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "address-page" &&
state["address-page"].selectedStateKey[0] == "selectedShippingAddress";
state.page.selectedStateKey[0] == "selectedShippingAddress";
}, "Address page is shown first during on-boarding if there are no saved addresses");
info("Checking if the address page has been rendered");
@ -86,23 +86,21 @@ add_task(async function test_onboarding_wizard_without_saved_addresses_and_saved
let billingAddressSelect = content.document.querySelector("#billingAddressGUID");
return state.selectedShippingAddress == billingAddressSelect.value;
}, "Shipping address is selected as the billing address");
});
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
await spawnPaymentDialogTask(frame, async function() {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
for (let [key, val] of Object.entries(PTU.BasicCards.JohnDoe)) {
let field = content.document.getElementById(key);
field.value = val;
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
}
content.document.querySelector("basic-card-form .save-button").click();
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "payment-summary";
}, "Payment summary page is shown after the basic card page during on boarding");
let cancelButton = content.document.querySelector("#cancel");
ok(content.isVisible(cancelButton), "Payment summary page is rendered");
ok(content.isVisible(cancelButton),
"Payment summary page is rendered");
});
info("Closing the payment dialog");
@ -319,8 +317,8 @@ add_task(async function test_onboarding_wizard_with_requestShipping_turned_off()
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "address-page" &&
state["address-page"].selectedStateKey[0] == "basic-card-page" &&
state["address-page"].selectedStateKey[1] == "billingAddressGUID";
state.page.selectedStateKey[0] == "basic-card-page" &&
state.page.selectedStateKey[1] == "billingAddressGUID";
// eslint-disable-next-line max-len
}, "Billing address page is shown first during on-boarding if requestShipping is turned off");
@ -358,16 +356,13 @@ add_task(async function test_onboarding_wizard_with_requestShipping_turned_off()
let billingAddressSelect = content.document.querySelector("#billingAddressGUID");
return state["basic-card-page"].billingAddressGUID == billingAddressSelect.value;
}, "Billing Address is correctly shown");
});
await fillInCardForm(frame, PTU.BasicCards.JohnDoe);
await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.clickPrimaryButton);
await spawnPaymentDialogTask(frame, async function() {
let {
PaymentTestUtils: PTU,
} = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
for (let [key, val] of Object.entries(PTU.BasicCards.JohnDoe)) {
let field = content.document.getElementById(key);
field.value = val;
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
}
content.document.querySelector("basic-card-form .save-button").click();
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "payment-summary";
@ -438,8 +433,8 @@ add_task(async function test_back_button_on_basic_card_page_during_onboarding()
await PTU.DialogContentUtils.waitForState(content, (state) => {
return state.page.id == "address-page";
}, "Billing address page is shown first if there are no saved addresses " +
"and requestShipping is false during on boarding");
}, "Address page is shown first if there are saved addresses during on boarding");
info("Checking if the address page has been rendered");
let addressSaveButton = content.document.querySelector("address-form .save-button");
ok(content.isVisible(addressSaveButton), "Address save button is rendered");

View File

@ -37,7 +37,7 @@ add_task(async function test_show_error_on_addresschange() {
}, PTU.ContentTasks.awaitPaymentRequestEventPromise);
await spawnPaymentDialogTask(frame, expectedText => {
let errorText = content.document.querySelector("header .page-error");
let errorText = content.document.querySelector("#error-text");
is(errorText.textContent, expectedText, "Error text should be on dialog");
ok(content.isVisible(errorText), "Error text should be visible");
}, PTU.Details.genericShippingError.error);
@ -59,7 +59,7 @@ add_task(async function test_show_error_on_addresschange() {
}, PTU.ContentTasks.awaitPaymentRequestEventPromise);
await spawnPaymentDialogTask(frame, () => {
let errorText = content.document.querySelector("header .page-error");
let errorText = content.document.querySelector("#error-text");
is(errorText.textContent, "", "Error text should not be on dialog");
ok(content.isHidden(errorText), "Error text should not be visible");
});
@ -112,7 +112,7 @@ add_task(async function test_show_field_specific_error_on_addresschange() {
return Object.keys(state.request.paymentDetails.shippingAddressErrors).length;
}, "Check that there are shippingAddressErrors");
is(content.document.querySelector("header .page-error").textContent,
is(content.document.querySelector("#error-text").textContent,
PTU.Details.fieldSpecificErrors.error,
"Error text should be present on dialog");
@ -165,7 +165,7 @@ add_task(async function test_show_field_specific_error_on_addresschange() {
return !Object.keys(state.request.paymentDetails.shippingAddressErrors).length;
}, "Check that there are no more shippingAddressErrors");
is(content.document.querySelector("header .page-error").textContent,
is(content.document.querySelector("#error-text").textContent,
"", "Error text should not be present on dialog");
info("click the Edit link again");

View File

@ -498,13 +498,8 @@ async function fillInCardForm(frame, aCard, aOptions = {}) {
if (!field) {
ok(false, `${key} field not found`);
}
ok(!field.disabled, `Field #${key} shouldn't be disabled`);
field.value = "";
field.focus();
// cc-exp-* fields are numbers so convert to strings and pad left with 0
EventUtils.synthesizeKey(val.toString().padStart(2, "0"), {}, content.window);
field.value = val;
}
let persistCheckbox = content.document.querySelector(options.checkboxSelector);
// only touch the checked state if explicitly told to in the options
if (options.hasOwnProperty("isTemporary")) {

View File

@ -140,13 +140,22 @@ add_task(async function test_saveButton() {
synthesizeMouseAtCenter(form.saveButton, {});
let details = await messagePromise;
ok(typeof(details.messageID) == "number" && details.messageID > 0, "Check messageID type");
delete details.messageID;
is(details.collectionName, "addresses", "Check collectionName");
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",
@ -160,6 +169,13 @@ add_task(async function test_saveButton() {
"email": "test@example.com",
"tel": "+15555551212",
},
selectedStateKey: undefined,
successStateChange: {
page: {
id: "payment-summary",
onboardingWizard: undefined,
},
},
}, "Check event details for the message to chrome");
form.remove();
});

View File

@ -96,41 +96,46 @@ add_task(async function test_saveButton() {
display.appendChild(form);
await asyncElementRendered();
ok(form.saveButton.disabled, "Save button should initially be disabled");
form.form.querySelector("#cc-number").focus();
sendString("4111 1111-1111 1111");
sendString("4111111111111111");
form.form.querySelector("#cc-name").focus();
// Check .disabled after .focus() so that it's after both "input" and "change" events.
ok(form.saveButton.disabled, "Save button should still be disabled without a name");
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);
form.saveButton.focus();
ok(!form.saveButton.disabled,
"Save button should be enabled since the required fields are filled");
let messagePromise = promiseContentToChromeMessage("updateAutofillRecord");
is(form.saveButton.textContent, "Save", "Check label");
synthesizeMouseAtCenter(form.saveButton, {});
let details = await messagePromise;
ok(typeof(details.messageID) == "number" && details.messageID > 0, "Check messageID type");
delete details.messageID;
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": "4111 1111-1111 1111",
"cc-number": "4111111111111111",
"billingAddressGUID": "",
},
selectedStateKey: ["selectedPaymentCard"],
successStateChange: {
page: {
id: "payment-summary",
},
},
}, "Check event details for the message to chrome");
form.remove();
});

View File

@ -327,10 +327,6 @@ class AutofillRecords {
}
} else if (!recordToSave.deleted) {
this._normalizeRecord(recordToSave);
// _normalizeRecord shouldn't do any validation (throw) because in the
// `update` case it is called with partial records whereas
// `_validateFields` is called with a complete one.
this._validateFields(recordToSave);
recordToSave.guid = this._generateGUID();
recordToSave.version = this.version;
@ -442,13 +438,6 @@ class AutofillRecords {
throw new Error("Record contains no valid field.");
}
// _normalizeRecord above is called with the `record` argument provided to
// `update` which may not contain all resulting fields when
// `preserveOldProperties` is used. This means we need to validate for
// missing fields after we compose the record (`recordFound`) with the stored
// record like we do in the loop above.
this._validateFields(recordFound);
recordFound.timeLastModified = Date.now();
let syncMetadata = this._getSyncMetaData(recordFound);
if (syncMetadata) {
@ -1231,28 +1220,8 @@ class AutofillRecords {
// An interface to be inherited.
computeFields(record) {}
/**
* An interface to be inherited to mutate the argument to normalize it.
*
* @param {object} partialRecord containing the record passed by the consumer of
* storage and in the case of `update` with
* `preserveOldProperties` will only include the
* properties that the user is changing so the
* lack of a field doesn't mean that the record
* won't have that field.
*/
_normalizeFields(partialRecord) {}
/**
* An interface to be inherited to validate that the complete record is
* consistent and isn't missing required fields. Overrides should throw for
* invalid records.
*
* @param {object} record containing the complete record that would be stored
* if this doesn't throw due to an error.
* @throws
*/
_validateFields(record) {}
// An interface to be inherited.
_normalizeFields(record) {}
// An interface to be inherited.
mergeIfPossible(guid, record, strict) {}
@ -1610,7 +1579,7 @@ class CreditCards extends AutofillRecords {
if (creditCard["cc-number"]) {
let card = new CreditCard({number: creditCard["cc-number"]});
creditCard["cc-number"] = card.number;
if (!card.isValidNumber()) {
if (!creditCard["cc-number"]) {
delete creditCard["cc-number"];
}
}
@ -1635,12 +1604,6 @@ class CreditCards extends AutofillRecords {
delete creditCard["cc-exp"];
}
_validateFields(creditCard) {
if (!creditCard["cc-number"]) {
throw new Error("Missing/invalid cc-number");
}
}
/**
* Normalize the given record and return the first matched guid if storage has the same record.
* @param {Object} targetCreditCard

View File

@ -221,7 +221,7 @@ this.FormAutofillUtils = {
isCCNumber(ccNumber) {
let card = new CreditCard({number: ccNumber});
return card.isValidNumber();
return !!card.number;
},
getCategoryFromFieldName(fieldName) {

View File

@ -208,7 +208,6 @@ class EditCreditCard extends EditAutofillForm {
Object.assign(this, config);
Object.assign(this._elements, {
ccNumber: this._elements.form.querySelector("#cc-number"),
invalidCardNumberStringElement: this._elements.form.querySelector("#invalidCardNumberString"),
year: this._elements.form.querySelector("#cc-exp-year"),
billingAddress: this._elements.form.querySelector("#billingAddressGUID"),
billingAddressRow: this._elements.form.querySelector(".billingAddressRow"),
@ -238,9 +237,6 @@ class EditCreditCard extends EditAutofillForm {
// Clear the list
this._elements.year.textContent = "";
// Provide an empty year option
this._elements.year.appendChild(new Option());
if (ccExpYear && ccExpYear < currentYear) {
this._elements.year.appendChild(new Option(ccExpYear));
}
@ -290,8 +286,7 @@ class EditCreditCard extends EditAutofillForm {
// Mark the cc-number field as invalid if the number is empty or invalid.
if (!this.isCCNumber(ccNumberField.value)) {
let invalidCardNumberString = this._elements.invalidCardNumberStringElement.textContent;
ccNumberField.setCustomValidity(invalidCardNumberString || " ");
ccNumberField.setCustomValidity(true);
}
}

View File

@ -20,12 +20,11 @@
<form id="form" autocomplete="off">
<label>
<span data-localization="cardNumber"/>
<span id="invalidCardNumberString" hidden="hidden" data-localization="invalidCardNumber"></span>
<input id="cc-number" type="text" required="required" minlength="9" pattern="[- 0-9]+"/>
<input id="cc-number" type="text"/>
</label>
<label>
<span data-localization="nameOnCard"/>
<input id="cc-name" type="text" required="required"/>
<input id="cc-name" type="text"/>
</label>
<div>
<span data-localization="cardExpires"/>

View File

@ -173,7 +173,7 @@ class EditCreditCardDialog extends AutofillEditDialog {
async handleSubmit() {
let creditCard = this._elements.fieldContainer.buildFormObject();
if (!this._elements.fieldContainer._elements.form.reportValidity()) {
if (!this._elements.fieldContainer._elements.form.checkValidity()) {
return;
}

View File

@ -136,7 +136,6 @@ countryWarningMessage2 = Form Autofill is currently available only for certain c
addNewCreditCardTitle = Add New Credit Card
editCreditCardTitle = Edit Credit Card
cardNumber = Card Number
invalidCardNumber = Please enter a valid card number
nameOnCard = Name on Card
cardExpires = Expires
billingAddress = Billing Address

View File

@ -2,8 +2,6 @@
"use strict";
let {PromiseTestUtils} = ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm", {});
add_task(async function setup() {
let {formAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
await formAutofillStorage.initialize();
@ -181,9 +179,6 @@ add_task(async function test_addInvalidCreditCard() {
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("test", {}, win);
EventUtils.synthesizeKey("VK_TAB", {}, win);
EventUtils.synthesizeKey("test name", {}, win);
PromiseTestUtils.expectUncaughtRejection(/Missing\/invalid cc-number/);
EventUtils.synthesizeMouseAtCenter(win.document.querySelector("#save"), {}, win);
is(win.document.querySelector("form").checkValidity(), false, "cc-number is invalid");

View File

@ -13,7 +13,6 @@
"use strict";
ChromeUtils.import("resource://testing-common/LoginTestUtils.jsm", this);
ChromeUtils.import("resource://formautofill/MasterPassword.jsm", this);
const MANAGE_ADDRESSES_DIALOG_URL = "chrome://formautofill/content/manageAddresses.xhtml";
const MANAGE_CREDIT_CARDS_DIALOG_URL = "chrome://formautofill/content/manageCreditCards.xhtml";
@ -345,11 +344,6 @@ async function waitForFocusAndFormReady(win) {
}
async function testDialog(url, testFn, arg = undefined) {
if (url == EDIT_CREDIT_CARD_DIALOG_URL && arg) {
arg = Object.assign({}, arg, {
"cc-number": await MasterPassword.decrypt(arg["cc-number-encrypted"]),
});
}
let win = window.openDialog(url, null, "width=600,height=600", arg);
await waitForFocusAndFormReady(win);
let unloadPromise = BrowserTestUtils.waitForEvent(win, "unload");

View File

@ -246,7 +246,7 @@ const TESTCASES = [
focusedInputId: "cc-number",
profileData: {
"guid": "123",
"cc-number": "4111111111111111",
"cc-number": "1234000056780000",
"cc-name": "test name",
"cc-exp-month": "06",
"cc-exp-year": "25",
@ -257,7 +257,7 @@ const TESTCASES = [
"country": "",
"email": "",
"tel": "",
"cc-number": "4111111111111111",
"cc-number": "1234000056780000",
"cc-name": "test name",
"cc-exp-month": "06",
"cc-exp-year": "25",

View File

@ -235,14 +235,14 @@ const TESTCASES = [
<input id="cc-exp" autocomplete="cc-exp">
</form>`,
formValue: {
"cc-number": "5105105105105100",
"cc-number": "4444000022220000",
"cc-name": "Foo Bar",
"cc-exp": "2022-06",
},
expectedRecord: {
address: [],
creditCard: [{
"cc-number": "5105105105105100",
"cc-number": "4444000022220000",
"cc-name": "Foo Bar",
"cc-exp": "2022-06",
}],
@ -254,12 +254,12 @@ const TESTCASES = [
<input id="cc-number" autocomplete="cc-number">
</form>`,
formValue: {
"cc-number": "4111111111111111",
"cc-number": "4444000022220000",
},
expectedRecord: {
address: [],
creditCard: [{
"cc-number": "4111111111111111",
"cc-number": "4444000022220000",
}],
},
},
@ -332,10 +332,10 @@ const TESTCASES = [
"organization-billing": "Bar",
"country-billing": "US",
"cc-number-section-one": "4111111111111111",
"cc-number-section-one": "4444000022220000",
"cc-name-section-one": "John",
"cc-number-section-two": "5105105105105100",
"cc-number-section-two": "4444000022221111",
"cc-name-section-two": "Foo Bar",
"cc-exp-section-two": "2026-26",
},
@ -355,10 +355,10 @@ const TESTCASES = [
"country": "US",
}],
creditCard: [{
"cc-number": "4111111111111111",
"cc-number": "4444000022220000",
"cc-name": "John",
}, {
"cc-number": "5105105105105100",
"cc-number": "4444000022221111",
"cc-name": "Foo Bar",
"cc-exp": "2026-26",
}],

View File

@ -82,7 +82,7 @@ const TESTCASES = [
description: "Trigger credit card saving",
formValue: {
"cc-name": "John Doe",
"cc-number": "5105105105105100",
"cc-number": "1234567812345678",
"cc-exp-month": 12,
"cc-exp-year": 2000,
},
@ -94,7 +94,7 @@ const TESTCASES = [
guid: null,
record: {
"cc-name": "John Doe",
"cc-number": "5105105105105100",
"cc-number": "1234567812345678",
"cc-exp-month": 12,
"cc-exp-year": 2000,
},
@ -110,7 +110,7 @@ const TESTCASES = [
"country": "USA",
"tel": "1-650-903-0800",
"cc-name": "John Doe",
"cc-number": "5105105105105100",
"cc-number": "1234567812345678",
"cc-exp-month": 12,
"cc-exp-year": 2000,
},
@ -133,7 +133,7 @@ const TESTCASES = [
guid: null,
record: {
"cc-name": "John Doe",
"cc-number": "5105105105105100",
"cc-number": "1234567812345678",
"cc-exp-month": 12,
"cc-exp-year": 2000,
},

View File

@ -526,11 +526,9 @@ const CREDIT_CARD_COMPUTE_TESTCASES = [
description: "Has \"cc-name\"",
creditCard: {
"cc-name": "Timothy John Berners-Lee",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-name": "Timothy John Berners-Lee",
"cc-number": "************1045",
"cc-given-name": "Timothy",
"cc-additional-name": "John",
"cc-family-name": "Berners-Lee",
@ -554,37 +552,31 @@ const CREDIT_CARD_COMPUTE_TESTCASES = [
creditCard: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-exp": "2022-12",
"cc-number": "************1045",
},
},
{
description: "Has only \"cc-exp-month\"",
creditCard: {
"cc-exp-month": 12,
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp": undefined,
"cc-number": "************1045",
},
},
{
description: "Has only \"cc-exp-year\"",
creditCard: {
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-year": 2022,
"cc-exp": undefined,
"cc-number": "************1045",
},
},
];
@ -597,11 +589,9 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
"cc-name": "Timothy John Berners-Lee",
"cc-given-name": "John",
"cc-family-name": "Doe",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-name": "Timothy John Berners-Lee",
"cc-number": "4929001587121045",
},
},
{
@ -609,11 +599,9 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
creditCard: {
"cc-given-name": "John",
"cc-family-name": "Doe",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-name": "John Doe",
"cc-number": "4929001587121045",
},
},
@ -650,181 +638,151 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
{
description: "Has \"cc-exp\" formatted \"yyyy-mm\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "2022-12",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"yyyy/mm\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "2022/12",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"yyyy-m\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "2022-3",
},
expectedResult: {
"cc-exp-month": 3,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"yyyy/m\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "2022/3",
},
expectedResult: {
"cc-exp-month": 3,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"mm-yyyy\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "12-2022",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"mm/yyyy\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "12/2022",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"m-yyyy\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "3-2022",
},
expectedResult: {
"cc-exp-month": 3,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"m/yyyy\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "3/2022",
},
expectedResult: {
"cc-exp-month": 3,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"mm-yy\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "12-22",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"mm/yy\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "12/22",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"yy-mm\"",
creditCard: {
"cc-number": "4929001587121045",
"cc-exp": "22-12",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"yy/mm\"",
creditCard: {
"cc-exp": "22/12",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"mmyy\"",
creditCard: {
"cc-exp": "1222",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" formatted \"yymm\"",
creditCard: {
"cc-exp": "2212",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
description: "Has \"cc-exp\" with spaces",
creditCard: {
"cc-exp": " 2033-11 ",
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 11,
"cc-exp-year": 2033,
"cc-number": "4929001587121045",
},
},
{
@ -844,12 +802,10 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
"cc-exp": "2022-12",
"cc-exp-month": 3,
"cc-exp-year": 2030,
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 3,
"cc-exp-year": 2030,
"cc-number": "4929001587121045",
},
},
{
@ -857,12 +813,10 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
creditCard: {
"cc-exp": "2022-12",
"cc-exp-year": 2030,
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
{
@ -870,12 +824,10 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
creditCard: {
"cc-exp": "2022-12",
"cc-exp-month": 3,
"cc-number": "4929001587121045",
},
expectedResult: {
"cc-exp-month": 12,
"cc-exp-year": 2022,
"cc-number": "4929001587121045",
},
},
];