Bug 1463608 - Add test helpers for payment address and cards. r=jaws draft
authorSam Foster <sfoster@mozilla.com>
Mon, 04 Jun 2018 09:36:21 -0700
changeset 810411 be32c684dba62d051bb119a530437830e41bb128
parent 810379 6e8e861540e6d8c85c73ab7b2afa1f027fb3750c
child 810412 6e8380826bcac87bc408ff75a6a398b5c4f6cafa
push id113982
push userbmo:sfoster@mozilla.com
push dateMon, 25 Jun 2018 20:24:05 +0000
reviewersjaws
bugs1463608
milestone63.0a1
Bug 1463608 - Add test helpers for payment address and cards. r=jaws * addSampleAddressesAndBasicCard now accepts arrays of addresses and card, but with no args defaults to its original behavior * addAddressRecord and addCardRecord helpers * New helpers for each step in the sequence of clicking through to add a new address MozReview-Commit-ID: Dto5MSekJTV
browser/components/payments/res/containers/address-form.js
browser/components/payments/res/containers/basic-card-form.js
browser/components/payments/test/PaymentTestUtils.jsm
browser/components/payments/test/browser/browser_address_edit.js
browser/components/payments/test/browser/head.js
--- a/browser/components/payments/res/containers/address-form.js
+++ b/browser/components/payments/res/containers/address-form.js
@@ -30,16 +30,17 @@ export default class AddressForm extends
     this.backButton.className = "back-button";
     this.backButton.addEventListener("click", this);
 
     this.saveButton = document.createElement("button");
     this.saveButton.className = "save-button primary";
     this.saveButton.addEventListener("click", this);
 
     this.persistCheckbox = new LabelledCheckbox();
+    this.persistCheckbox.className = "persist-checkbox";
 
     this._errorFieldMap = {
       addressLine: "#street-address-container",
       city: "#address-level2-container",
       country: "#country-container",
       organization: "#organization-container",
       phone: "#tel-container",
       postalCode: "#postal-code-container",
--- a/browser/components/payments/res/containers/basic-card-form.js
+++ b/browser/components/payments/res/containers/basic-card-form.js
@@ -40,16 +40,17 @@ export default class BasicCardForm exten
     this.backButton.className = "back-button";
     this.backButton.addEventListener("click", this);
 
     this.saveButton = document.createElement("button");
     this.saveButton.className = "save-button primary";
     this.saveButton.addEventListener("click", this);
 
     this.persistCheckbox = new LabelledCheckbox();
+    this.persistCheckbox.className = "persist-checkbox";
 
     // The markup is shared with form autofill preferences.
     let url = "formautofill/editCreditCard.xhtml";
     this.promiseReady = this._fetchMarkup(url).then(doc => {
       this.form = doc.getElementById("form");
       return this.form;
     });
   }
