mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 1461477 - Create a CreditCard.jsm to consolidate various credit card handling and validation. r=MattN
MozReview-Commit-ID: 3tJdzU3hBvY --HG-- extra : rebase_source : bf79b4767ed40e792d1523bd262d527061a21a4c
This commit is contained in:
parent
16d53b6538
commit
23f6d33e60
@ -195,9 +195,7 @@ function checkPaymentAddressMatchesStorageAddress(paymentAddress, storageAddress
|
||||
function checkPaymentMethodDetailsMatchesCard(methodDetails, card, msg) {
|
||||
info(msg);
|
||||
// The card expiry month should be a zero-padded two-digit string.
|
||||
let cardExpiryMonth = card["cc-exp-month"] < 10 ?
|
||||
"0" + card["cc-exp-month"] :
|
||||
card["cc-exp-month"].toString();
|
||||
let cardExpiryMonth = card["cc-exp-month"].toString().padStart(2, "0");
|
||||
is(methodDetails.cardholderName, card["cc-name"], "Check cardholderName");
|
||||
is(methodDetails.cardNumber, card["cc-number"], "Check cardNumber");
|
||||
is(methodDetails.expiryMonth, cardExpiryMonth, "Check expiryMonth");
|
||||
|
@ -38,6 +38,7 @@ ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
BrowserWindowTracker: "resource:///modules/BrowserWindowTracker.jsm",
|
||||
CreditCard: "resource://gre/modules/CreditCard.jsm",
|
||||
FormAutofillPreferences: "resource://formautofill/FormAutofillPreferences.jsm",
|
||||
FormAutofillDoorhanger: "resource://formautofill/FormAutofillDoorhanger.jsm",
|
||||
MasterPassword: "resource://formautofill/MasterPassword.jsm",
|
||||
@ -519,7 +520,12 @@ FormAutofillParent.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
const description = FormAutofillUtils.getCreditCardLabel(creditCard.record, false);
|
||||
const card = new CreditCard({
|
||||
number: creditCard.record["cc-number"] || creditCard.record["cc-number-decrypted"],
|
||||
encryptedNumber: creditCard.record["cc-number-encrypted"],
|
||||
name: creditCard.record["cc-name"],
|
||||
});
|
||||
const description = await card.getLabel();
|
||||
const state = await FormAutofillDoorhanger.show(target,
|
||||
creditCard.guid ? "updateCreditCard" : "addCreditCard",
|
||||
description);
|
||||
|
@ -132,6 +132,8 @@ ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "CreditCard",
|
||||
"resource://gre/modules/CreditCard.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "JSONFile",
|
||||
"resource://gre/modules/JSONFile.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "FormAutofillNameUtils",
|
||||
@ -1161,7 +1163,7 @@ class AutofillRecords {
|
||||
}
|
||||
if (typeof record[key] !== "string" &&
|
||||
typeof record[key] !== "number") {
|
||||
throw new Error(`"${key}" contains invalid data type.`);
|
||||
throw new Error(`"${key}" contains invalid data type: ${typeof record[key]}`);
|
||||
}
|
||||
if (!preserveEmptyFields && record[key] === "") {
|
||||
delete record[key];
|
||||
@ -1494,13 +1496,6 @@ class CreditCards extends AutofillRecords {
|
||||
super(store, "creditCards", VALID_CREDIT_CARD_FIELDS, VALID_CREDIT_CARD_COMPUTED_FIELDS, CREDIT_CARD_SCHEMA_VERSION);
|
||||
}
|
||||
|
||||
_getMaskedCCNumber(ccNumber) {
|
||||
if (ccNumber.length <= 4) {
|
||||
throw new Error(`Invalid credit card number`);
|
||||
}
|
||||
return "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
|
||||
}
|
||||
|
||||
_computeFields(creditCard) {
|
||||
// NOTE: Remember to bump the schema version number if any of the existing
|
||||
// computing algorithm changes. (No need to bump when just adding new
|
||||
@ -1538,7 +1533,7 @@ class CreditCards extends AutofillRecords {
|
||||
if (!("cc-number-encrypted" in creditCard)) {
|
||||
if ("cc-number" in creditCard) {
|
||||
let ccNumber = creditCard["cc-number"];
|
||||
creditCard["cc-number"] = this._getMaskedCCNumber(ccNumber);
|
||||
creditCard["cc-number"] = CreditCard.getLongMaskedNumber(ccNumber);
|
||||
creditCard["cc-number-encrypted"] = MasterPassword.encryptSync(ccNumber);
|
||||
} else {
|
||||
creditCard["cc-number-encrypted"] = "";
|
||||
@ -1578,7 +1573,8 @@ class CreditCards extends AutofillRecords {
|
||||
|
||||
_normalizeCCNumber(creditCard) {
|
||||
if (creditCard["cc-number"]) {
|
||||
creditCard["cc-number"] = FormAutofillUtils.normalizeCCNumber(creditCard["cc-number"]);
|
||||
let card = new CreditCard({number: creditCard["cc-number"]});
|
||||
creditCard["cc-number"] = card.number;
|
||||
if (!creditCard["cc-number"]) {
|
||||
delete creditCard["cc-number"];
|
||||
}
|
||||
@ -1586,84 +1582,21 @@ class CreditCards extends AutofillRecords {
|
||||
}
|
||||
|
||||
_normalizeCCExpirationDate(creditCard) {
|
||||
if (creditCard["cc-exp-month"]) {
|
||||
let expMonth = parseInt(creditCard["cc-exp-month"], 10);
|
||||
if (isNaN(expMonth) || expMonth < 1 || expMonth > 12) {
|
||||
delete creditCard["cc-exp-month"];
|
||||
} else {
|
||||
creditCard["cc-exp-month"] = expMonth;
|
||||
}
|
||||
let card = new CreditCard({
|
||||
expirationMonth: creditCard["cc-exp-month"],
|
||||
expirationYear: creditCard["cc-exp-year"],
|
||||
expirationString: creditCard["cc-exp"],
|
||||
});
|
||||
if (card.expirationMonth) {
|
||||
creditCard["cc-exp-month"] = card.expirationMonth;
|
||||
} else {
|
||||
delete creditCard["cc-exp-month"];
|
||||
}
|
||||
|
||||
if (creditCard["cc-exp-year"]) {
|
||||
let expYear = parseInt(creditCard["cc-exp-year"], 10);
|
||||
if (isNaN(expYear) || expYear < 0) {
|
||||
delete creditCard["cc-exp-year"];
|
||||
} else if (expYear < 100) {
|
||||
// Enforce 4 digits years.
|
||||
creditCard["cc-exp-year"] = expYear + 2000;
|
||||
} else {
|
||||
creditCard["cc-exp-year"] = expYear;
|
||||
}
|
||||
if (card.expirationYear) {
|
||||
creditCard["cc-exp-year"] = card.expirationYear;
|
||||
} else {
|
||||
delete creditCard["cc-exp-year"];
|
||||
}
|
||||
|
||||
if (creditCard["cc-exp"] && (!creditCard["cc-exp-month"] || !creditCard["cc-exp-year"])) {
|
||||
let rules = [
|
||||
{
|
||||
regex: "(\\d{4})[-/](\\d{1,2})",
|
||||
yearIndex: 1,
|
||||
monthIndex: 2,
|
||||
},
|
||||
{
|
||||
regex: "(\\d{1,2})[-/](\\d{4})",
|
||||
yearIndex: 2,
|
||||
monthIndex: 1,
|
||||
},
|
||||
{
|
||||
regex: "(\\d{1,2})[-/](\\d{1,2})",
|
||||
},
|
||||
{
|
||||
regex: "(\\d{2})(\\d{2})",
|
||||
},
|
||||
];
|
||||
|
||||
for (let rule of rules) {
|
||||
let result = new RegExp(`(?:^|\\D)${rule.regex}(?!\\d)`).exec(creditCard["cc-exp"]);
|
||||
if (!result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let expYear, expMonth;
|
||||
|
||||
if (!rule.yearIndex || !rule.monthIndex) {
|
||||
expMonth = parseInt(result[1], 10);
|
||||
if (expMonth > 12) {
|
||||
expYear = parseInt(result[1], 10);
|
||||
expMonth = parseInt(result[2], 10);
|
||||
} else {
|
||||
expYear = parseInt(result[2], 10);
|
||||
}
|
||||
} else {
|
||||
expYear = parseInt(result[rule.yearIndex], 10);
|
||||
expMonth = parseInt(result[rule.monthIndex], 10);
|
||||
}
|
||||
|
||||
if (expMonth < 1 || expMonth > 12) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (expYear < 100) {
|
||||
expYear += 2000;
|
||||
} else if (expYear < 2000) {
|
||||
continue;
|
||||
}
|
||||
|
||||
creditCard["cc-exp-month"] = expMonth;
|
||||
creditCard["cc-exp-year"] = expYear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete creditCard["cc-exp"];
|
||||
}
|
||||
|
||||
@ -1686,7 +1619,7 @@ class CreditCards extends AutofillRecords {
|
||||
if (MasterPassword.isEnabled) {
|
||||
// Compare the masked numbers instead when the master password is
|
||||
// enabled because we don't want to leak the credit card number.
|
||||
return this._getMaskedCCNumber(clonedTargetCreditCard[field]) == creditCard[field];
|
||||
return CreditCard.getLongMaskedNumber(clonedTargetCreditCard[field]) == creditCard[field];
|
||||
}
|
||||
return clonedTargetCreditCard[field] == MasterPassword.decryptSync(creditCard["cc-number-encrypted"]);
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ const MAX_FIELD_VALUE_LENGTH = 200;
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "CreditCard",
|
||||
"resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
let AddressDataLoader = {
|
||||
// Status of address data loading. We'll load all the countries with basic level 1
|
||||
@ -229,17 +231,9 @@ this.FormAutofillUtils = {
|
||||
return this._fieldNameInfo[fieldName] == "creditCard";
|
||||
},
|
||||
|
||||
normalizeCCNumber(ccNumber) {
|
||||
ccNumber = ccNumber.replace(/[-\s]/g, "");
|
||||
|
||||
// Based on the information on wiki[1], the shortest valid length should be
|
||||
// 12 digits(Maestro).
|
||||
// [1] https://en.wikipedia.org/wiki/Payment_card_number
|
||||
return ccNumber.match(/^\d{12,}$/) ? ccNumber : null;
|
||||
},
|
||||
|
||||
isCCNumber(ccNumber) {
|
||||
return !!this.normalizeCCNumber(ccNumber);
|
||||
let card = new CreditCard({number: ccNumber});
|
||||
return !!card.number;
|
||||
},
|
||||
|
||||
getCategoryFromFieldName(fieldName) {
|
||||
@ -263,42 +257,6 @@ this.FormAutofillUtils = {
|
||||
return " ";
|
||||
},
|
||||
|
||||
/**
|
||||
* Get credit card display label. It should display masked numbers and the
|
||||
* cardholder's name, separated by a comma. If `showCreditCards` is set to
|
||||
* true, decrypted credit card numbers are shown instead.
|
||||
*
|
||||
* @param {object} creditCard
|
||||
* @param {boolean} showCreditCards [optional]
|
||||
* @returns {string}
|
||||
*/
|
||||
getCreditCardLabel(creditCard, showCreditCards = false) {
|
||||
let parts = [];
|
||||
let ccLabel;
|
||||
let ccNumber = creditCard["cc-number"];
|
||||
let decryptedCCNumber = creditCard["cc-number-decrypted"];
|
||||
|
||||
if (showCreditCards && decryptedCCNumber) {
|
||||
ccLabel = decryptedCCNumber;
|
||||
}
|
||||
if (ccNumber && !ccLabel) {
|
||||
if (this.isCCNumber(ccNumber)) {
|
||||
ccLabel = "*".repeat(4) + " " + ccNumber.substr(-4);
|
||||
} else {
|
||||
let {affix, label} = this.fmtMaskedCreditCardLabel(ccNumber);
|
||||
ccLabel = `${affix} ${label}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (ccLabel) {
|
||||
parts.push(ccLabel);
|
||||
}
|
||||
if (creditCard["cc-name"]) {
|
||||
parts.push(creditCard["cc-name"]);
|
||||
}
|
||||
return parts.join(", ");
|
||||
},
|
||||
|
||||
/**
|
||||
* Get address display label. It should display up to two pieces of
|
||||
* information, separated by a comma.
|
||||
@ -379,13 +337,6 @@ this.FormAutofillUtils = {
|
||||
}
|
||||
},
|
||||
|
||||
fmtMaskedCreditCardLabel(maskedCCNum = "") {
|
||||
return {
|
||||
affix: "****",
|
||||
label: maskedCCNum.replace(/^\**/, ""),
|
||||
};
|
||||
},
|
||||
|
||||
defineLazyLogGetter(scope, logPrefix) {
|
||||
XPCOMUtils.defineLazyGetter(scope, "log", () => {
|
||||
let ConsoleAPI = ChromeUtils.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
|
||||
|
@ -11,6 +11,8 @@ var EXPORTED_SYMBOLS = ["AddressResult", "CreditCardResult"];
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "CreditCard",
|
||||
"resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "insecureWarningEnabled", "security.insecure_field_warning.contextual.enabled");
|
||||
|
||||
@ -339,7 +341,7 @@ class CreditCardResult extends ProfileAutoCompleteResult {
|
||||
|
||||
if (matching) {
|
||||
if (currentFieldName == "cc-number") {
|
||||
let {affix, label} = FormAutofillUtils.fmtMaskedCreditCardLabel(profile[currentFieldName]);
|
||||
let {affix, label} = CreditCard.formatMaskedNumber(profile[currentFieldName]);
|
||||
return affix + label;
|
||||
}
|
||||
return profile[currentFieldName];
|
||||
@ -374,7 +376,7 @@ class CreditCardResult extends ProfileAutoCompleteResult {
|
||||
let primary = profile[focusedFieldName];
|
||||
|
||||
if (focusedFieldName == "cc-number") {
|
||||
let {affix, label} = FormAutofillUtils.fmtMaskedCreditCardLabel(primary);
|
||||
let {affix, label} = CreditCard.formatMaskedNumber(primary);
|
||||
primaryAffix = affix;
|
||||
primary = label;
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://formautofill/FormAutofillUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "CreditCard",
|
||||
"resource://gre/modules/CreditCard.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "formAutofillStorage",
|
||||
"resource://formautofill/FormAutofillStorage.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "MasterPassword",
|
||||
@ -334,17 +336,17 @@ class ManageCreditCards extends ManageRecords {
|
||||
* cardholder's name, separated by a comma. If `showCreditCards` is set to
|
||||
* true, decrypted credit card numbers are shown instead.
|
||||
*
|
||||
* @param {object} creditCard
|
||||
* @param {boolean} showCreditCards [optional]
|
||||
* @param {object} creditCard
|
||||
* @param {boolean} showCreditCards [optional]
|
||||
* @returns {string}
|
||||
*/
|
||||
async getLabel(creditCard, showCreditCards = false) {
|
||||
let patchObj = {};
|
||||
if (creditCard["cc-number"] && showCreditCards) {
|
||||
patchObj["cc-number-decrypted"] = await MasterPassword.decrypt(creditCard["cc-number-encrypted"]);
|
||||
}
|
||||
|
||||
return FormAutofillUtils.getCreditCardLabel({...creditCard, ...patchObj}, showCreditCards);
|
||||
let cardObj = new CreditCard({
|
||||
encryptedNumber: creditCard["cc-number-encrypted"],
|
||||
number: creditCard["cc-number"],
|
||||
name: creditCard["cc-name"],
|
||||
});
|
||||
return cardObj.getLabel({showNumbers: showCreditCards});
|
||||
}
|
||||
|
||||
async toggleShowHideCards(options) {
|
||||
|
@ -18,7 +18,7 @@ add_task(async function test_submit_creditCard_cancel_saving() {
|
||||
name.setUserInput("User 1");
|
||||
|
||||
let number = form.querySelector("#cc-number");
|
||||
number.setUserInput("1111222233334444");
|
||||
number.setUserInput("5038146897157463");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
@ -57,7 +57,7 @@ add_task(async function test_submit_creditCard_saved() {
|
||||
name.focus();
|
||||
name.setUserInput("User 1");
|
||||
|
||||
form.querySelector("#cc-number").setUserInput("1111222233334444");
|
||||
form.querySelector("#cc-number").setUserInput("5038146897157463");
|
||||
form.querySelector("#cc-exp-month").setUserInput("12");
|
||||
form.querySelector("#cc-exp-year").setUserInput("2017");
|
||||
|
||||
@ -135,7 +135,7 @@ add_task(async function test_submit_changed_subset_creditCard_form() {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
name.setUserInput("");
|
||||
|
||||
form.querySelector("#cc-number").setUserInput("1234567812345678");
|
||||
form.querySelector("#cc-number").setUserInput("4111111111111111");
|
||||
form.querySelector("#cc-exp-month").setUserInput("4");
|
||||
form.querySelector("#cc-exp-year").setUserInput(new Date().getFullYear());
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
@ -173,7 +173,7 @@ add_task(async function test_submit_duplicate_creditCard_form() {
|
||||
name.focus();
|
||||
|
||||
name.setUserInput("John Doe");
|
||||
form.querySelector("#cc-number").setUserInput("1234567812345678");
|
||||
form.querySelector("#cc-number").setUserInput("4111111111111111");
|
||||
form.querySelector("#cc-exp-month").setUserInput("4");
|
||||
form.querySelector("#cc-exp-year").setUserInput(new Date().getFullYear());
|
||||
|
||||
@ -209,7 +209,7 @@ add_task(async function test_submit_unnormailzed_creditCard_form() {
|
||||
name.focus();
|
||||
|
||||
name.setUserInput("John Doe");
|
||||
form.querySelector("#cc-number").setUserInput("1234567812345678");
|
||||
form.querySelector("#cc-number").setUserInput("4111111111111111");
|
||||
form.querySelector("#cc-exp-month").setUserInput("4");
|
||||
// Set unnormalized year
|
||||
form.querySelector("#cc-exp-year").setUserInput(new Date().getFullYear().toString().substr(2, 2));
|
||||
@ -248,7 +248,7 @@ add_task(async function test_submit_creditCard_never_save() {
|
||||
name.setUserInput("User 0");
|
||||
|
||||
let number = form.querySelector("#cc-number");
|
||||
number.setUserInput("1234123412341234");
|
||||
number.setUserInput("6387060366272981");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
@ -286,7 +286,7 @@ add_task(async function test_submit_creditCard_saved_with_mp_enabled() {
|
||||
name.setUserInput("User 0");
|
||||
|
||||
let number = form.querySelector("#cc-number");
|
||||
number.setUserInput("1234123412341234");
|
||||
number.setUserInput("6387060366272981");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
@ -303,7 +303,7 @@ add_task(async function test_submit_creditCard_saved_with_mp_enabled() {
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "1 credit card in storage");
|
||||
is(creditCards[0]["cc-name"], "User 0", "Verify the name field");
|
||||
is(creditCards[0]["cc-number"], "************1234", "Verify the card number field");
|
||||
is(creditCards[0]["cc-number"], "************2981", "Verify the card number field");
|
||||
LoginTestUtils.masterPassword.disable();
|
||||
await removeAllRecords();
|
||||
});
|
||||
@ -323,7 +323,7 @@ add_task(async function test_submit_creditCard_saved_with_mp_enabled_but_cancele
|
||||
name.setUserInput("User 2");
|
||||
|
||||
let number = form.querySelector("#cc-number");
|
||||
number.setUserInput("5678567856785678");
|
||||
number.setUserInput("5471839082338112");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
@ -361,7 +361,7 @@ add_task(async function test_submit_creditCard_with_sync_account() {
|
||||
name.setUserInput("User 2");
|
||||
|
||||
let number = form.querySelector("#cc-number");
|
||||
number.setUserInput("1234123412341234");
|
||||
number.setUserInput("6387060366272981");
|
||||
|
||||
// Wait 500ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
@ -417,7 +417,7 @@ add_task(async function test_submit_creditCard_with_synced_already() {
|
||||
name.setUserInput("User 2");
|
||||
|
||||
let number = form.querySelector("#cc-number");
|
||||
number.setUserInput("1234123412341234");
|
||||
number.setUserInput("6387060366272981");
|
||||
|
||||
// Wait 500ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
@ -451,7 +451,7 @@ add_task(async function test_submit_manual_mergeable_creditCard_form() {
|
||||
name.focus();
|
||||
|
||||
name.setUserInput("User 3");
|
||||
form.querySelector("#cc-number").setUserInput("9999888877776666");
|
||||
form.querySelector("#cc-number").setUserInput("5103059495477870");
|
||||
form.querySelector("#cc-exp-month").setUserInput("1");
|
||||
form.querySelector("#cc-exp-year").setUserInput("2000");
|
||||
|
||||
@ -506,7 +506,7 @@ add_task(async function test_update_autofill_form_name() {
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card");
|
||||
is(creditCards[0]["cc-name"], "User 1", "cc-name field is updated");
|
||||
is(creditCards[0]["cc-number"], "************5678", "Verify the card number field");
|
||||
is(creditCards[0]["cc-number"], "************1111", "Verify the card number field");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 3, "User has used autofill");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
@ -546,7 +546,7 @@ add_task(async function test_update_autofill_form_exp_date() {
|
||||
creditCards = await getCreditCards();
|
||||
is(creditCards.length, 1, "Still 1 credit card");
|
||||
is(creditCards[0]["cc-exp-year"], "2020", "cc-exp-year field is updated");
|
||||
is(creditCards[0]["cc-number"], "************5678", "Verify the card number field");
|
||||
is(creditCards[0]["cc-number"], "************1111", "Verify the card number field");
|
||||
is(SpecialPowers.getIntPref(CREDITCARDS_USED_STATUS_PREF), 3, "User has used autofill");
|
||||
SpecialPowers.clearUserPref(CREDITCARDS_USED_STATUS_PREF);
|
||||
await removeAllRecords();
|
||||
@ -600,10 +600,10 @@ add_task(async function test_update_duplicate_autofill_form() {
|
||||
],
|
||||
});
|
||||
await saveCreditCard({
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "6387060366272981",
|
||||
});
|
||||
await saveCreditCard({
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "5038146897157463",
|
||||
});
|
||||
let creditCards = await getCreditCards();
|
||||
is(creditCards.length, 2, "2 credit card in storage");
|
||||
@ -615,9 +615,9 @@ add_task(async function test_update_duplicate_autofill_form() {
|
||||
await ContentTask.spawn(browser, null, async function() {
|
||||
let form = content.document.getElementById("form");
|
||||
let number = form.querySelector("#cc-number");
|
||||
is(number.value, "1234123412341234", "Should be the first credit card number");
|
||||
is(number.value, "6387060366272981", "Should be the first credit card number");
|
||||
// Change number to the second credit card number
|
||||
number.setUserInput("1111222233334444");
|
||||
number.setUserInput("5038146897157463");
|
||||
|
||||
// Wait 1000ms before submission to make sure the input value applied
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
@ -51,9 +51,9 @@ add_task(async function test_removingSingleAndMultipleCreditCards() {
|
||||
let btnEdit = win.document.querySelector(TEST_SELECTORS.btnEdit);
|
||||
|
||||
is(selRecords.length, 3, "Three credit cards");
|
||||
is(selRecords[0].text, "**** 6666", "Masked credit card 3");
|
||||
is(selRecords[1].text, "**** 4444, Timothy Berners-Lee", "Masked credit card 2");
|
||||
is(selRecords[2].text, "**** 5678, John Doe", "Masked credit card 1");
|
||||
is(selRecords[0].text, "**** 7870", "Masked credit card 3");
|
||||
is(selRecords[1].text, "**** 1045, Timothy Berners-Lee", "Masked credit card 2");
|
||||
is(selRecords[2].text, "**** 1111, John Doe", "Masked credit card 1");
|
||||
|
||||
EventUtils.synthesizeMouseAtCenter(selRecords.children[0], {}, win);
|
||||
is(btnRemove.disabled, false, "Remove button enabled");
|
||||
@ -125,29 +125,29 @@ add_task(async function test_showCreditCards() {
|
||||
// Show credit card numbers
|
||||
EventUtils.synthesizeMouseAtCenter(btnShowHideCreditCards, {}, win);
|
||||
await BrowserTestUtils.waitForEvent(selRecords, "LabelsUpdated");
|
||||
is(selRecords[0].text, "9999888877776666", "Decrypted credit card 3");
|
||||
is(selRecords[1].text, "1111222233334444, Timothy Berners-Lee", "Decrypted credit card 2");
|
||||
is(selRecords[2].text, "1234567812345678, John Doe", "Decrypted credit card 1");
|
||||
is(selRecords[0].text, "5103059495477870", "Decrypted credit card 3");
|
||||
is(selRecords[1].text, "4929001587121045, Timothy Berners-Lee", "Decrypted credit card 2");
|
||||
is(selRecords[2].text, "4111111111111111, John Doe", "Decrypted credit card 1");
|
||||
is(btnShowHideCreditCards.textContent, "Hide Credit Cards", "Label should be 'Hide Credit Cards'");
|
||||
|
||||
// Hide credit card numbers
|
||||
EventUtils.synthesizeMouseAtCenter(btnShowHideCreditCards, {}, win);
|
||||
await BrowserTestUtils.waitForEvent(selRecords, "LabelsUpdated");
|
||||
is(selRecords[0].text, "**** 6666", "Masked credit card 3");
|
||||
is(selRecords[1].text, "**** 4444, Timothy Berners-Lee", "Masked credit card 2");
|
||||
is(selRecords[2].text, "**** 5678, John Doe", "Masked credit card 1");
|
||||
is(selRecords[0].text, "**** 7870", "Masked credit card 3");
|
||||
is(selRecords[1].text, "**** 1045, Timothy Berners-Lee", "Masked credit card 2");
|
||||
is(selRecords[2].text, "**** 1111, John Doe", "Masked credit card 1");
|
||||
is(btnShowHideCreditCards.textContent, "Show Credit Cards", "Label should be 'Show Credit Cards'");
|
||||
|
||||
// Show credit card numbers again to test if they revert back to masked form when reloaded
|
||||
EventUtils.synthesizeMouseAtCenter(btnShowHideCreditCards, {}, win);
|
||||
await BrowserTestUtils.waitForEvent(selRecords, "LabelsUpdated");
|
||||
// Ensure credit card numbers are shown again
|
||||
is(selRecords[0].text, "9999888877776666", "Decrypted credit card 3");
|
||||
is(selRecords[0].text, "5103059495477870", "Decrypted credit card 3");
|
||||
// Remove a card to trigger reloading
|
||||
await removeCreditCards([selRecords.options[2].value]);
|
||||
await BrowserTestUtils.waitForEvent(selRecords, "RecordsLoaded");
|
||||
is(selRecords[0].text, "**** 6666", "Masked credit card 3");
|
||||
is(selRecords[1].text, "**** 4444, Timothy Berners-Lee", "Masked credit card 2");
|
||||
is(selRecords[0].text, "**** 7870", "Masked credit card 3");
|
||||
is(selRecords[1].text, "**** 1045, Timothy Berners-Lee", "Masked credit card 2");
|
||||
|
||||
// Remove the rest of the cards
|
||||
await removeCreditCards([selRecords.options[1].value]);
|
||||
|
@ -100,20 +100,20 @@ const TEST_ADDRESS_DE_1 = {
|
||||
|
||||
const TEST_CREDIT_CARD_1 = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": new Date().getFullYear(),
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_2 = {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": new Date().getFullYear() + 10,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_3 = {
|
||||
"cc-number": "9999888877776666",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 1,
|
||||
"cc-exp-year": 2000,
|
||||
};
|
||||
|
@ -202,9 +202,11 @@ async function cleanUpStorage() {
|
||||
}
|
||||
|
||||
function patchRecordCCNumber(record) {
|
||||
const ccNumber = record["cc-number"];
|
||||
const normalizedCCNumber = "*".repeat(ccNumber.length - 4) + ccNumber.substr(-4);
|
||||
const ccNumberFmt = FormAutofillUtils.fmtMaskedCreditCardLabel(normalizedCCNumber);
|
||||
const number = record["cc-number"];
|
||||
const ccNumberFmt = {
|
||||
affix: "****",
|
||||
label: number.substr(-4),
|
||||
};
|
||||
|
||||
return Object.assign({}, record, {ccNumberFmt});
|
||||
}
|
||||
|
@ -22,19 +22,19 @@ Form autofill test: simple form credit card autofill
|
||||
|
||||
const MOCK_STORAGE = [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
}, {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2022,
|
||||
}];
|
||||
|
||||
const reducedMockRecord = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234123456785678",
|
||||
"cc-number": "4929001587121045",
|
||||
};
|
||||
|
||||
async function setupCreditCardStorage() {
|
||||
|
@ -33,12 +33,12 @@ const MOCK_ADDR_STORAGE = [{
|
||||
}];
|
||||
const MOCK_CC_STORAGE = [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
}, {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2022,
|
||||
}];
|
||||
|
@ -22,12 +22,12 @@ Form autofill test: simple form credit card autofill
|
||||
|
||||
const MOCK_STORAGE = [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
}, {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2022,
|
||||
}];
|
||||
@ -40,7 +40,7 @@ async function setupCreditCardStorage() {
|
||||
async function setupFormHistory() {
|
||||
await updateFormHistory([
|
||||
{op: "add", fieldname: "cc-name", value: "John Smith"},
|
||||
{op: "add", fieldname: "cc-number", value: "1234000056780000"},
|
||||
{op: "add", fieldname: "cc-number", value: "6011029476355493"},
|
||||
]);
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ add_task(async function history_only_menu_checking() {
|
||||
await setInput("#cc-number", "");
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
await expectPopup();
|
||||
checkMenuEntries(["1234000056780000"], false);
|
||||
checkMenuEntries(["6011029476355493"], false);
|
||||
|
||||
await setInput("#cc-name", "");
|
||||
synthesizeKey("KEY_ArrowDown");
|
||||
|
@ -7,33 +7,34 @@
|
||||
const {FormAutofillStorage} = ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm", {});
|
||||
ChromeUtils.defineModuleGetter(this, "Preferences",
|
||||
"resource://gre/modules/Preferences.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
const TEST_STORE_FILE_NAME = "test-credit-card.json";
|
||||
const COLLECTION_NAME = "creditCards";
|
||||
|
||||
const TEST_CREDIT_CARD_1 = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_2 = {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2022,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_3 = {
|
||||
"cc-number": "9999888877776666",
|
||||
"cc-number": "3589993783099582",
|
||||
"cc-exp-month": 1,
|
||||
"cc-exp-year": 2000,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_4 = {
|
||||
"cc-name": "Foo Bar",
|
||||
"cc-number": "9999888877776666",
|
||||
"cc-number": "3589993783099582",
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_WITH_BILLING_ADDRESS = {
|
||||
@ -45,7 +46,7 @@ const TEST_CREDIT_CARD_WITH_BILLING_ADDRESS = {
|
||||
const TEST_CREDIT_CARD_WITH_EMPTY_FIELD = {
|
||||
billingAddressGUID: "",
|
||||
"cc-name": "",
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "344060747836806",
|
||||
"cc-exp-month": 1,
|
||||
};
|
||||
|
||||
@ -54,31 +55,31 @@ const TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD = {
|
||||
"cc-additional-name": "",
|
||||
"cc-family-name": "",
|
||||
"cc-exp": "",
|
||||
"cc-number": "1928374619283746",
|
||||
"cc-number": "5415425865751454",
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_WITH_2_DIGITS_YEAR = {
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "344060747836806",
|
||||
"cc-exp-month": 1,
|
||||
"cc-exp-year": 12,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_WITH_INVALID_FIELD = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "344060747836806",
|
||||
invalidField: "INVALID",
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_WITH_INVALID_EXPIRY_DATE = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 13,
|
||||
"cc-exp-year": -3,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_WITH_SPACES_BETWEEN_DIGITS = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111 2222 3333 4444",
|
||||
"cc-number": "5103 0594 9547 7870",
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_EMPTY_AFTER_NORMALIZE = {
|
||||
@ -96,19 +97,19 @@ const MERGE_TESTCASES = [
|
||||
{
|
||||
description: "Merge a superset",
|
||||
creditCardInStorage: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
creditCardToMerge: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
expectedCreditCard: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
@ -116,14 +117,14 @@ const MERGE_TESTCASES = [
|
||||
{
|
||||
description: "Merge a superset with billingAddressGUID",
|
||||
creditCardInStorage: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
creditCardToMerge: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
billingAddressGUID: "ijsnbhfr",
|
||||
},
|
||||
expectedCreditCard: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
billingAddressGUID: "ijsnbhfr",
|
||||
},
|
||||
},
|
||||
@ -131,18 +132,18 @@ const MERGE_TESTCASES = [
|
||||
description: "Merge a subset",
|
||||
creditCardInStorage: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
creditCardToMerge: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
expectedCreditCard: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
@ -151,15 +152,15 @@ const MERGE_TESTCASES = [
|
||||
{
|
||||
description: "Merge a subset with billingAddressGUID",
|
||||
creditCardInStorage: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
billingAddressGUID: "8fhdb3ug6",
|
||||
},
|
||||
creditCardToMerge: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
expectedCreditCard: {
|
||||
billingAddressGUID: "8fhdb3ug6",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
noNeedToUpdate: true,
|
||||
},
|
||||
@ -167,16 +168,16 @@ const MERGE_TESTCASES = [
|
||||
description: "Merge an creditCard with partial overlaps",
|
||||
creditCardInStorage: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
creditCardToMerge: {
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
expectedCreditCard: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
},
|
||||
@ -317,7 +318,7 @@ add_task(async function test_add() {
|
||||
profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD);
|
||||
creditCard = profileStorage.creditCards._data[3];
|
||||
Assert.equal(creditCard["cc-number"],
|
||||
profileStorage.creditCards._getMaskedCCNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
|
||||
CreditCard.getLongMaskedNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
|
||||
|
||||
Assert.throws(() => profileStorage.creditCards.add(TEST_CREDIT_CARD_WITH_INVALID_FIELD),
|
||||
/"invalidField" is not a valid field\./);
|
||||
@ -399,11 +400,11 @@ add_task(async function test_update() {
|
||||
profileStorage.creditCards.update(profileStorage.creditCards._data[0].guid, TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD, false);
|
||||
creditCard = profileStorage.creditCards._data[0];
|
||||
Assert.equal(creditCard["cc-number"],
|
||||
profileStorage.creditCards._getMaskedCCNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
|
||||
CreditCard.getLongMaskedNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
|
||||
profileStorage.creditCards.update(profileStorage.creditCards._data[1].guid, TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD, true);
|
||||
creditCard = profileStorage.creditCards._data[1];
|
||||
Assert.equal(creditCard["cc-number"],
|
||||
profileStorage.creditCards._getMaskedCCNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
|
||||
CreditCard.getLongMaskedNumber(TEST_CREDIT_CARD_WITH_EMPTY_COMPUTED_FIELD["cc-number"]));
|
||||
|
||||
Assert.throws(
|
||||
() => profileStorage.creditCards.update("INVALID_GUID", TEST_CREDIT_CARD_3),
|
||||
@ -635,7 +636,9 @@ add_task(async function test_getDuplicateGuid() {
|
||||
// Numbers with the same last 4 digits shouldn't be treated as a duplicate.
|
||||
record = Object.assign({}, TEST_CREDIT_CARD_3);
|
||||
let last4Digits = record["cc-number"].substr(-4);
|
||||
record["cc-number"] = "000000000000" + last4Digits;
|
||||
// This number differs from TEST_CREDIT_CARD_3 by swapping the order of the
|
||||
// 09 and 90 adjacent digits, which is still a valid credit card number.
|
||||
record["cc-number"] = "358999378390" + last4Digits;
|
||||
Assert.equal(profileStorage.creditCards.getDuplicateGuid(record), null);
|
||||
|
||||
// ... However, we treat numbers with the same last 4 digits as a duplicate if
|
||||
|
@ -7,6 +7,7 @@
|
||||
let {FormAutofillParent} = ChromeUtils.import("resource://formautofill/FormAutofillParent.jsm", {});
|
||||
ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
|
||||
ChromeUtils.import("resource://formautofill/FormAutofillStorage.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
const TEST_ADDRESS_1 = {
|
||||
"given-name": "Timothy",
|
||||
@ -29,14 +30,14 @@ const TEST_ADDRESS_2 = {
|
||||
|
||||
let TEST_CREDIT_CARD_1 = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
};
|
||||
|
||||
let TEST_CREDIT_CARD_2 = {
|
||||
"cc-name": "John Dai",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 2,
|
||||
"cc-exp-year": 2017,
|
||||
};
|
||||
@ -59,7 +60,7 @@ add_task(async function test_getRecords() {
|
||||
}],
|
||||
creditCards: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
}],
|
||||
@ -169,7 +170,7 @@ add_task(async function test_getRecords_creditCards() {
|
||||
let collection = formAutofillParent.formAutofillStorage.creditCards;
|
||||
let encryptedCCRecords = [TEST_CREDIT_CARD_1, TEST_CREDIT_CARD_2].map(record => {
|
||||
let clonedRecord = Object.assign({}, record);
|
||||
clonedRecord["cc-number"] = collection._getMaskedCCNumber(record["cc-number"]);
|
||||
clonedRecord["cc-number"] = CreditCard.getLongMaskedNumber(record["cc-number"]);
|
||||
clonedRecord["cc-number-encrypted"] = MasterPassword.encryptSync(record["cc-number"]);
|
||||
return clonedRecord;
|
||||
});
|
||||
@ -212,7 +213,7 @@ add_task(async function test_getRecords_creditCards() {
|
||||
filter: {
|
||||
collectionName: "creditCards",
|
||||
info: {fieldName: "cc-number"},
|
||||
searchString: "123",
|
||||
searchString: "411",
|
||||
},
|
||||
expectedResult: CreditCardsWithDecryptedNumber.slice(0, 1),
|
||||
},
|
||||
@ -221,7 +222,7 @@ add_task(async function test_getRecords_creditCards() {
|
||||
filter: {
|
||||
collectionName: "creditCards",
|
||||
info: {fieldName: "cc-number"},
|
||||
searchString: "1",
|
||||
searchString: "4",
|
||||
},
|
||||
expectedResult: CreditCardsWithDecryptedNumber,
|
||||
},
|
||||
@ -240,7 +241,7 @@ add_task(async function test_getRecords_creditCards() {
|
||||
filter: {
|
||||
collectionName: "creditCards",
|
||||
info: {fieldName: "cc-number"},
|
||||
searchString: "123",
|
||||
searchString: "411",
|
||||
},
|
||||
mpEnabled: true,
|
||||
expectedResult: encryptedCCRecords,
|
||||
|
@ -472,13 +472,13 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "2bbd2d8fbc6b",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
// The current local record - by comparing against parent we can see that
|
||||
// only the cc-number has changed locally.
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
}],
|
||||
remote: {
|
||||
// This is the incoming record. It has the same values as "parent", so
|
||||
@ -487,12 +487,12 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "2bbd2d8fbc6b",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "2bbd2d8fbc6b",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -501,22 +501,22 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "e3680e9f890d",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
}],
|
||||
remote: {
|
||||
"guid": "e3680e9f890d",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "e3680e9f890d",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
},
|
||||
|
||||
@ -526,23 +526,23 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "0cba738b1be0",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
}],
|
||||
remote: {
|
||||
"guid": "0cba738b1be0",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "0cba738b1be0",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
},
|
||||
@ -552,23 +552,23 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "be3ef97f8285",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
}],
|
||||
remote: {
|
||||
"guid": "be3ef97f8285",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "be3ef97f8285",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
},
|
||||
@ -578,24 +578,24 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "9627322248ec",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
}],
|
||||
remote: {
|
||||
"guid": "9627322248ec",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "9627322248ec",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -604,24 +604,24 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "7d7509f3eeb2",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
}],
|
||||
remote: {
|
||||
"guid": "7d7509f3eeb2",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "7d7509f3eeb2",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -631,13 +631,13 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "e087a06dfc57",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
local: [{
|
||||
// The current local record - so locally we've changed "cc-number".
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 12,
|
||||
}],
|
||||
remote: {
|
||||
@ -645,13 +645,13 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "e087a06dfc57",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 1,
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "e087a06dfc57",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 1,
|
||||
},
|
||||
},
|
||||
@ -661,27 +661,27 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "340a078c596f",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "Skip",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
}, {
|
||||
"cc-name": "Skip",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
}],
|
||||
remote: {
|
||||
"guid": "340a078c596f",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-year": 2000,
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "340a078c596f",
|
||||
"cc-name": "Skip",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2000,
|
||||
},
|
||||
@ -694,22 +694,22 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "0b3a72a1bea2",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
}],
|
||||
remote: {
|
||||
"guid": "0b3a72a1bea2",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "0b3a72a1bea2",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -719,31 +719,31 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "62068784d089",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
local: [{
|
||||
// The current version of the local record - the cc-number has changed locally.
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111111111111111",
|
||||
"cc-number": "5103059495477870",
|
||||
}],
|
||||
remote: {
|
||||
// An incoming record has a different cc-number than any of the above!
|
||||
"guid": "62068784d089",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
forked: {
|
||||
// So we've forked the local record to a new GUID (and the next sync is
|
||||
// going to write this as a new record)
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111111111111111",
|
||||
"cc-number": "5103059495477870",
|
||||
},
|
||||
reconciled: {
|
||||
// And we've updated the local version of the record to be the remote version.
|
||||
guid: "62068784d089",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -752,30 +752,30 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "244dbb692e94",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111111111111111",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 1,
|
||||
}],
|
||||
remote: {
|
||||
"guid": "244dbb692e94",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 3,
|
||||
},
|
||||
forked: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111111111111111",
|
||||
"cc-number": "5103059495477870",
|
||||
"cc-exp-month": 1,
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "244dbb692e94",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 3,
|
||||
},
|
||||
},
|
||||
@ -785,28 +785,28 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "6fc45e03d19a",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
}],
|
||||
remote: {
|
||||
"guid": "6fc45e03d19a",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 3,
|
||||
},
|
||||
forked: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "6fc45e03d19a",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 3,
|
||||
},
|
||||
},
|
||||
@ -816,29 +816,29 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "fff9fa27fa18",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 12,
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 3,
|
||||
}],
|
||||
remote: {
|
||||
"guid": "fff9fa27fa18",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
forked: {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 3,
|
||||
},
|
||||
reconciled: {
|
||||
"guid": "fff9fa27fa18",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -850,7 +850,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "5113f329c42f",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"timeCreated": 1234,
|
||||
"timeLastModified": 5678,
|
||||
"timeLastUsed": 5678,
|
||||
@ -861,7 +861,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "5113f329c42f",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"timeCreated": 1200,
|
||||
"timeLastModified": 5700,
|
||||
"timeLastUsed": 5700,
|
||||
@ -870,7 +870,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
reconciled: {
|
||||
"guid": "5113f329c42f",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"timeCreated": 1200,
|
||||
"timeLastModified": 5700,
|
||||
"timeLastUsed": 5678,
|
||||
@ -885,7 +885,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
"guid": "791e5608b80a",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"timeCreated": 1234,
|
||||
"timeLastModified": 5678,
|
||||
"timeLastUsed": 5678,
|
||||
@ -893,13 +893,13 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
},
|
||||
local: [{
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
}],
|
||||
remote: {
|
||||
"guid": "791e5608b80a",
|
||||
"version": 1,
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4111111111111111",
|
||||
"timeCreated": 1300,
|
||||
"timeLastModified": 5000,
|
||||
"timeLastUsed": 5000,
|
||||
@ -908,7 +908,7 @@ const CREDIT_CARD_RECONCILE_TESTCASES = [
|
||||
reconciled: {
|
||||
"guid": "791e5608b80a",
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "4444333322221111",
|
||||
"cc-number": "4929001587121045",
|
||||
"timeCreated": 1234,
|
||||
"timeLastUsed": 5678,
|
||||
"timesUsed": 6,
|
||||
|
@ -29,14 +29,14 @@ const TEST_ADDRESS_2 = {
|
||||
|
||||
const TEST_CREDIT_CARD_1 = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
};
|
||||
|
||||
const TEST_CREDIT_CARD_2 = {
|
||||
"cc-name": "Timothy Berners-Lee",
|
||||
"cc-number": "1111222233334444",
|
||||
"cc-number": "4929001587121045",
|
||||
"cc-exp-month": 12,
|
||||
"cc-exp-year": 2022,
|
||||
};
|
||||
|
@ -24,7 +24,7 @@ const TEST_ADDRESS_1 = {
|
||||
|
||||
const TEST_CC_1 = {
|
||||
"cc-name": "John Doe",
|
||||
"cc-number": "1234567812345678",
|
||||
"cc-number": "4111111111111111",
|
||||
"cc-exp-month": 4,
|
||||
"cc-exp-year": 2017,
|
||||
};
|
||||
|
@ -539,10 +539,10 @@ const CREDIT_CARD_COMPUTE_TESTCASES = [
|
||||
{
|
||||
description: "Number should be encrypted and masked",
|
||||
creditCard: {
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
expectedResult: {
|
||||
"cc-number": "************1234",
|
||||
"cc-number": "************1045",
|
||||
},
|
||||
},
|
||||
|
||||
@ -609,28 +609,28 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
|
||||
{
|
||||
description: "Regular number",
|
||||
creditCard: {
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
expectedResult: {
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "4929001587121045",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Number with spaces",
|
||||
creditCard: {
|
||||
"cc-number": "1234 1234 1234 1234",
|
||||
"cc-number": "4111 1111 1111 1111",
|
||||
},
|
||||
expectedResult: {
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
},
|
||||
{
|
||||
description: "Number with hyphens",
|
||||
creditCard: {
|
||||
"cc-number": "1234-1234-1234-1234",
|
||||
"cc-number": "4111-1111-1111-1111",
|
||||
},
|
||||
expectedResult: {
|
||||
"cc-number": "1234123412341234",
|
||||
"cc-number": "4111111111111111",
|
||||
},
|
||||
},
|
||||
|
||||
@ -788,7 +788,7 @@ const CREDIT_CARD_NORMALIZE_TESTCASES = [
|
||||
{
|
||||
description: "Has invalid \"cc-exp\"",
|
||||
creditCard: {
|
||||
"cc-number": "1111222233334444", // Make sure it won't be an empty record.
|
||||
"cc-number": "4111111111111111", // Make sure it won't be an empty record.
|
||||
"cc-exp": "99-9999",
|
||||
},
|
||||
expectedResult: {
|
||||
|
@ -4,6 +4,9 @@
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "CreditCard",
|
||||
"resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
(function() {
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
@ -31,36 +34,6 @@ let satchelFormListener = {
|
||||
this.enabled = Services.prefs.getBoolPref("browser.formfill.enable");
|
||||
},
|
||||
|
||||
// Implements the Luhn checksum algorithm as described at
|
||||
// http://wikipedia.org/wiki/Luhn_algorithm
|
||||
isValidCCNumber(ccNumber) {
|
||||
// Remove dashes and whitespace
|
||||
ccNumber = ccNumber.replace(/[\-\s]/g, "");
|
||||
|
||||
let len = ccNumber.length;
|
||||
if (len != 9 && len != 15 && len != 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!/^\d+$/.test(ccNumber)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let ch = parseInt(ccNumber[len - i - 1], 10);
|
||||
if (i % 2 == 1) {
|
||||
// Double it, add digits together if > 10
|
||||
ch *= 2;
|
||||
if (ch > 9) {
|
||||
ch -= 9;
|
||||
}
|
||||
}
|
||||
total += ch;
|
||||
}
|
||||
return total % 10 == 0;
|
||||
},
|
||||
|
||||
log(message) {
|
||||
if (!this.debug) {
|
||||
return;
|
||||
@ -127,7 +100,7 @@ let satchelFormListener = {
|
||||
}
|
||||
|
||||
// Don't save credit card numbers.
|
||||
if (this.isValidCCNumber(value)) {
|
||||
if (CreditCard.isValidNumber(value)) {
|
||||
this.log("skipping saving a credit card number");
|
||||
continue;
|
||||
}
|
||||
|
295
toolkit/modules/CreditCard.jsm
Normal file
295
toolkit/modules/CreditCard.jsm
Normal file
@ -0,0 +1,295 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["CreditCard"];
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "MasterPassword",
|
||||
"resource://formautofill/MasterPassword.jsm");
|
||||
|
||||
class CreditCard {
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string} number
|
||||
* @param {string} expirationString
|
||||
* @param {string|number} expirationMonth
|
||||
* @param {string|number} expirationYear
|
||||
* @param {string|number} ccv
|
||||
* @param {string} encryptedNumber
|
||||
*/
|
||||
constructor({
|
||||
name,
|
||||
number,
|
||||
expirationString,
|
||||
expirationMonth,
|
||||
expirationYear,
|
||||
ccv,
|
||||
encryptedNumber
|
||||
}) {
|
||||
this._name = name;
|
||||
this._unmodifiedNumber = number;
|
||||
this._encryptedNumber = encryptedNumber;
|
||||
this._ccv = ccv;
|
||||
this.number = number;
|
||||
// Only prefer the string version if missing one or both parsed formats.
|
||||
if (expirationString && (!expirationMonth || !expirationYear)) {
|
||||
this.expirationString = expirationString;
|
||||
} else {
|
||||
this.expirationMonth = expirationMonth;
|
||||
this.expirationYear = expirationYear;
|
||||
}
|
||||
}
|
||||
|
||||
set name(value) {
|
||||
this._name = value;
|
||||
}
|
||||
|
||||
set expirationMonth(value) {
|
||||
if (typeof value == "undefined") {
|
||||
this._expirationMonth = undefined;
|
||||
return;
|
||||
}
|
||||
this._expirationMonth = this._normalizeExpirationMonth(value);
|
||||
}
|
||||
|
||||
get expirationMonth() {
|
||||
return this._expirationMonth;
|
||||
}
|
||||
|
||||
set expirationYear(value) {
|
||||
if (typeof value == "undefined") {
|
||||
this._expirationYear = undefined;
|
||||
return;
|
||||
}
|
||||
this._expirationYear = this._normalizeExpirationYear(value);
|
||||
}
|
||||
|
||||
get expirationYear() {
|
||||
return this._expirationYear;
|
||||
}
|
||||
|
||||
set expirationString(value) {
|
||||
let {month, year} = this._parseExpirationString(value);
|
||||
this.expirationMonth = month;
|
||||
this.expirationYear = year;
|
||||
}
|
||||
|
||||
set ccv(value) {
|
||||
this._ccv = value;
|
||||
}
|
||||
|
||||
get number() {
|
||||
return this._number;
|
||||
}
|
||||
|
||||
set number(value) {
|
||||
if (value) {
|
||||
let normalizedNumber = value.replace(/[-\s]/g, "");
|
||||
// Based on the information on wiki[1], the shortest valid length should be
|
||||
// 9 digits (Canadian SIN).
|
||||
// [1] https://en.wikipedia.org/wiki/Social_Insurance_Number
|
||||
normalizedNumber = normalizedNumber.match(/^\d{9,}$/) ?
|
||||
normalizedNumber : null;
|
||||
this._number = normalizedNumber;
|
||||
}
|
||||
}
|
||||
|
||||
// Implements the Luhn checksum algorithm as described at
|
||||
// http://wikipedia.org/wiki/Luhn_algorithm
|
||||
isValidNumber() {
|
||||
if (!this._number) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove dashes and whitespace
|
||||
let number = this._number.replace(/[\-\s]/g, "");
|
||||
|
||||
let len = number.length;
|
||||
if (len != 9 && len != 15 && len != 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!/^\d+$/.test(number)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
for (let i = 0; i < len; i++) {
|
||||
let ch = parseInt(number[len - i - 1], 10);
|
||||
if (i % 2 == 1) {
|
||||
// Double it, add digits together if > 10
|
||||
ch *= 2;
|
||||
if (ch > 9) {
|
||||
ch -= 9;
|
||||
}
|
||||
}
|
||||
total += ch;
|
||||
}
|
||||
return total % 10 == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the card number is valid and the
|
||||
* expiration date has not passed. Otherwise false.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isValid() {
|
||||
if (!this.isValidNumber()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let currentDate = new Date();
|
||||
let currentYear = currentDate.getFullYear();
|
||||
if (this._expirationYear > currentYear) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// getMonth is 0-based, so add 1 because credit cards are 1-based
|
||||
let currentMonth = currentDate.getMonth() + 1;
|
||||
return this._expirationYear == currentYear &&
|
||||
this._expirationMonth >= currentMonth;
|
||||
}
|
||||
|
||||
get maskedNumber() {
|
||||
if (!this.isValidNumber()) {
|
||||
throw new Error("Invalid credit card number");
|
||||
}
|
||||
return "*".repeat(4) + " " + this._number.substr(-4);
|
||||
}
|
||||
|
||||
get longMaskedNumber() {
|
||||
if (!this.isValidNumber()) {
|
||||
throw new Error("Invalid credit card number");
|
||||
}
|
||||
return "*".repeat(this.number.length - 4) + this.number.substr(-4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get credit card display label. It should display masked numbers and the
|
||||
* cardholder's name, separated by a comma. If `showNumbers` is set to
|
||||
* true, decrypted credit card numbers are shown instead.
|
||||
*/
|
||||
async getLabel({showNumbers} = {}) {
|
||||
let parts = [];
|
||||
let label;
|
||||
|
||||
if (showNumbers) {
|
||||
if (this._encryptedNumber) {
|
||||
label = await MasterPassword.decrypt(this._encryptedNumber);
|
||||
} else {
|
||||
label = this._number;
|
||||
}
|
||||
}
|
||||
if (this._unmodifiedNumber && !label) {
|
||||
if (this.isValidNumber()) {
|
||||
label = this.maskedNumber;
|
||||
} else {
|
||||
let maskedNumber = CreditCard.formatMaskedNumber(this._unmodifiedNumber);
|
||||
label = `${maskedNumber.affix} ${maskedNumber.label}`;
|
||||
}
|
||||
}
|
||||
|
||||
if (label) {
|
||||
parts.push(label);
|
||||
}
|
||||
if (this._name) {
|
||||
parts.push(this._name);
|
||||
}
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
_normalizeExpirationMonth(month) {
|
||||
month = parseInt(month, 10);
|
||||
if (isNaN(month) || month < 1 || month > 12) {
|
||||
return undefined;
|
||||
}
|
||||
return month;
|
||||
}
|
||||
|
||||
_normalizeExpirationYear(year) {
|
||||
year = parseInt(year, 10);
|
||||
if (isNaN(year) || year < 0) {
|
||||
return undefined;
|
||||
}
|
||||
if (year < 100) {
|
||||
year += 2000;
|
||||
}
|
||||
return year;
|
||||
}
|
||||
|
||||
_parseExpirationString(expirationString) {
|
||||
let rules = [
|
||||
{
|
||||
regex: "(\\d{4})[-/](\\d{1,2})",
|
||||
yearIndex: 1,
|
||||
monthIndex: 2,
|
||||
},
|
||||
{
|
||||
regex: "(\\d{1,2})[-/](\\d{4})",
|
||||
yearIndex: 2,
|
||||
monthIndex: 1,
|
||||
},
|
||||
{
|
||||
regex: "(\\d{1,2})[-/](\\d{1,2})",
|
||||
},
|
||||
{
|
||||
regex: "(\\d{2})(\\d{2})",
|
||||
},
|
||||
];
|
||||
|
||||
for (let rule of rules) {
|
||||
let result = new RegExp(`(?:^|\\D)${rule.regex}(?!\\d)`).exec(expirationString);
|
||||
if (!result) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let year, month;
|
||||
|
||||
if (!rule.yearIndex || !rule.monthIndex) {
|
||||
month = parseInt(result[1], 10);
|
||||
if (month > 12) {
|
||||
year = parseInt(result[1], 10);
|
||||
month = parseInt(result[2], 10);
|
||||
} else {
|
||||
year = parseInt(result[2], 10);
|
||||
}
|
||||
} else {
|
||||
year = parseInt(result[rule.yearIndex], 10);
|
||||
month = parseInt(result[rule.monthIndex], 10);
|
||||
}
|
||||
|
||||
if ((month < 1 || month > 12) ||
|
||||
(year >= 100 && year < 2000)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return {month, year};
|
||||
}
|
||||
return {month: undefined, year: undefined};
|
||||
}
|
||||
|
||||
static formatMaskedNumber(maskedNumber) {
|
||||
return {
|
||||
affix: "****",
|
||||
label: maskedNumber.replace(/^\**/, ""),
|
||||
};
|
||||
}
|
||||
|
||||
static getMaskedNumber(number) {
|
||||
let creditCard = new CreditCard({number});
|
||||
return creditCard.maskedNumber;
|
||||
}
|
||||
|
||||
static getLongMaskedNumber(number) {
|
||||
let creditCard = new CreditCard({number});
|
||||
return creditCard.longMaskedNumber;
|
||||
}
|
||||
|
||||
static isValidNumber(number) {
|
||||
let creditCard = new CreditCard({number});
|
||||
return creditCard.isValidNumber();
|
||||
}
|
||||
}
|
@ -189,6 +189,7 @@ EXTRA_JS_MODULES += [
|
||||
'ClientID.jsm',
|
||||
'Color.jsm',
|
||||
'Console.jsm',
|
||||
'CreditCard.jsm',
|
||||
'css-selector.js',
|
||||
'DateTimePickerHelper.jsm',
|
||||
'DeferredTask.jsm',
|
||||
|
@ -7,6 +7,9 @@
|
||||
var EXPORTED_SYMBOLS = ["FormData"];
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "CreditCard",
|
||||
"resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
/**
|
||||
* Returns whether the given URL very likely has input
|
||||
* fields that contain serialized session store data.
|
||||
@ -35,44 +38,6 @@ function getDocumentURI(doc) {
|
||||
return doc.documentURI.replace(/#.*$/, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given value is a valid credit card number based on
|
||||
* the Luhn algorithm. See https://en.wikipedia.org/wiki/Luhn_algorithm.
|
||||
*/
|
||||
function isValidCCNumber(value) {
|
||||
// Remove dashes and whitespace.
|
||||
let ccNumber = value.replace(/[-\s]+/g, "");
|
||||
|
||||
// Check for non-alphanumeric characters.
|
||||
if (/[^0-9]/.test(ccNumber)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for invalid length.
|
||||
let length = ccNumber.length;
|
||||
if (length != 9 && length != 15 && length != 16) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let total = 0;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let currentChar = ccNumber.charAt(length - i - 1);
|
||||
let currentDigit = parseInt(currentChar, 10);
|
||||
|
||||
if (i % 2) {
|
||||
// Double every other value.
|
||||
total += currentDigit * 2;
|
||||
// If the doubled value has two digits, add the digits together.
|
||||
if (currentDigit > 4) {
|
||||
total -= 9;
|
||||
}
|
||||
} else {
|
||||
total += currentDigit;
|
||||
}
|
||||
}
|
||||
return total % 10 == 0;
|
||||
}
|
||||
|
||||
// For a comprehensive list of all available <INPUT> types see
|
||||
// https://dxr.mozilla.org/mozilla-central/search?q=kInputTypeTable&redirect=false
|
||||
const IGNORE_PROPERTIES = [
|
||||
@ -198,9 +163,10 @@ var FormDataInternal = {
|
||||
}
|
||||
|
||||
// We do not want to collect credit card numbers.
|
||||
if (ChromeUtils.getClassName(node) === "HTMLInputElement" &&
|
||||
isValidCCNumber(node.value)) {
|
||||
continue;
|
||||
if (ChromeUtils.getClassName(node) === "HTMLInputElement") {
|
||||
if (CreditCard.isValidNumber(node.value)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ChromeUtils.getClassName(node) === "HTMLInputElement" ||
|
||||
|
@ -29,6 +29,7 @@ support-files =
|
||||
[browser_AsyncPrefs.js]
|
||||
[browser_Battery.js]
|
||||
[browser_BrowserUtils.js]
|
||||
[browser_CreditCard.js]
|
||||
[browser_Deprecated.js]
|
||||
[browser_Finder.js]
|
||||
[browser_Finder_hidden_textarea.js]
|
||||
|
45
toolkit/modules/tests/browser/browser_CreditCard.js
Normal file
45
toolkit/modules/tests/browser/browser_CreditCard.js
Normal file
@ -0,0 +1,45 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
|
||||
ChromeUtils.import("resource://formautofill/MasterPassword.jsm");
|
||||
|
||||
let oldGetters = {};
|
||||
let gFakeLoggedIn = true;
|
||||
|
||||
add_task(function setup() {
|
||||
oldGetters._token = Object.getOwnPropertyDescriptor(MasterPassword, "_token").get;
|
||||
oldGetters.isEnabled = Object.getOwnPropertyDescriptor(MasterPassword, "isEnabled").get;
|
||||
oldGetters.isLoggedIn = Object.getOwnPropertyDescriptor(MasterPassword, "isLoggedIn").get;
|
||||
MasterPassword.__defineGetter__("_token", () => { return {hasPassword: true}; });
|
||||
MasterPassword.__defineGetter__("isEnabled", () => true);
|
||||
MasterPassword.__defineGetter__("isLoggedIn", () => gFakeLoggedIn);
|
||||
registerCleanupFunction(() => {
|
||||
MasterPassword.__defineGetter__("_token", oldGetters._token);
|
||||
MasterPassword.__defineGetter__("isEnabled", oldGetters.isEnabled);
|
||||
MasterPassword.__defineGetter__("isLoggedIn", oldGetters.isLoggedIn);
|
||||
|
||||
// CreditCard.jsm and MasterPassword.jsm are imported into the global scope
|
||||
// -- the window -- above. If they're not deleted, they outlive the test and
|
||||
// are reported as a leak.
|
||||
delete window.MasterPassword;
|
||||
delete window.CreditCard;
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_getLabel_withMasterPassword() {
|
||||
ok(MasterPassword.isEnabled, "Confirm that MasterPassword is faked and thinks it is enabled");
|
||||
ok(MasterPassword.isLoggedIn, "Confirm that MasterPassword is faked and thinks it is logged in");
|
||||
|
||||
const ccNumber = "4111111111111111";
|
||||
const encryptedNumber = await MasterPassword.encrypt(ccNumber);
|
||||
const decryptedNumber = await MasterPassword.decrypt(encryptedNumber);
|
||||
is(decryptedNumber, ccNumber, "Decrypted CC number should match original");
|
||||
|
||||
const name = "Foxkeh";
|
||||
const creditCard = new CreditCard({encryptedNumber, name: "Foxkeh"});
|
||||
const label = await creditCard.getLabel({showNumbers: true});
|
||||
is(label, `${ccNumber}, ${name}`);
|
||||
});
|
274
toolkit/modules/tests/xpcshell/test_CreditCard.js
Normal file
274
toolkit/modules/tests/xpcshell/test_CreditCard.js
Normal file
@ -0,0 +1,274 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/CreditCard.jsm");
|
||||
|
||||
add_task(function isValidNumber() {
|
||||
function testValid(number, shouldPass) {
|
||||
if (shouldPass) {
|
||||
ok(CreditCard.isValidNumber(number), `${number} should be considered valid`);
|
||||
} else {
|
||||
ok(!CreditCard.isValidNumber(number), `${number} should not be considered valid`);
|
||||
}
|
||||
}
|
||||
|
||||
testValid("0000000000000000", true);
|
||||
testValid("4929001587121045", true);
|
||||
testValid("5103059495477870", true);
|
||||
testValid("6011029476355493", true);
|
||||
testValid("3589993783099582", true);
|
||||
testValid("5415425865751454", true);
|
||||
if (CreditCard.isValidNumber("30190729470495")) {
|
||||
ok(false, "todo: 14-digit numbers (Diners Club) aren't supported by isValidNumber yet");
|
||||
}
|
||||
if (CreditCard.isValidNumber("36333851788250")) {
|
||||
ok(false, "todo: 14-digit numbers (Diners Club) aren't supported by isValidNumber yet");
|
||||
}
|
||||
if (CreditCard.isValidNumber("3532596776688495393")) {
|
||||
ok(false, "todo: 19-digit numbers (JCB, Discover, Maestro) could have 16-19 digits");
|
||||
}
|
||||
testValid("5038146897157463", true);
|
||||
testValid("4026313395502338", true);
|
||||
testValid("6387060366272981", true);
|
||||
testValid("474915027480942", true);
|
||||
testValid("924894781317325", true);
|
||||
testValid("714816113937185", true);
|
||||
testValid("790466087343106", true);
|
||||
testValid("474320195408363", true);
|
||||
testValid("219211148122351", true);
|
||||
testValid("633038472250799", true);
|
||||
testValid("354236732906484", true);
|
||||
testValid("095347810189325", true);
|
||||
testValid("930771457288760", true);
|
||||
testValid("3091269135815020", true);
|
||||
testValid("5471839082338112", true);
|
||||
testValid("0580828863575793", true);
|
||||
testValid("5015290610002932", true);
|
||||
testValid("9465714503078607", true);
|
||||
testValid("4302068493801686", true);
|
||||
testValid("2721398408985465", true);
|
||||
testValid("6160334316984331", true);
|
||||
testValid("8643619970075142", true);
|
||||
testValid("0218246069710785", true);
|
||||
testValid("0000-0000-0080-4609", true);
|
||||
testValid("0000 0000 0222 331", true);
|
||||
testValid("344060747836806", true);
|
||||
testValid("001064088", true);
|
||||
testValid("4929001587121046", false);
|
||||
testValid("5103059495477876", false);
|
||||
testValid("6011029476355494", false);
|
||||
testValid("3589993783099581", false);
|
||||
testValid("5415425865751455", false);
|
||||
testValid("5038146897157462", false);
|
||||
testValid("4026313395502336", false);
|
||||
testValid("6387060366272980", false);
|
||||
testValid("344060747836804", false);
|
||||
testValid("30190729470496", false);
|
||||
testValid("36333851788255", false);
|
||||
testValid("526931005800649", false);
|
||||
testValid("724952425140686", false);
|
||||
testValid("379761391174135", false);
|
||||
testValid("030551436468583", false);
|
||||
testValid("947377014076746", false);
|
||||
testValid("254848023655752", false);
|
||||
testValid("226871580283345", false);
|
||||
testValid("708025346034339", false);
|
||||
testValid("917585839076788", false);
|
||||
testValid("918632588027666", false);
|
||||
testValid("9946177098017064", false);
|
||||
testValid("4081194386488872", false);
|
||||
testValid("3095975979578034", false);
|
||||
testValid("3662215692222536", false);
|
||||
testValid("6723210018630429", false);
|
||||
testValid("4411962856225025", false);
|
||||
testValid("8276996369036686", false);
|
||||
testValid("4449796938248871", false);
|
||||
testValid("3350852696538147", false);
|
||||
testValid("5011802870046957", false);
|
||||
testValid("0000", false);
|
||||
});
|
||||
|
||||
add_task(function test_formatMaskedNumber() {
|
||||
function testFormat(number) {
|
||||
let format = CreditCard.formatMaskedNumber(number);
|
||||
Assert.equal(format.affix, "****", "Affix should always be four asterisks");
|
||||
Assert.equal(format.label, number.substr(-4),
|
||||
"The label should always be the last four digits of the card number");
|
||||
}
|
||||
testFormat("************0000");
|
||||
testFormat("************1045");
|
||||
testFormat("***********6806");
|
||||
testFormat("**********0495");
|
||||
testFormat("**********8250");
|
||||
});
|
||||
|
||||
add_task(function test_maskNumber() {
|
||||
function testMask(number, expected) {
|
||||
let card = new CreditCard({number});
|
||||
Assert.equal(card.maskedNumber, expected,
|
||||
"Masked number should only show the last four digits");
|
||||
}
|
||||
testMask("0000000000000000", "**** 0000");
|
||||
testMask("4929001587121045", "**** 1045");
|
||||
testMask("5103059495477870", "**** 7870");
|
||||
testMask("6011029476355493", "**** 5493");
|
||||
testMask("3589993783099582", "**** 9582");
|
||||
testMask("5415425865751454", "**** 1454");
|
||||
testMask("344060747836806", "**** 6806");
|
||||
Assert.throws(() => (new CreditCard({number: "1234"})).maskedNumber,
|
||||
/Invalid credit card number/,
|
||||
"Four or less numbers should throw when retrieving the maskedNumber");
|
||||
});
|
||||
|
||||
add_task(function test_longMaskedNumber() {
|
||||
function testMask(number, expected) {
|
||||
let card = new CreditCard({number});
|
||||
Assert.equal(card.longMaskedNumber, expected,
|
||||
"Long masked number should show asterisks for all digits but last four");
|
||||
}
|
||||
testMask("0000000000000000", "************0000");
|
||||
testMask("4929001587121045", "************1045");
|
||||
testMask("5103059495477870", "************7870");
|
||||
testMask("6011029476355493", "************5493");
|
||||
testMask("3589993783099582", "************9582");
|
||||
testMask("5415425865751454", "************1454");
|
||||
testMask("344060747836806", "***********6806");
|
||||
Assert.throws(() => (new CreditCard({number: "1234"})).longMaskedNumber,
|
||||
/Invalid credit card number/,
|
||||
"Four or less numbers should throw when retrieving the maskedNumber");
|
||||
});
|
||||
|
||||
add_task(function test_isValid() {
|
||||
function testValid(number, expirationMonth, expirationYear, shouldPass, message) {
|
||||
let card = new CreditCard({
|
||||
number,
|
||||
expirationMonth,
|
||||
expirationYear,
|
||||
});
|
||||
if (shouldPass) {
|
||||
ok(card.isValid(), message);
|
||||
} else {
|
||||
ok(!card.isValid(), message);
|
||||
}
|
||||
}
|
||||
let year = (new Date()).getFullYear();
|
||||
let month = (new Date()).getMonth() + 1;
|
||||
|
||||
testValid("0000000000000000", month, year + 2, true,
|
||||
"Valid number and future expiry date (two years) should pass");
|
||||
testValid("0000000000000000", month + 2, year, true,
|
||||
"Valid number and future expiry date (two months) should pass");
|
||||
testValid("0000000000000000", month, year, true,
|
||||
"Valid number and expiry date equal to this month should pass");
|
||||
testValid("0000000000000000", month - 1, year, false,
|
||||
"Valid number but overdue expiry date should fail");
|
||||
testValid("0000000000000000", month, year - 1, false,
|
||||
"Valid number but overdue expiry date (by a year) should fail");
|
||||
testValid("0000000000000001", month, year + 2, false,
|
||||
"Invalid number but future expiry date should fail");
|
||||
});
|
||||
|
||||
add_task(function test_normalize() {
|
||||
Assert.equal((new CreditCard({number: "0000 0000 0000 0000"})).number, "0000000000000000",
|
||||
"Spaces should be removed from card number after it is normalized");
|
||||
Assert.equal((new CreditCard({number: "0000 0000\t 0000\t0000"})).number, "0000000000000000",
|
||||
"Spaces should be removed from card number after it is normalized");
|
||||
Assert.equal((new CreditCard({number: "0000-0000-0000-0000"})).number, "0000000000000000",
|
||||
"Hyphens should be removed from card number after it is normalized");
|
||||
Assert.equal((new CreditCard({number: "0000-0000 0000-0000"})).number, "0000000000000000",
|
||||
"Spaces and hyphens should be removed from card number after it is normalized");
|
||||
Assert.equal((new CreditCard({number: "0000000000000000"})).number, "0000000000000000",
|
||||
"Normalized numbers should not get changed");
|
||||
Assert.equal((new CreditCard({number: "0000"})).number, null,
|
||||
"Card numbers that are too short get set to null");
|
||||
|
||||
let card = new CreditCard({number: "0000000000000000"});
|
||||
card.expirationYear = "22";
|
||||
card.expirationMonth = "11";
|
||||
Assert.equal(card.expirationYear, 2022, "Years less than four digits are in the third millenium");
|
||||
card.expirationYear = "-200";
|
||||
ok(isNaN(card.expirationYear), "Negative years are blocked");
|
||||
card.expirationYear = "1998";
|
||||
Assert.equal(card.expirationYear, 1998, "Years with four digits are not changed");
|
||||
card.expirationYear = "test";
|
||||
ok(isNaN(card.expirationYear), "non-number years are returned as NaN");
|
||||
card.expirationMonth = "02";
|
||||
Assert.equal(card.expirationMonth, 2, "Zero-leading months are converted properly (not octal)");
|
||||
card.expirationMonth = "test";
|
||||
ok(isNaN(card.expirationMonth), "non-number months are returned as NaN");
|
||||
card.expirationMonth = "12";
|
||||
Assert.equal(card.expirationMonth, 12, "Months formatted correctly are unchanged");
|
||||
card.expirationMonth = "13";
|
||||
ok(isNaN(card.expirationMonth), "Months above 12 are blocked");
|
||||
card.expirationMonth = "7";
|
||||
Assert.equal(card.expirationMonth, 7, "Changing back to a valid number passes");
|
||||
card.expirationMonth = "0";
|
||||
ok(isNaN(card.expirationMonth), "Months below 1 are blocked");
|
||||
|
||||
card.expirationMonth = card.expirationYear = undefined;
|
||||
card.expirationString = "2022/01";
|
||||
Assert.equal(card.expirationMonth, 1, "Month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2022, "Year should be parsed correctly");
|
||||
card.expirationString = "2023-02";
|
||||
Assert.equal(card.expirationMonth, 2, "Month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2023, "Year should be parsed correctly");
|
||||
card.expirationString = "03-2024";
|
||||
Assert.equal(card.expirationMonth, 3, "Month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2024, "Year should be parsed correctly");
|
||||
card.expirationString = "04/2025";
|
||||
Assert.equal(card.expirationMonth, 4, "Month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2025, "Year should be parsed correctly");
|
||||
card.expirationString = "05/26";
|
||||
Assert.equal(card.expirationMonth, 5, "Month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2026, "Year should be parsed correctly");
|
||||
card.expirationString = "27-6";
|
||||
Assert.equal(card.expirationMonth, 6, "Month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2027, "Year should be parsed correctly");
|
||||
card.expirationString = "07/11";
|
||||
Assert.equal(card.expirationMonth, 7, "Ambiguous month should be parsed correctly");
|
||||
Assert.equal(card.expirationYear, 2011, "Ambiguous year should be parsed correctly");
|
||||
|
||||
card = new CreditCard({
|
||||
number: "0000000000000000",
|
||||
expirationMonth: "02",
|
||||
expirationYear: "2112",
|
||||
expirationString: "06-2066",
|
||||
});
|
||||
Assert.equal(card.expirationMonth, 2, "expirationString is takes lower precendence than explicit month");
|
||||
Assert.equal(card.expirationYear, 2112, "expirationString is takes lower precendence than explicit year");
|
||||
});
|
||||
|
||||
add_task(async function test_label() {
|
||||
let testCases = [{
|
||||
number: "0000000000000000",
|
||||
name: "Rudy Badoody",
|
||||
expectedLabel: "0000000000000000, Rudy Badoody",
|
||||
expectedMaskedLabel: "**** 0000, Rudy Badoody",
|
||||
}, {
|
||||
number: "3589993783099582",
|
||||
name: "Jimmy Babimmy",
|
||||
expectedLabel: "3589993783099582, Jimmy Babimmy",
|
||||
expectedMaskedLabel: "**** 9582, Jimmy Babimmy",
|
||||
}, {
|
||||
number: "************9582",
|
||||
name: "Jimmy Babimmy",
|
||||
expectedLabel: "**** 9582, Jimmy Babimmy",
|
||||
expectedMaskedLabel: "**** 9582, Jimmy Babimmy",
|
||||
}, {
|
||||
name: "Ricky Bobby",
|
||||
expectedLabel: "Ricky Bobby",
|
||||
expectedMaskedLabel: "Ricky Bobby",
|
||||
}];
|
||||
|
||||
for (let testCase of testCases) {
|
||||
let {number, name} = testCase;
|
||||
let card = new CreditCard({number, name});
|
||||
|
||||
Assert.equal(await card.getLabel({showNumbers: true}), testCase.expectedLabel,
|
||||
"The expectedLabel should be shown when showNumbers is true");
|
||||
Assert.equal(await card.getLabel({showNumbers: false}), testCase.expectedMaskedLabel,
|
||||
"The expectedMaskedLabel should be shown when showNumbers is false");
|
||||
}
|
||||
});
|
@ -13,6 +13,7 @@ skip-if = toolkit == 'android'
|
||||
[test_client_id.js]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_Color.js]
|
||||
[test_CreditCard.js]
|
||||
[test_DeferredTask.js]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_FileUtils.js]
|
||||
|
Loading…
Reference in New Issue
Block a user