Commit f5f3467e authored by Matthew Noorenberghe's avatar Matthew Noorenberghe
Browse files

Bug 1476204 - Check Luhn algorithm in the basic-card-form and in storage and...

Bug 1476204 - Check Luhn algorithm in the basic-card-form and in storage and disable save button when invalid. r=jaws

* Provide an cc-exp-year option to match cc-exp-month
* Make cc-number and cc-name required in the basic-card-form
* Disable the basic-card-page save button when the form is invalid.

MozReview-Commit-ID: LjzsnAKJp6R

--HG--
extra : rebase_source : 467fa09ea07c0234e1839b6dfd7e53375c118104
parent 286bd792
Loading
Loading
Loading
Loading
+29 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@ 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";
@@ -54,6 +56,8 @@ 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;
    });
  }
@@ -163,6 +167,8 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
    } else if (!editing) {
      billingAddressSelect.value = Object.keys(addresses)[0];
    }

    this.updateSaveButtonState();
  }

  handleEvent(event) {
@@ -171,6 +177,14 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
        this.onClick(event);
        break;
      }
      case "input": {
        this.onInput(event);
        break;
      }
      case "invalid": {
        this.onInvalid(event);
        break;
      }
    }
  }

@@ -247,7 +261,9 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
        break;
      }
      case this.saveButton: {
        if (this.form.checkValidity()) {
          this.saveRecord();
        }
        break;
      }
      default: {
@@ -256,6 +272,18 @@ export default class BasicCardForm extends PaymentStateSubscriberMixin(PaymentRe
    }
  }

  onInput(event) {
    this.updateSaveButtonState();
  }

  onInvalid(event) {
    this.saveButton.disabled = true;
  }

  updateSaveButtonState() {
    this.saveButton.disabled = !this.form.checkValidity();
  }

  saveRecord() {
    let record = this.formHandler.buildFormObject();
    let currentState = this.requestStore.getState();
+4 −0
Original line number Diff line number Diff line
@@ -78,6 +78,10 @@ payment-dialog > header {
  display: none;
}

.page-error {
  color: #D70022;
}

.page > footer {
  align-items: center;
  background-color: #eaeaee;
+1 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ var PaymentDialogUtils = {
    return `${address.name} (${address.guid})`;
  },
  isCCNumber(str) {
    return str.length > 0;
    return !!str.replace(/[-\s]/g, "").match(/^\d{9,}$/);
  },
  DEFAULT_REGION: "US",
  supportedCountries: ["US", "CA"],
+1 −0
Original line number Diff line number Diff line
@@ -172,6 +172,7 @@ 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();
    },

+63 −54
Original line number Diff line number Diff line
@@ -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,18 +152,19 @@ 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 sumamry page");
      }, "Check we are back on the summary page");
    });


    await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.setSecurityCode, {
      securityCode: "123",
    });
@@ -370,7 +371,7 @@ add_task(async function test_edit_link() {
      ok(!field.disabled, `Field #${key} shouldn't be disabled`);
    }

    content.document.querySelector("address-form button:last-of-type").click();
    content.document.querySelector("address-form button.save-button").click();
    state = await PTU.DialogContentUtils.waitForState(content, (state) => {
      return state.page.id == "basic-card-page" && state["basic-card-page"].guid &&
             Object.keys(state.savedAddresses).length == 1;
@@ -379,7 +380,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:last-of-type").click();
    content.document.querySelector("basic-card-form button.save-button").click();

    state = await PTU.DialogContentUtils.waitForState(content, (state) => {
      let cards = Object.entries(state.savedBasicCards);
@@ -403,12 +404,19 @@ 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 = {
  let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});

  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,
  };
  let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
  await spawnInDialogForMerchantTask(PTU.ContentTasks.createAndShowRequest, async function check() {
      merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
    });

    await spawnPaymentDialogTask(frame, async function check() {
      let {
        PaymentTestUtils: PTU,
      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
@@ -418,24 +426,24 @@ add_task(async function test_private_card_adding() {

      addLink.click();

    let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
      await PTU.DialogContentUtils.waitForState(content, (state) => {
        return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
      },
                                                          "Check add page state");

    let savedCardCount = Object.keys(state.savedBasicCards).length;
    let tempCardCount = Object.keys(state.tempBasicCards).length;
                                                "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;
@@ -460,8 +468,9 @@ add_task(async function test_private_card_adding() {
      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,
    });
    spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
    await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
  });
  await BrowserTestUtils.closeWindow(privateWin);
});
Loading