--- a/browser/components/payments/test/PaymentTestUtils.jsm
+++ b/browser/components/payments/test/PaymentTestUtils.jsm
@@ -177,16 +177,21 @@ var PaymentTestUtils = {
     waitForState: async (content, stateCheckFn, msg) => {
       const {
         ContentTaskUtils,
       } = ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm", {});
       let {requestStore} = Cu.waiveXrays(content.document.querySelector("payment-dialog"));
       await ContentTaskUtils.waitForCondition(() => stateCheckFn(requestStore.getState()), msg);
       return requestStore.getState();
     },
+
+    getCurrentState: async (content) => {
+      let {requestStore} = Cu.waiveXrays(content.document.querySelector("payment-dialog"));
+      return requestStore.getState();
+    },
   },
 
   /**
    * Common PaymentMethodData for testing
    */
   MethodData: {
     basicCard: {
       supportedMethods: "basic-card",
@@ -417,10 +422,22 @@ var PaymentTestUtils = {
 
   BasicCards: {
     JohnDoe: {
       "cc-exp-month": 1,
       "cc-exp-year": (new Date()).getFullYear() + 9,
       "cc-name": "John Doe",
       "cc-number": "4111111111111111",
     },
+    JaneMasterCard: {
+      "cc-exp-month": 12,
+      "cc-exp-year": (new Date()).getFullYear() + 9,
+      "cc-name": "Jane McMaster-Card",
+      "cc-number": "5555555555554444",
+    },
+    Temp: {
+      "cc-exp-month": 12,
+      "cc-exp-year": (new Date()).getFullYear() + 9,
+      "cc-name": "Temp Name",
+      "cc-number": "5105105105105100",
+    },
   },
 };
--- a/browser/components/payments/test/browser/browser_address_edit.js
+++ b/browser/components/payments/test/browser/browser_address_edit.js
@@ -1,99 +1,96 @@
 /* eslint-disable no-shadow */
 
 "use strict";
 
 async function setup() {
   await setupFormAutofillStorage();
-  // adds 2 addresses and one card
-  await addSampleAddressesAndBasicCard();
+  await cleanupFormAutofillStorage();
+  // add an address and card to avoid the FTU sequence
+  let prefilledGuids = await addSampleAddressesAndBasicCard(
+    [PTU.Addresses.TimBL], [PTU.BasicCards.JohnDoe]);
+
+  return prefilledGuids;
 }
 
+/*
+ * Test that we can correctly add an address and elect for it to be saved or temporary
+ */
 add_task(async function test_add_link() {
+  let prefilledGuids = await setup();
+
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: BLANK_PAGE_URL,
   }, async browser => {
     let {win, frame} =
       await setupPaymentDialog(browser, {
         methodData: [PTU.MethodData.basicCard],
         details: Object.assign({}, PTU.Details.twoShippingOptions, PTU.Details.total2USD),
         options: PTU.Options.requestShippingOption,
         merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
       }
     );
 
-    let shippingAddressChangePromise = ContentTask.spawn(browser, {
-      eventName: "shippingaddresschange",
-    }, PTU.ContentTasks.awaitPaymentRequestEventPromise);
-
-    await spawnPaymentDialogTask(frame, async (address) => {
+    info("setup got prefilledGuids: " + JSON.stringify(prefilledGuids));
+    await spawnPaymentDialogTask(frame, async () => {
       let {
         PaymentTestUtils: PTU,
       } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return Object.keys(state.savedAddresses).length == 0;
-      }, "No saved addresses when starting test");
+      let {tempAddresses, savedAddresses} = await PTU.DialogContentUtils.getCurrentState(content);
+      is(Object.keys(tempAddresses).length, 0, "No temporary addresses at the start of test");
+      is(Object.keys(savedAddresses).length, 1, "1 saved address at the start of test");
+    });
 
-      let addLink = content.document.querySelector("address-picker .add-link");
-      is(addLink.textContent, "Add", "Add link text");
+    let testOptions = [
+      { isTemporary: false, expectPersist: true },
+      { isTemporary: true, expectPersist: false },
+    ];
+    let newAddress = PTU.Addresses.TimBL2;
 
-      addLink.click();
-
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "address-page" && !state["address-page"].guid;
-      }, "Check add page state");
+    for (let options of testOptions) {
+      let shippingAddressChangePromise = ContentTask.spawn(browser, {
+        eventName: "shippingaddresschange",
+      }, PTU.ContentTasks.awaitPaymentRequestEventPromise);
 
-      let title = content.document.querySelector("address-form h2");
-      is(title.textContent, "Add Shipping Address", "Page title should be set");
+      await manuallyAddAddress(frame, newAddress, options);
+      await shippingAddressChangePromise;
+      info("got shippingaddresschange event");
+
+      await spawnPaymentDialogTask(frame, async ({address, isTemporary, prefilledGuids}) => {
+        let {
+          PaymentTestUtils: PTU,
+        } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
-      ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
-      ok(Cu.waiveXrays(persistCheckbox).checked, "persist checkbox should be checked by default");
+        let newAddresses = await PTU.DialogContentUtils.waitForState(content, (state) => {
+          return state.tempAddresses && state.savedAddresses;
+        });
+        let colnName = isTemporary ? "tempAddresses" : "savedAddresses";
+        // remove any pre-filled entries
+        delete newAddresses[colnName][prefilledGuids.address1GUID];
 
-      info("filling fields");
-      for (let [key, val] of Object.entries(address)) {
-        let field = content.document.getElementById(key);
-        if (!field) {
-          ok(false, `${key} field not found`);
+        let addressGUIDs = Object.keys(newAddresses[colnName]);
+        is(addressGUIDs.length, 1, "Check there is one address");
+        let resultAddress = newAddresses[colnName][addressGUIDs[0]];
+        for (let [key, val] of Object.entries(address)) {
+          is(resultAddress[key], val, "Check " + key);
         }
-        field.value = val;
-        ok(!field.disabled, `Field #${key} shouldn't be disabled`);
-      }
-
-      content.document.querySelector("address-form button:last-of-type").click();
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return Object.keys(state.savedAddresses).length == 1;
-      }, "Check address was added");
+      }, {address: newAddress, isTemporary: options.isTemporary, prefilledGuids});
+    }
 
-      let addressGUIDs = Object.keys(state.savedAddresses);
-      is(addressGUIDs.length, 1, "Check there is one address");
-      let savedAddress = state.savedAddresses[addressGUIDs[0]];
-      for (let [key, val] of Object.entries(address)) {
-        is(savedAddress[key], val, "Check " + key);
-      }
-
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "payment-summary";
-      }, "Switched back to payment-summary");
-    }, PTU.Addresses.TimBL);
-
-    await shippingAddressChangePromise;
-    info("got shippingaddresschange event");
-
-    info("clicking cancel");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
-
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
+  await cleanupFormAutofillStorage();
 });
 
 add_task(async function test_edit_link() {
+  let prefilledGuids = await setup();
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: BLANK_PAGE_URL,
   }, async browser => {
     let {win, frame} =
       await setupPaymentDialog(browser, {
         methodData: [PTU.MethodData.basicCard],
         details: Object.assign({}, PTU.Details.twoShippingOptions, PTU.Details.total2USD),
@@ -107,63 +104,64 @@ add_task(async function test_edit_link()
     }, PTU.ContentTasks.awaitPaymentRequestEventPromise);
 
     const EXPECTED_ADDRESS = {
       "given-name": "Jaws",
       "family-name": "swaJ",
       "organization": "aliizoM",
     };
 
-    await spawnPaymentDialogTask(frame, async (address) => {
+    info("setup got prefilledGuids: " + JSON.stringify(prefilledGuids));
+    await spawnPaymentDialogTask(frame, async () => {
       let {
         PaymentTestUtils: PTU,
       } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return Object.keys(state.savedAddresses).length == 1;
-      }, "One saved address when starting test");
+      let {tempAddresses, savedAddresses} = await PTU.DialogContentUtils.getCurrentState(content);
+      is(Object.keys(tempAddresses).length, 0, "No temporary addresses at the start of test");
+      is(Object.keys(savedAddresses).length, 1, "1 saved address at the start of test");
 
       let picker = content.document
                      .querySelector("address-picker[selected-state-key='selectedShippingAddress']");
       Cu.waiveXrays(picker).dropdown.click();
       Cu.waiveXrays(picker).dropdown.popupBox.children[0].click();
 
       let editLink = content.document.querySelector("address-picker .edit-link");
       is(editLink.textContent, "Edit", "Edit link text");
 
       editLink.click();
 
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+      await PTU.DialogContentUtils.waitForState(content, (state) => {
         return state.page.id == "address-page" && !!state["address-page"].guid;
       }, "Check edit page state");
 
       let title = content.document.querySelector("address-form h2");
       is(title.textContent, "Edit Shipping Address", "Page title should be set");
-
-      let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
-      ok(persistCheckbox.hidden, "checkbox should be hidden when editing an address");
+    });
 
-      info("overwriting field values");
-      for (let [key, val] of Object.entries(address)) {
-        let field = content.document.getElementById(key);
-        field.value = val;
-        ok(!field.disabled, `Field #${key} shouldn't be disabled`);
-      }
+    let editOptions = {
+      checkboxSelector: "#address-page .persist-checkbox",
+      isEditing: true,
+      expectPersist: true,
+    };
+    await fillInAddressForm(frame, EXPECTED_ADDRESS, editOptions);
+    await verifyPersistCheckbox(frame, editOptions);
+    await submitAddressForm(frame, EXPECTED_ADDRESS, editOptions);
 
-      content.document.querySelector("address-form button:last-of-type").click();
+    await spawnPaymentDialogTask(frame, async (address) => {
+      let {
+        PaymentTestUtils: PTU,
+      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
         let addresses = Object.entries(state.savedAddresses);
         return addresses.length == 1 &&
                addresses[0][1]["given-name"] == address["given-name"];
       }, "Check address was edited");
 
-      // check nothing went into tempAddresses
-      is(Object.keys(state.tempAddresses).length, 0, "tempAddresses collection was untouched");
-
       let addressGUIDs = Object.keys(state.savedAddresses);
       is(addressGUIDs.length, 1, "Check there is still one address");
       let savedAddress = state.savedAddresses[addressGUIDs[0]];
       for (let [key, val] of Object.entries(address)) {
         is(savedAddress[key], val, "Check updated " + key);
       }
 
       state = await PTU.DialogContentUtils.waitForState(content, (state) => {
@@ -178,16 +176,17 @@ add_task(async function test_edit_link()
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
 
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
   await cleanupFormAutofillStorage();
 });
 
 add_task(async function test_add_payer_contact_name_email_link() {
+  await setup();
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: BLANK_PAGE_URL,
   }, async browser => {
     let {win, frame} =
       await setupPaymentDialog(browser, {
         methodData: [PTU.MethodData.basicCard],
         details: PTU.Details.total60USD,
@@ -197,82 +196,74 @@ add_task(async function test_add_payer_c
     );
 
     const EXPECTED_ADDRESS = {
       "given-name": "Deraj",
       "family-name": "Niew",
       "email": "test@example.com",
     };
 
-    await spawnPaymentDialogTask(frame, async (address) => {
+    const addOptions = {
+      addLinkSelector: "address-picker.payer-related .add-link",
+      checkboxSelector: "#address-page .persist-checkbox",
+      initialPageId: "payment-summary",
+      expectPersist: true,
+    };
+
+    await spawnPaymentDialogTask(frame, async () => {
       let {
         PaymentTestUtils: PTU,
       } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return Object.keys(state.savedAddresses).length == 0;
-      }, "No saved addresses when starting test");
-
-      let addLink = content.document.querySelector("address-picker.payer-related .add-link");
-      is(addLink.textContent, "Add", "Add link text");
+      let {tempAddresses, savedAddresses} = await PTU.DialogContentUtils.getCurrentState(content);
+      is(Object.keys(tempAddresses).length, 0, "No temporary addresses at the start of test");
+      is(Object.keys(savedAddresses).length, 1, "1 saved address at the start of test");
+    });
 
-      addLink.click();
+    await navigateToAddAddressPage(frame, addOptions);
 
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "address-page" && !state["address-page"].guid;
-      }, "Check add page state");
-
+    await spawnPaymentDialogTask(frame, async () => {
       let title = content.document.querySelector("address-form h2");
       is(title.textContent, "Add Payer Contact", "Page title should be set");
 
-      let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
-      ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
-      ok(Cu.waiveXrays(persistCheckbox).checked, "persist checkbox should be checked by default");
-
-      info("filling fields");
-      for (let [key, val] of Object.entries(address)) {
-        let field = content.document.getElementById(key);
-        if (!field) {
-          ok(false, `${key} field not found`);
-        }
-        field.value = val;
-        ok(!field.disabled, `Field #${key} shouldn't be disabled`);
-      }
-
       info("check that non-payer requested fields are hidden");
       for (let selector of ["#organization", "#tel"]) {
         let element = content.document.querySelector(selector);
         ok(content.isHidden(element), selector + " should be hidden");
       }
+    });
 
-      content.document.querySelector("address-form button:last-of-type").click();
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return Object.keys(state.savedAddresses).length == 1;
-      }, "Check address was added");
+    await fillInAddressForm(frame, EXPECTED_ADDRESS, addOptions);
+    await verifyPersistCheckbox(frame, addOptions);
+    await submitAddressForm(frame, EXPECTED_ADDRESS, addOptions);
 
-      let addressGUIDs = Object.keys(state.savedAddresses);
-      is(addressGUIDs.length, 1, "Check there is one addresses");
-      let savedAddress = state.savedAddresses[addressGUIDs[0]];
+    await spawnPaymentDialogTask(frame, async (address) => {
+      let {
+        PaymentTestUtils: PTU,
+      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+      let {savedAddresses} = await PTU.DialogContentUtils.getCurrentState(content);
+
+      let addressGUIDs = Object.keys(savedAddresses);
+      is(addressGUIDs.length, 2, "Check there are now 2 addresses");
+      let savedAddress = savedAddresses[addressGUIDs[1]];
       for (let [key, val] of Object.entries(address)) {
         is(savedAddress[key], val, "Check " + key);
       }
-
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "payment-summary";
-      }, "Switched back to payment-summary");
     }, EXPECTED_ADDRESS);
 
     info("clicking cancel");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
 
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
 });
 
 add_task(async function test_edit_payer_contact_name_email_phone_link() {
+  await setup();
   await BrowserTestUtils.withNewTab({
     gBrowser,
     url: BLANK_PAGE_URL,
   }, async browser => {
     let {win, frame} =
       await setupPaymentDialog(browser, {
         methodData: [PTU.MethodData.basicCard],
         details: PTU.Details.total60USD,
@@ -282,188 +273,168 @@ add_task(async function test_edit_payer_
     );
 
     const EXPECTED_ADDRESS = {
       "given-name": "Deraj",
       "family-name": "Niew",
       "email": "test@example.com",
       "tel": "+15555551212",
     };
+    const editOptions = {
+      checkboxSelector: "#address-page .persist-checkbox",
+      expectPersist: true,
+      isEditing: true,
+    };
 
     await spawnPaymentDialogTask(frame, async (address) => {
       let {
         PaymentTestUtils: PTU,
       } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+      await PTU.DialogContentUtils.waitForState(content, (state) => {
         return Object.keys(state.savedAddresses).length == 1;
       }, "One saved addresses when starting test");
 
       let editLink =
         content.document.querySelector("address-picker.payer-related .edit-link");
       is(editLink.textContent, "Edit", "Edit link text");
 
       editLink.click();
 
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+      await PTU.DialogContentUtils.waitForState(content, (state) => {
         return state.page.id == "address-page" && !!state["address-page"].guid;
       }, "Check edit page state");
 
       let title = content.document.querySelector("address-form h2");
       is(title.textContent, "Edit Payer Contact", "Page title should be set");
 
-      let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
-      ok(persistCheckbox.hidden, "checkbox should be hidden when editing an address");
-
-      info("overwriting field values");
-      for (let [key, val] of Object.entries(address)) {
-        let field = content.document.getElementById(key);
-        field.value = val + "1";
-        ok(!field.disabled, `Field #${key} shouldn't be disabled`);
-      }
-
       info("check that non-payer requested fields are hidden");
       let formElements =
         content.document.querySelectorAll("address-form :-moz-any(input, select, textarea");
       let allowedFields = ["given-name", "additional-name", "family-name", "email", "tel"];
       for (let element of formElements) {
         let shouldBeVisible = allowedFields.includes(element.id);
         if (shouldBeVisible) {
           ok(content.isVisible(element), element.id + " should be visible");
         } else {
           ok(content.isHidden(element), element.id + " should be hidden");
         }
       }
 
-      content.document.querySelector("address-form button:last-of-type").click();
+      info("overwriting field values");
+      for (let [key, val] of Object.entries(address)) {
+        let field = content.document.getElementById(key);
+        field.value = val + "1";
+        ok(!field.disabled, `Field #${key} shouldn't be disabled`);
+      }
+    }, EXPECTED_ADDRESS);
 
-      state = await PTU.DialogContentUtils.waitForState(content, (state) => {
+    await verifyPersistCheckbox(frame, editOptions);
+    await submitAddressForm(frame, EXPECTED_ADDRESS, editOptions);
+
+    await spawnPaymentDialogTask(frame, async (address) => {
+      let {
+        PaymentTestUtils: PTU,
+      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
         let addresses = Object.entries(state.savedAddresses);
         return addresses.length == 1 &&
                addresses[0][1]["given-name"] == address["given-name"] + "1";
       }, "Check address was edited");
 
       let addressGUIDs = Object.keys(state.savedAddresses);
       is(addressGUIDs.length, 1, "Check there is still one address");
       let savedAddress = state.savedAddresses[addressGUIDs[0]];
       for (let [key, val] of Object.entries(address)) {
         is(savedAddress[key], val + "1", "Check updated " + key);
       }
-
-      await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "payment-summary";
-      }, "Switched back to payment-summary");
     }, EXPECTED_ADDRESS);
 
     info("clicking cancel");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.manuallyClickCancel);
 
     await BrowserTestUtils.waitForCondition(() => win.closed, "dialog should be closed");
   });
 });
 
+/*
+ * Test that we can correctly add an address from a private window
+ */
 add_task(async function test_private_persist_addresses() {
-  formAutofillStorage.addresses.removeAll();
   await setup();
 
-  is((await formAutofillStorage.addresses.getAll()).length, 2,
-     "Setup results in 2 stored addresses at start of test");
+  is((await formAutofillStorage.addresses.getAll()).length, 1,
+     "Setup results in 1 stored address at start of test");
 
   await withNewTabInPrivateWindow({
     url: BLANK_PAGE_URL,
   }, async browser => {
     info("in new tab w. private window");
     let {frame} =
       // setupPaymentDialog from a private window.
       await setupPaymentDialog(browser, {
         methodData: [PTU.MethodData.basicCard],
         details: Object.assign({}, PTU.Details.twoShippingOptions, PTU.Details.total2USD),
         options: PTU.Options.requestShippingOption,
         merchantTaskFn: PTU.ContentTasks.createAndShowRequest,
       }
     );
     info("/setupPaymentDialog");
 
-    await spawnPaymentDialogTask(frame, async () => {
-      let {
-        PaymentTestUtils: PTU,
-      } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
-
-      info("adding link");
-      // click through to add/edit address page
-      let addLink = content.document.querySelector("address-picker a.add-link");
-      addLink.click();
-      info("link clicked");
-
-      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "address-page" && !state.page.guid;
-      }, "Check add page state");
-
-      info("on address-page and state.isPrivate: " + state.isPrivate);
-      ok(state.isPrivate,
-         "isPrivate flag is set when paymentrequest is shown in a private session");
-      ok(typeof state.isPrivate,
-         "isPrivate is a flag");
-    });
-
-    info("wait for initialAddresses");
-    let initialAddresses =
-      await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.getShippingAddresses);
-    is(initialAddresses.options.length, 2, "Got expected number of pre-filled shipping addresses");
-
     // listen for shippingaddresschange event in merchant (private) window
     info("listen for shippingaddresschange");
     let shippingAddressChangePromise = ContentTask.spawn(browser, {
       eventName: "shippingaddresschange",
     }, PTU.ContentTasks.awaitPaymentRequestEventPromise);
 
-    // add an address
-    // (return to summary view)
-    info("add an address");
+    let addressToAdd = PTU.Addresses.Temp;
+    const addOptions = {
+      checkboxSelector: "#address-page .persist-checkbox",
+      expectPersist: false,
+      isPrivate: true,
+    };
+
+    await navigateToAddAddressPage(frame);
     await spawnPaymentDialogTask(frame, async () => {
       let {
         PaymentTestUtils: PTU,
       } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
-      let persistCheckbox = content.document.querySelector("address-form labelled-checkbox");
-      ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
-      ok(!Cu.waiveXrays(persistCheckbox).checked,
-         "persist checkbox should be unchecked by default");
+      let state = await PTU.DialogContentUtils.getCurrentState(content);
+      info("on address-page and state.isPrivate: " + state.isPrivate);
+      ok(state.isPrivate,
+         "isPrivate flag is set when paymentrequest is shown in a private session");
+    });
 
-      info("add the temp address");
-      let addressToAdd = PTU.Addresses.Temp;
-      for (let [key, val] of Object.entries(addressToAdd)) {
-        let field = content.document.getElementById(key);
-        field.value = val;
-      }
-      content.document.querySelector("address-form button:last-of-type").click();
+    info("wait for initialAddresses");
+    let initialAddresses =
+      await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.getShippingAddresses);
+    is(initialAddresses.options.length, 1,
+       "Got expected number of pre-filled shipping addresses");
 
-      info("wait until we return to the summary page");
-      let state = await PTU.DialogContentUtils.waitForState(content, (state) => {
-        return state.page.id == "payment-summary";
-      }, "Return to summary page");
-
-      is(Object.keys(state.tempAddresses).length, 1, "Address added to temporary collection");
-    });
+    await fillInAddressForm(frame, addressToAdd, addOptions);
+    await verifyPersistCheckbox(frame, addOptions);
+    await submitAddressForm(frame, addressToAdd, addOptions);
 
     await shippingAddressChangePromise;
     info("got shippingaddresschange event");
 
     // verify address picker has more addresses and new selected
     info("check shipping addresses");
     let addresses =
       await spawnPaymentDialogTask(frame, PTU.DialogContentTasks.getShippingAddresses);
-
-    is(addresses.options.length, 3, "Got expected number of shipping addresses");
+    is(addresses.options.length, 2, "Got expected number of shipping addresses");
 
     info("clicking pay");
     spawnPaymentDialogTask(frame, PTU.DialogContentTasks.completePayment);
 
     // Add a handler to complete the payment above.
     info("acknowledging the completion from the merchant page");
     let result = await ContentTask.spawn(browser, {}, PTU.ContentTasks.addCompletionHandler);
     is(result.response.methodName, "basic-card", "Check methodName");
+    info("response: " + JSON.stringify(result.response));
   });
   // verify formautofill store doesnt have the new temp addresses
-  is((await formAutofillStorage.addresses.getAll()).length, 2,
-     "Original 2 stored addresses at end of test");
+  is((await formAutofillStorage.addresses.getAll()).length, 1,
+     "Original 1 stored address at end of test");
 });
 
--- a/browser/components/payments/test/browser/head.js
+++ b/browser/components/payments/test/browser/head.js
@@ -135,37 +135,58 @@ async function withNewTabInPrivateWindow
  * @returns {Promise}
  */
 function spawnTaskInNewDialog(requestId, contentTaskFn, args = null) {
   return withNewDialogFrame(requestId, async function spawnTaskInNewDialog_tabTask(reqFrame) {
     await spawnPaymentDialogTask(reqFrame, contentTaskFn, args);
   });
 }
 
-async function addSampleAddressesAndBasicCard() {
+async function addAddressRecord(address) {
+  let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
+                                          (subject, data) => data == "add");
+  let guid = formAutofillStorage.addresses.add(address);
+  await onChanged;
+  return guid;
+}
+
+async function addCardRecord(card) {
   let onChanged = TestUtils.topicObserved("formautofill-storage-changed",
                                           (subject, data) => data == "add");
-  let address1GUID = formAutofillStorage.addresses.add(PTU.Addresses.TimBL);
+  let guid = formAutofillStorage.creditCards.add(card);
   await onChanged;
-
-  onChanged = TestUtils.topicObserved("formautofill-storage-changed",
-                                      (subject, data) => data == "add");
-  let address2GUID = formAutofillStorage.addresses.add(PTU.Addresses.TimBL2);
-  await onChanged;
+  return guid;
+}
 
-  onChanged = TestUtils.topicObserved("formautofill-storage-changed",
-                                      (subject, data) => data == "add");
-  let card1GUID = formAutofillStorage.creditCards.add(PTU.BasicCards.JohnDoe);
-  await onChanged;
+/**
+ * Add address and creditCard records to the formautofill store
+ *
+ * @param {array=} addresses  - The addresses to add to the formautofill address store
+ * @param {array=} cards - The cards to add to the formautofill creditCards store
+ * @returns {Promise}
+ */
+async function addSampleAddressesAndBasicCard(
+  addresses = [
+    PTU.Addresses.TimBL,
+    PTU.Addresses.TimBL2,
+  ],
+  cards = [
+    PTU.BasicCards.JohnDoe,
+  ]) {
+  let guids = {};
 
-  return {
-    address1GUID,
-    address2GUID,
-    card1GUID,
-  };
+  for (let i = 0; i < addresses.length; i++) {
+    guids[`address${i + 1}GUID`] = await addAddressRecord(addresses[i]);
+  }
+
+  for (let i = 0; i < cards.length; i++) {
+    guids[`card${i + 1}GUID`] = await addCardRecord(cards[i]);
+  }
+
+  return guids;
 }
 
 /**
  * Checks that an address from autofill storage matches a Payment Request PaymentAddress.
  * @param {PaymentAddress} paymentAddress
  * @param {object} storageAddress
  * @param {string} msg to describe the check
  */
@@ -301,8 +322,169 @@ function deepClone(obj) {
   return JSON.parse(JSON.stringify(obj));
 }
 
 async function selectPaymentDialogShippingAddressByCountry(frame, country) {
   await spawnPaymentDialogTask(frame,
                                PTU.DialogContentTasks.selectShippingAddressByCountry,
                                country);
 }
+
+async function navigateToAddAddressPage(frame, aOptions = {
+  addLinkSelector: "address-picker a.add-link",
+  initialPageId: "payment-summary",
+}) {
+  await spawnPaymentDialogTask(frame, async (options) => {
+    let {
+      PaymentTestUtils,
+    } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+    info("navigateToAddAddressPage: check were on the expected page first");
+    await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
+      info("current page state: " + state.page.id + " waiting for: " + options.initialPageId);
+      return state.page.id == options.initialPageId;
+    }, "Check summary page state");
+
+    // click through to add/edit address page
+    info("navigateToAddAddressPage: click the link");
+    let addLink = content.document.querySelector(options.addLinkSelector);
+    addLink.click();
+
+    info("navigateToAddAddressPage: wait for address page");
+    await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
+      return state.page.id == "address-page" && !state.page.guid;
+    }, "Check add page state");
+  }, aOptions);
+}
+
+async function fillInAddressForm(frame, aAddress, aOptions = {}) {
+  await spawnPaymentDialogTask(frame, async (args) => {
+    let {address, options = {}} = args;
+
+    // fill the form
+    info("manuallyAddAddress: fill the form with address: " + JSON.stringify(address));
+    for (let [key, val] of Object.entries(address)) {
+      let field = content.document.getElementById(key);
+      if (!field) {
+        ok(false, `${key} field not found`);
+      }
+      field.value = val;
+    }
+    let persistCheckbox = Cu.waiveXrays(
+        content.document.querySelector(options.checkboxSelector));
+    // only touch the checked state if explicitly told to in the options
+    if (options.hasOwnProperty("isTemporary")) {
+      info(`fillInAddressForm: toggling persistCheckbox as isTemporary is: ${options.isTemporary}`);
+      persistCheckbox.checked = !options.isTemporary;
+    }
+    info(`fillInAddressForm, persistCheckbox.checked: ${persistCheckbox.checked}`);
+  }, {address: aAddress, options: aOptions});
+}
+
+async function verifyPersistCheckbox(frame, aOptions = {}) {
+  await spawnPaymentDialogTask(frame, async (args) => {
+    let {options = {}} = args;
+    // ensure card/address is persisted or not based on the temporary option given
+    info("verifyPersistCheckbox");
+    let persistCheckbox = Cu.waiveXrays(
+        content.document.querySelector(options.checkboxSelector));
+
+    if (options.isEditing) {
+      ok(persistCheckbox.hidden, "checkbox should be hidden when editing an existing address");
+    } else {
+      ok(!persistCheckbox.hidden, "checkbox should be visible when adding a new address");
+      is(persistCheckbox.checked, options.expectPersist,
+         "persist checkbox is in the expected state");
+    }
+  }, {options: aOptions});
+}
+
+async function submitAddressForm(frame, aAddress, aOptions = {}) {
+  await spawnPaymentDialogTask(frame, async (args) => {
+    let {options = {}} = args;
+    let {
+      PaymentTestUtils,
+    } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+    let oldAddresses = await PaymentTestUtils.DialogContentUtils.getCurrentState(content);
+
+    // submit the form to return to summary page
+    content.document.querySelector("address-form button:last-of-type").click();
+
+    let currState = await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
+      return state.page.id == "payment-summary";
+    }, "Switched back to payment-summary");
+
+    let savedCount = Object.keys(currState.savedAddresses).length;
+    let tempCount = Object.keys(currState.tempAddresses).length;
+    let oldSavedCount = Object.keys(oldAddresses.savedAddresses).length;
+    let oldTempCount = Object.keys(oldAddresses.tempAddresses).length;
+
+    if (options.isEditing) {
+      is(tempCount, oldTempCount, "tempAddresses count didn't change");
+      is(savedCount, oldSavedCount, "savedAddresses count didn't change");
+    } else if (options.expectPersist) {
+      is(tempCount, oldTempCount, "tempAddresses count didn't change");
+      is(savedCount, oldSavedCount + 1, "Entry added to savedAddresses");
+    } else {
+      is(tempCount, oldTempCount + 1, "Entry added to tempAddresses");
+      is(savedCount, oldSavedCount, "savedAddresses count didn't change");
+    }
+  }, {address: aAddress, options: aOptions});
+}
+
+async function manuallyAddAddress(frame, aAddress, aOptions = {}) {
+  let options = Object.assign({
+    expectPersist: true,
+    isEditing: false,
+  }, aOptions, {
+    checkboxSelector: "#address-page .persist-checkbox",
+  });
+  await navigateToAddAddressPage(frame);
+  await fillInAddressForm(frame, aAddress, options);
+  await verifyPersistCheckbox(frame, options);
+  await submitAddressForm(frame, aAddress, options);
+}
+
+async function navigateToAddCardPage(frame, aOptions = {
+  addLinkSelector: "payment-method-picker .add-link",
+}) {
+  await spawnPaymentDialogTask(frame, async (options) => {
+    let {
+      PaymentTestUtils,
+    } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
+
+    // check were on the summary page first
+    await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
+      return !state.page.id || (state.page.id == "payment-summary");
+    }, "Check summary page state");
+
+    // click through to add/edit card page
+    let addLink = content.document.querySelector(options.addLinkSelector);
+    addLink.click();
+
+    // wait for card page
+    await PaymentTestUtils.DialogContentUtils.waitForState(content, (state) => {
+      return state.page.id == "basic-card-page" && !state["basic-card-page"].guid;
+    }, "Check add page state");
+  }, aOptions);
+}
+
+async function fillInCardForm(frame, aCard, aOptions = {}) {
+  await spawnPaymentDialogTask(frame, async (args) => {
+    let {card, options = {}} = args;
+
+    // fill the form
+    info("fillInCardForm: fill the form with card: " + JSON.stringify(card));
+    for (let [key, val] of Object.entries(card)) {
+      let field = content.document.getElementById(key);
+      if (!field) {
+        ok(false, `${key} field not found`);
+      }
+      field.value = val;
+    }
+    let persistCheckbox = content.document.querySelector(options.checkboxSelector);
+    // only touch the checked state if explicitly told to in the options
+    if (options.hasOwnProperty("isTemporary")) {
+      Cu.waiveXrays(persistCheckbox).checked = !options.isTemporary;
+    }
+  }, {card: aCard, options: aOptions});
+}