Bug 1463554 - Fix the tests that are broken due to change in the internal structure of rich-select.r?MattN draft
authorprathiksha <prathikshaprasadsuman@gmail.com>
Wed, 27 Jun 2018 17:32:54 -0700
changeset 818495 82045022d4e13971d85f8c48a539a8e195b5d88c
parent 818494 23b66343c243fd175c4833cd7d772bcc25dac3e5
child 818496 3d0d2e040b734df81c79c5b61cf3df0ac93836f5
push id116274
push userbmo:prathikshaprasadsuman@gmail.com
push dateSat, 14 Jul 2018 06:06:00 +0000
reviewersMattN
bugs1463554
milestone63.0a1
Bug 1463554 - Fix the tests that are broken due to change in the internal structure of rich-select.r?MattN MozReview-Commit-ID: BTHw9JHZoud
browser/components/payments/test/PaymentTestUtils.jsm
browser/components/payments/test/browser/browser_address_edit.js
browser/components/payments/test/browser/head.js
browser/components/payments/test/mochitest/test_address_form.html
browser/components/payments/test/mochitest/test_address_picker.html
browser/components/payments/test/mochitest/test_basic_card_form.html
browser/components/payments/test/mochitest/test_payer_address_picker.html
browser/components/payments/test/mochitest/test_payment_method_picker.html
browser/components/payments/test/mochitest/test_rich_select.html
browser/components/payments/test/mochitest/test_shipping_option_picker.html
--- a/browser/components/payments/test/PaymentTestUtils.jsm
+++ b/browser/components/payments/test/PaymentTestUtils.jsm
@@ -79,80 +79,80 @@ var PaymentTestUtils = {
       content.showPromise = rq.show();
 
       handle.destruct();
     },
   },
 
   DialogContentTasks: {
     getShippingOptions: () => {
-      let select = content.document.querySelector("shipping-option-picker > rich-select");
-      let popupBox = Cu.waiveXrays(select).popupBox;
-      let selectedOptionIndex = Array.from(popupBox.children)
-                                     .findIndex(item => item.hasAttribute("selected"));
-      let selectedOption = popupBox.children[selectedOptionIndex];
-      let currencyAmount = selectedOption.querySelector("currency-amount");
+      let picker = content.document.querySelector("shipping-option-picker");
+      let popupBox = Cu.waiveXrays(picker).dropdown.popupBox;
+      let selectedOptionIndex = popupBox.selectedIndex;
+      let selectedOption = Cu.waiveXrays(picker).dropdown.selectedOption;
       return {
         optionCount: popupBox.children.length,
         selectedOptionIndex,
         selectedOptionID: selectedOption.getAttribute("value"),
         selectedOptionLabel: selectedOption.getAttribute("label"),
-        selectedOptionCurrency: currencyAmount.getAttribute("currency"),
-        selectedOptionValue: currencyAmount.getAttribute("value"),
+        selectedOptionCurrency: selectedOption.getAttribute("amount-currency"),
+        selectedOptionValue: selectedOption.getAttribute("amount-value"),
       };
     },
 
     getShippingAddresses: () => {
       let doc = content.document;
       let addressPicker =
         doc.querySelector("address-picker[selected-state-key='selectedShippingAddress']");
-      let select = addressPicker.querySelector("rich-select");
-      let popupBox = Cu.waiveXrays(select).popupBox;
+      let popupBox = Cu.waiveXrays(addressPicker).dropdown.popupBox;
       let options = Array.from(popupBox.children).map(option => {
         return {
-          guid: option.guid,
-          country: option.country,
+          guid: option.getAttribute("guid"),
+          country: option.getAttribute("country"),
           selected: option.selected,
         };
       });
       let selectedOptionIndex = options.findIndex(item => item.selected);
       return {
         selectedOptionIndex,
         options,
       };
     },
 
-    selectShippingAddressByCountry: country => {
+    selectShippingAddressByCountry: (country) => {
       let doc = content.document;
       let addressPicker =
         doc.querySelector("address-picker[selected-state-key='selectedShippingAddress']");
-      let select = addressPicker.querySelector("rich-select");
+      let select = Cu.waiveXrays(addressPicker).dropdown.popupBox;
       let option = select.querySelector(`[country="${country}"]`);
-      select.click();
-      option.click();
+      select.focus();
+      // eslint-disable-next-line no-undef
+      EventUtils.synthesizeKey(option.label, {}, content.window);
     },
 
     selectShippingAddressByGuid: guid => {
       let doc = content.document;
       let addressPicker =
         doc.querySelector("address-picker[selected-state-key='selectedShippingAddress']");
-      let select = addressPicker.querySelector("rich-select");
+      let select = Cu.waiveXrays(addressPicker).dropdown.popupBox;
       let option = select.querySelector(`[guid="${guid}"]`);
-      select.click();
-      option.click();
+      select.focus();
+      // eslint-disable-next-line no-undef
+      EventUtils.synthesizeKey(option.label, {}, content.window);
     },
 
     selectShippingOptionById: value => {
       let doc = content.document;
       let optionPicker =
         doc.querySelector("shipping-option-picker");
-      let select = optionPicker.querySelector("rich-select");
+      let select = Cu.waiveXrays(optionPicker).dropdown.popupBox;
       let option = select.querySelector(`[value="${value}"]`);
-      select.click();
-      option.click();
+      select.focus();
+      // eslint-disable-next-line no-undef
+      EventUtils.synthesizeKey(option.textContent, {}, content.window);
     },
 
     /**
      * Click the cancel button
      *
      * Don't await on this task since the cancel can close the dialog before
      * ContentTask can resolve the promise.
      *
--- a/browser/components/payments/test/browser/browser_address_edit.js
+++ b/browser/components/payments/test/browser/browser_address_edit.js
@@ -116,18 +116,18 @@ add_task(async function test_edit_link()
       } = ChromeUtils.import("resource://testing-common/PaymentTestUtils.jsm", {});
 
       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();
+      Cu.waiveXrays(picker).dropdown.popupBox.focus();
+      EventUtils.synthesizeKey(PTU.Addresses.TimBL["given-name"], {}, content.window);
 
       let editLink = content.document.querySelector("address-picker .edit-link");
       is(editLink.textContent, "Edit", "Edit link text");
 
       editLink.click();
 
       await PTU.DialogContentUtils.waitForState(content, (state) => {
         return state.page.id == "address-page" && !!state["address-page"].guid;
--- a/browser/components/payments/test/browser/head.js
+++ b/browser/components/payments/test/browser/head.js
@@ -259,16 +259,17 @@ async function setupPaymentDialog(browse
   info("dialog ready");
 
   await spawnPaymentDialogTask(frame, () => {
     let elementHeight = (element) =>
       element.getBoundingClientRect().height;
     content.isHidden = (element) => elementHeight(element) == 0;
     content.isVisible = (element) => elementHeight(element) > 0;
   });
+  await injectEventUtilsInContentTask(frame);
   info("helper functions injected into frame");
 
   return {win, requestId, frame};
 }
 
 /**
  * Open a merchant tab with the given merchantTaskFn to create a PaymentRequest
  * and then open the associated PaymentRequest dialog in a new tab and run the
@@ -483,8 +484,45 @@ async function fillInCardForm(frame, aCa
     }
     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});
 }
+
+// The JSDoc validator does not support @returns tags in abstract functions or
+// star functions without return statements.
+/* eslint-disable valid-jsdoc */
+/**
+ * Inject `EventUtils` helpers into ContentTask scope.
+ *
+ * This helper is automatically exposed to mochitest browser tests,
+ * but is missing from content task scope.
+ * You should call this method only once per <browser> tag
+ *
+ * @param {xul:browser} browser
+ *        Reference to the browser in which we load content task
+ */
+/* eslint-enable valid-jsdoc */
+async function injectEventUtilsInContentTask(browser) {
+  await ContentTask.spawn(browser, {}, async function() {
+    if ("EventUtils" in this) {
+      return;
+    }
+
+    const EventUtils = this.EventUtils = {};
+
+    EventUtils.window = {};
+    EventUtils.parent = EventUtils.window;
+    /* eslint-disable camelcase */
+    EventUtils._EU_Ci = Ci;
+    EventUtils._EU_Cc = Cc;
+    /* eslint-enable camelcase */
+    // EventUtils' `sendChar` function relies on the navigator to synthetize events.
+    EventUtils.navigator = content.navigator;
+    EventUtils.KeyboardEvent = content.KeyboardEvent;
+
+    Services.scriptloader.loadSubScript(
+      "chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
+  });
+}
--- a/browser/components/payments/test/mochitest/test_address_form.html
+++ b/browser/components/payments/test/mochitest/test_address_form.html
@@ -154,16 +154,17 @@ add_task(async function test_saveButton(
       },
     },
     guid: undefined,
     messageType: "updateAutofillRecord",
     preserveOldProperties: true,
     record: {
       "given-name": "Jaws",
       "family-name": "Swaj",
+      "additional-name": "",
       "organization": "Allizom",
       "street-address": "404 Internet Super Highway",
       "address-level2": "Firefoxity City",
       "address-level1": "CA",
       "postal-code": "00001",
       "country": "US",
       "email": "test@example.com",
       "tel": "+15555551212",
--- a/browser/components/payments/test/mochitest/test_address_picker.html
+++ b/browser/components/payments/test/mochitest/test_address_picker.html
@@ -64,18 +64,18 @@ add_task(async function test_initialSet(
         "street-address": "P.O. Box 123",
         "tel": "+1 650 555-5555",
       },
     },
   });
   await asyncElementRendered();
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 2, "Check dropdown has both addresses");
-  ok(options[0].textContent.includes("123 Sesame Street"), "Check first address");
-  ok(options[1].textContent.includes("P.O. Box 123"), "Check second address");
+  ok(options[0].textContent.includes("Mr. Foo"), "Check first address");
+  ok(options[1].textContent.includes("Mrs. Bar"), "Check second address");
 });
 
 add_task(async function test_update() {
   picker1.requestStore.setState({
     savedAddresses: {
       "48bnds6854t": {
         // Same GUID, different values to trigger an update
         "address-level1": "MI-edit",
@@ -97,38 +97,48 @@ add_task(async function test_update() {
         "street-address": "P.O. Box 123",
         "tel": "+1 650 555-5555",
       },
     },
   });
   await asyncElementRendered();
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 2, "Check dropdown still has both addresses");
-  ok(options[0].textContent.includes("MI-edit"), "Check updated first address-level1");
-  ok(!options[0].textContent.includes("Some City"), "Check removed first address-level2");
-  ok(options[0].textContent.includes("new-edit"), "Check updated first address");
-
-  ok(options[1].textContent.includes("P.O. Box 123"), "Check second address is the same");
+  ok(options[0].textContent.includes("Mr. Foo-edit"), "Check updated name in first address");
+  ok(!options[0].getAttribute("address-level2"), "Check removed first address-level2");
+  ok(options[1].textContent.includes("Mrs. Bar"), "Check that name is the same in second address");
+  ok(options[1].getAttribute("street-address").includes("P.O. Box 123"),
+     "Check second address is the same");
 });
 
 add_task(async function test_change_selected_address() {
   let options = picker1.dropdown.popupBox.children;
-  let selectedOption = picker1.dropdown.selectedOption;
-  is(selectedOption, null, "Should default to no selected option");
+  is(picker1.dropdown.selectedOption, null, "Should default to no selected option");
   let {selectedShippingAddress} = picker1.requestStore.getState();
   is(selectedShippingAddress, null, "store should have no option selected");
 
-  picker1.dropdown.click();
-  options[1].click();
+  picker1.dropdown.popupBox.focus();
+  synthesizeKey(options[1].getAttribute("name"), {});
   await asyncElementRendered();
 
-  selectedOption = picker1.dropdown.selectedOption;
+  let selectedOption = picker1.dropdown.selectedOption;
   is(selectedOption, options[1], "Selected option should now be the second option");
   selectedShippingAddress = picker1.requestStore.getState().selectedShippingAddress;
-  is(selectedShippingAddress, selectedOption.guid, "store should have second option selected");
+  is(selectedShippingAddress, selectedOption.getAttribute("guid"),
+     "store should have second option selected");
+});
+
+add_task(async function test_streetAddress_combines_street_level2_level1_postalCode_country() {
+  let options = picker1.dropdown.popupBox.children;
+  let richoption1 = picker1.dropdown.querySelector(".rich-select-selected-option");
+  let streetAddress = richoption1.querySelector(".street-address");
+  /* eslint-disable max-len */
+  is(streetAddress.textContent,
+     `${options[1].getAttribute("street-address")} ${options[1].getAttribute("address-level2")} ${options[1].getAttribute("address-level1")} ${options[1].getAttribute("postal-code")} ${options[1].getAttribute("country")}`);
+  /* eslint-enable max-len */
 });
 
 add_task(async function test_delete() {
   picker1.requestStore.setState({
     savedAddresses: {
       // 48bnds6854t was deleted
       "68gjdh354j": {
         "address-level1": "CA",
@@ -140,14 +150,14 @@ add_task(async function test_delete() {
         "street-address": "P.O. Box 123",
         "tel": "+1 650 555-5555",
       },
     },
   });
   await asyncElementRendered();
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 1, "Check dropdown has one remaining address");
-  ok(options[0].textContent.includes("P.O. Box 123"), "Check remaining address");
+  ok(options[0].textContent.includes("Mrs. Bar"), "Check remaining address");
 });
 </script>
 
 </body>
 </html>
--- a/browser/components/payments/test/mochitest/test_basic_card_form.html
+++ b/browser/components/payments/test/mochitest/test_basic_card_form.html
@@ -123,16 +123,17 @@ add_task(async function test_saveButton(
     guid: undefined,
     messageType: "updateAutofillRecord",
     preserveOldProperties: true,
     record: {
       "cc-exp-month": "11",
       "cc-exp-year": year,
       "cc-name": "J. Smith",
       "cc-number": "4111111111111111",
+      "billingAddressGUID": "",
     },
     selectedStateKey: ["selectedPaymentCard"],
     successStateChange: {
       page: {
         id: "payment-summary",
       },
     },
   }, "Check event details for the message to chrome");
--- a/browser/components/payments/test/mochitest/test_payer_address_picker.html
+++ b/browser/components/payments/test/mochitest/test_payer_address_picker.html
@@ -27,23 +27,16 @@ Test the paymentOptions address-picker
 </div>
 <pre id="test">
 </pre>
 <script type="module">
 /** Test the payer requested details functionality **/
 
 import PaymentDialog from "../../res/containers/payment-dialog.js";
 
-function getVisiblePickerOptions(picker) {
-  let select = picker.querySelector(":scope > rich-select");
-  let options = select.querySelectorAll("address-option");
-  let visibleOptions = Array.from(options).filter(isVisible);
-  return visibleOptions;
-}
-
 function isVisible(elem) {
   let result = elem.getBoundingClientRect().height > 0;
   return result;
 }
 
 function setPaymentOptions(requestStore, options) {
   let {request} = requestStore.getState();
   request = Object.assign({}, request, {
@@ -181,29 +174,27 @@ add_task(async function test_visible_fie
 
   requestStore.setState({
     savedAddresses: SAVED_ADDRESSES,
     selectedPayerAddress: "48bnds6854t",
   });
 
   await asyncElementRendered();
 
-  let visibleOptions = getVisiblePickerOptions(elPicker);
-  let visibleOption = visibleOptions[0];
-
+  let closedRichOption = elPicker.dropdown.querySelector(".rich-select-selected-option");
   is(elPicker.dropdown.popupBox.children.length, 2, "Check dropdown has 2 addresses");
-  is(visibleOptions.length, 1, "One option should be visible");
-  is(visibleOption.getAttribute("guid"), "48bnds6854t", "expected option is visible");
+  is(closedRichOption.getAttribute("guid"), "48bnds6854t", "expected option is visible");
 
   for (let fieldName of ["name", "email", "tel"]) {
-    let elem = visibleOption.querySelector(`.${fieldName}`);
+    let elem = closedRichOption.querySelector(`.${fieldName}`);
     ok(elem, `field ${fieldName} exists`);
     ok(isVisible(elem), `field ${fieldName} is visible`);
   }
-  ok(!isVisible(visibleOption.querySelector(".street-address")), "street-address is not visible");
+  ok(!isVisible(closedRichOption.querySelector(".street-address")),
+     "street-address is not visible");
 });
 
 add_task(async function test_selective_fields() {
   await setup();
   let requestStore = elPicker.requestStore;
 
   requestStore.setState({
     savedAddresses: SAVED_ADDRESSES,
@@ -218,20 +209,20 @@ add_task(async function test_selective_f
     {requestPayerName: false, requestPayerEmail: true, requestPayerPhone: true },
     {requestPayerName: true, requestPayerEmail: false, requestPayerPhone: true },
   ];
 
   for (let payerFields of payerFieldVariations) {
     setPaymentOptions(requestStore, payerFields);
     await asyncElementRendered();
 
-    let visibleOption = getVisiblePickerOptions(elPicker)[0];
-    let elName = visibleOption.querySelector(".name");
-    let elEmail = visibleOption.querySelector(".email");
-    let elPhone = visibleOption.querySelector(".tel");
+    let closedRichOption = elPicker.dropdown.querySelector(".rich-select-selected-option");
+    let elName = closedRichOption.querySelector(".name");
+    let elEmail = closedRichOption.querySelector(".email");
+    let elPhone = closedRichOption.querySelector(".tel");
 
     is(isVisible(elName), payerFields.requestPayerName,
        "name field is correctly toggled");
     is(isVisible(elEmail), payerFields.requestPayerEmail,
        "email field is correctly toggled");
     is(isVisible(elPhone), payerFields.requestPayerPhone,
        "tel field is correctly toggled");
   }
@@ -247,42 +238,37 @@ add_task(async function test_filtered_op
 
   requestStore.setState({
     savedAddresses: DUPED_ADDRESSES,
     selectedPayerAddress: "a9e830667189",
   });
 
   await asyncElementRendered();
 
-  let visibleOptions = getVisiblePickerOptions(elPicker);
-  let visibleOption = visibleOptions[0];
-
-  is(elPicker.dropdown.popupBox.children.length, 4, "Check dropdown has 3 addresses");
-  is(visibleOptions.length, 1, "One option should be visible");
-  is(visibleOption.getAttribute("guid"), "a9e830667189", "expected option is visible");
+  let closedRichOption = elPicker.dropdown.querySelector(".rich-select-selected-option");
+  is(elPicker.dropdown.popupBox.children.length, 4, "Check dropdown has 4 addresses");
+  is(closedRichOption.getAttribute("guid"), "a9e830667189", "expected option is visible");
 
   for (let fieldName of ["name", "email"]) {
-    let elem = visibleOption.querySelector(`.${fieldName}`);
+    let elem = closedRichOption.querySelector(`.${fieldName}`);
     ok(elem, `field ${fieldName} exists`);
     ok(isVisible(elem), `field ${fieldName} is visible`);
   }
 
   setPaymentOptions(requestStore, {
     requestPayerPhone: true,
   });
   await asyncElementRendered();
 
   is(elPicker.dropdown.popupBox.children.length, 1, "Check dropdown has 1 addresses");
-  is(visibleOptions.length, 1, "One option should be visible");
 
   setPaymentOptions(requestStore, {});
   await asyncElementRendered();
 
   is(elPicker.dropdown.popupBox.children.length, 4, "Check dropdown has 4 addresses");
-  is(visibleOptions.length, 1, "One option should be visible");
 });
 
 add_task(async function test_no_matches() {
   await setup();
   let requestStore = elPicker.requestStore;
   setPaymentOptions(requestStore, {
     requestPayerPhone: true,
   });
@@ -299,17 +285,17 @@ add_task(async function test_no_matches(
         "name": "Rita Foo",
       },
     },
     selectedPayerAddress: "a9e830667189",
   });
 
   await asyncElementRendered();
 
-  let visibleOptions = getVisiblePickerOptions(elPicker);
+  let closedRichOption = elPicker.dropdown.querySelector(".rich-select-selected-option");
   is(elPicker.dropdown.popupBox.children.length, 0, "Check dropdown is empty");
-  is(visibleOptions.length, 0, "No options should be visible");
+  ok(closedRichOption.localName !== "address-option", "No option is selected and visible");
 });
 
 </script>
 
 </body>
 </html>
--- a/browser/components/payments/test/mochitest/test_payment_method_picker.html
+++ b/browser/components/payments/test/mochitest/test_payment_method_picker.html
@@ -99,46 +99,43 @@ add_task(async function test_update() {
   ok(options[0].textContent.includes("9876"), "Check updated first cc-number");
   ok(options[0].textContent.includes("09"), "Check updated first exp-month");
 
   ok(options[1].textContent.includes("J Smith"), "Check second card is the same");
 });
 
 add_task(async function test_change_selected_card() {
   let options = picker1.dropdown.popupBox.children;
-  let selectedOption = picker1.dropdown.selectedOption;
-  is(selectedOption, null, "Should default to no selected option");
+  is(picker1.dropdown.selectedOption, null, "Should default to no selected option");
   let {
     selectedPaymentCard,
     selectedPaymentCardSecurityCode,
   } = picker1.requestStore.getState();
   is(selectedPaymentCard, null, "store should have no option selected");
   is(selectedPaymentCardSecurityCode, null, "store should have no security code");
 
   await SimpleTest.promiseFocus();
-  let codeFocusPromise = new Promise(resolve => {
-    picker1.securityCodeInput.addEventListener("focus", resolve, {once: true});
-  });
-  picker1.dropdown.click();
-  options[1].click();
+  picker1.dropdown.popupBox.focus();
+  synthesizeKey("************1234", {});
   await asyncElementRendered();
-  await codeFocusPromise;
   ok(true, "Focused the security code field");
   ok(!picker1.open, "Picker should be closed");
 
-  selectedOption = picker1.dropdown.selectedOption;
+  let selectedOption = picker1.dropdown.selectedOption;
   is(selectedOption, options[1], "Selected option should now be the second option");
   selectedPaymentCard = picker1.requestStore.getState().selectedPaymentCard;
-  is(selectedPaymentCard, selectedOption.guid, "store should have second option selected");
+  is(selectedPaymentCard, selectedOption.getAttribute("guid"),
+     "store should have second option selected");
   selectedPaymentCardSecurityCode = picker1.requestStore.getState().selectedPaymentCardSecurityCode;
   is(selectedPaymentCardSecurityCode, null, "store should have empty security code");
 
   let stateChangePromise = promiseStateChange(picker1.requestStore);
 
   // Type in the security code field
+  picker1.securityCodeInput.focus();
   sendString("836");
   sendKey("Tab");
   let state = await stateChangePromise;
   ok(state.selectedPaymentCardSecurityCode, "836", "Check security code in state");
 });
 
 add_task(async function test_delete() {
   picker1.requestStore.setState({
--- a/browser/components/payments/test/mochitest/test_rich_select.html
+++ b/browser/components/payments/test/mochitest/test_rich_select.html
@@ -14,358 +14,128 @@ Test the rich-select component
 
   <link rel="stylesheet" type="text/css" href="../../res/components/rich-select.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/address-option.css"/>
   <link rel="stylesheet" type="text/css" href="../../res/components/basic-card-option.css"/>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
   <p id="display">
-    <rich-select id="select1">
-      <!-- the class="rich-option" is required to be hard-coded due to a bug with the custom
-           elements polyfill causing the address-option constructor to happen too late. -->
-      <address-option id="option1"
-                      class="rich-option"
-                      email="emzembrano92@email.com"
-                      name="Emily Zembrano"
-                      street-address="717 Hyde Street #6"
-                      address-level2="San Francisco"
-                      address-level1="CA"
-                      tel="415 203 0845"
-                      postal-code="94109"
-                      country="USA"></address-option>
-      <address-option id="option2"
-                      class="rich-option"
-                      email="jenz9382@email.com"
-                      name="Jennifer Zembrano"
-                      street-address="42 Fairydust Lane"
-                      address-level2="Lala Land"
-                      address-level1="HI"
-                      tel="415 439 2827"
-                      postal-code="98765"
-                      country="USA"></address-option>
-      <address-option id="option3"
-                      class="rich-option"
-                      email="johnz9382@email.com"
-                      name="John Zembrano"
-                      street-address="42 Fairydust Lane"
-                      address-level2="Lala Land"
-                      missinginformation="true"
-                      address-level1="HI"
-                      tel="415 439 2827"
-                      postal-code="98765"
-                      country="USA"></address-option>
-    </rich-select>
-
-    <rich-select id="select2">
-      <basic-card-option cc-name="Jared Wein"
-                         class="rich-option"
-                         cc-exp="1970-01"
-                         cc-number="************3599"
-                         type="Visa"></basic-card-option>
-      <basic-card-option cc-name="Whimsy Corn"
-                         class="rich-option"
-                         cc-exp="1970-01"
-                         cc-number="*************7667"
-                         type="Mastercard"></basic-card-option>
-      <basic-card-option cc-name="Fire Fox"
-                         class="rich-option"
-                         cc-exp="1970-01"
-                         cc-number="************1054"
-                         type="Discover"></basic-card-option>
-    </rich-select>
   </p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 </pre>
 <script type="module">
 /** Test the rich-select address-option component **/
 
 import AddressOption from "../../res/components/address-option.js";
-import BasicCardOption from "../../res/components/basic-card-option.js";
 import RichSelect from "../../res/components/rich-select.js";
 
-let select1 = document.getElementById("select1");
-let option1 = document.getElementById("option1");
-let option2 = document.getElementById("option2");
-let option3 = document.getElementById("option3");
+let addresses = {
+  "58gjdh354k": {
+    "email": "emzembrano92@email.com",
+    "name": "Emily Zembrano",
+    "street-address": "717 Hyde Street #6",
+    "address-level2": "San Francisco",
+    "address-level1": "CA",
+    "tel": "415 203 0845",
+    "postal-code": "94109",
+    "country": "USA",
+    "guid": "58gjdh354k",
+  },
+  "67gjdh354k": {
+    "email": "jenz9382@email.com",
+    "name": "Jennifer Zembrano",
+    "street-address": "42 Fairydust Lane",
+    "address-level2": "Lala Land",
+    "address-level1": "HI",
+    "tel": "415 439 2827",
+    "postal-code": "98765",
+    "country": "USA",
+    "guid": "67gjdh354k",
+  },
+};
+
+let select1 = new RichSelect();
+for (let address of Object.values(addresses)) {
+  let option = document.createElement("option");
+  option.textContent = address.name + " " + address["street-address"];
+  option.setAttribute("value", address.guid);
+  for (let field of Object.keys(address)) {
+    option.setAttribute(field, address[field]);
+  }
+  select1.popupBox.appendChild(option);
+}
+select1.setAttribute("option-type", "address-option");
+select1.value = "";
+document.getElementById("display").appendChild(select1);
+
+let options = select1.popupBox.children;
+let option1 = options[0];
+let option2 = options[1];
 
 function get_selected_clone() {
-  return select1.querySelector(".rich-select-selected-clone");
+  return select1.querySelector(".rich-select-selected-option");
 }
 
 function is_visible(element, message) {
   ok(!isHidden(element), message);
 }
 
-function is_hidden(element, message) {
-  ok(isHidden(element), message);
-}
-
-function dispatchKeyDown(key, keyCode) {
-  select1.dispatchEvent(new KeyboardEvent("keydown", {key, keyCode}));
-}
-
-add_task(async function test_streetAddress_combines_street_level2_level1_postalCode_country() {
-  ok(option1, "option1 exists");
-  let streetAddress = option1.querySelector(".street-address");
-  /* eslint-disable max-len */
-  is(streetAddress.textContent,
-     `${option1.streetAddress} ${option1.addressLevel2} ${option1.addressLevel1} ${option1.postalCode} ${option1.country}`);
-  /* eslint-enable max-len */
-});
-
-add_task(async function test_no_option_selected() {
+add_task(async function test_closed_state_on_selection() {
   ok(select1, "select1 exists");
-
+  select1.popupBox.focus();
+  synthesizeKey(option1.textContent, {});
   await asyncElementRendered();
-
-  is_hidden(option1, "option 1 should be hidden when popup is not open");
-  is_hidden(option2, "option 2 should be hidden when popup is not open");
-  is_hidden(option3, "option 3 should be hidden when popup is not open");
-  ok(!option1.selected, "option 1 should not be selected");
-  ok(!option1.hasAttribute("selected"), "option 1 should not have selected attribute");
-  let selectedClone = get_selected_clone();
-  is_visible(selectedClone, "The selected clone should be visible at all times");
-  ok(selectedClone.classList.contains("rich-option"), "Default option should be a rich-option");
-  ok(selectedClone.textContent.includes("None selected"), "Check default text");
-});
-
-add_task(async function test_clicking_on_select_shows_all_options() {
-  ok(select1, "select1 exists");
-  ok(!select1.open, "select is not open by default");
-  ok(!option1.selected, "option 1 should not be selected by default");
-
-  select1.click();
-
-  ok(select1.open, "select is open after clicking on it");
-  is(select1.selectedOption, null, "No selected option when open");
-  ok(!option1.selected, "option 1 should not be selected when open");
-  is_visible(option1, "option 1 is visible when select is open");
-  is_visible(option2, "option 2 is visible when select is open");
-  is_visible(option3, "option 3 is visible when select is open");
-
-  option2.click();
-
-  ok(!select1.open, "select is not open after blur");
-  ok(!option1.selected, "option 1 is not selected after click on option 2");
-  ok(option2.selected, "option 2 is selected after clicking on it");
-  is_hidden(option1, "option 1 is hidden when select is closed");
-  is_hidden(option2, "option 2 is hidden when select is closed");
-  is_hidden(option3, "option 3 is hidden when select is closed");
-
-  await asyncElementRendered();
+  ok(option1.selected, "option 1 is now selected");
 
   let selectedClone = get_selected_clone();
   is_visible(selectedClone, "The selected clone should be visible at all times");
-  is(selectedClone.getAttribute("email"), option2.getAttribute("email"),
+  is(selectedClone.getAttribute("email"), option1.getAttribute("email"),
      "The selected clone email should be equivalent to the selected option 2");
-  is(selectedClone.getAttribute("name"), option2.getAttribute("name"),
+  is(selectedClone.getAttribute("name"), option1.getAttribute("name"),
      "The selected clone name should be equivalent to the selected option 2");
 });
 
-add_task(async function test_changing_option_selected_affects_other_options() {
-  ok(option2.selected, "Option 2 should be selected from prior test");
-
-  select1.selectedOption = option1;
-  ok(!option2.selected, "Option 2 should no longer be selected after making option 1 selected");
-  ok(option1.hasAttribute("selected"), "Option 1 should now have selected attribute");
-});
-
-add_task(async function test_up_down_keys_change_selected_item() {
-  let openObserver = new MutationObserver(mutations => {
-    for (let mutation of mutations) {
-      ok(mutation.attributeName != "open", "the select should not open/close during this test");
-    }
-  });
-  openObserver.observe(select1, {attributes: true});
-
-  ok(select1, "select1 exists");
-  ok(option1.selected, "option 1 should be selected by default");
-
-  ok(!select1.open, "select should not be open before focusing");
-  select1.focus();
-  ok(!select1.open, "select should not be open after focusing");
-
-  dispatchKeyDown("ArrowDown", 40);
-  ok(!option1.selected, "option 1 should no longer be selected");
-  ok(option2.selected, "option 2 should now be selected");
-
-  dispatchKeyDown("ArrowDown", 40);
-  ok(!option2.selected, "option 2 should no longer be selected");
-  ok(option3.selected, "option 3 should now be selected");
-
-  dispatchKeyDown("ArrowDown", 40);
-  ok(option3.selected, "option 3 should remain selected");
-  ok(!option1.selected, "option 1 should not be selected");
-
-  dispatchKeyDown("ArrowUp", 38);
-  ok(!option3.selected, "option 3 should no longer be selected");
-  ok(option2.selected, "option 2 should now be selected");
-
-  dispatchKeyDown("ArrowUp", 38);
-  ok(!option2.selected, "option 2 should no longer be selected");
-  ok(option1.selected, "option 1 should now be selected");
-
-  dispatchKeyDown("ArrowUp", 38);
-  ok(option1.selected, "option 1 should remain selected");
-  ok(!option3.selected, "option 3 should not be selected");
-
-  // Wait for any mutation observer notifications to fire before exiting.
-  await Promise.resolve();
+add_task(async function test_multi_select_not_supported_in_dropdown() {
+  ok(option1.selected, "Option 1 should be selected from prior test");
 
-  openObserver.disconnect();
-});
-
-add_task(async function test_open_close_from_keyboard() {
-  select1.focus();
-
-  ok(!select1.open, "select should not be open by default");
-
-  dispatchKeyDown(" ", 32);
-  ok(select1.open, "select should now be open");
-  ok(option1.selected, "option 1 should be selected by default");
-
-  dispatchKeyDown("ArrowDown", 40);
-  ok(!option1.selected, "option 1 should not be selected");
-  ok(option2.selected, "option 2 should now be selected");
-  ok(select1.open, "select should remain open");
-
-  dispatchKeyDown("ArrowUp", 38);
-  ok(option1.selected, "option 1 should now be selected");
-  ok(!option2.selected, "option 2 should not be selected");
-  ok(select1.open, "select should remain open");
-
-  dispatchKeyDown("Enter", 13);
-  ok(option1.selected, "option 1 should now be selected");
-  ok(!select1.open, "select should be closed");
+  select1.popupBox.focus();
+  synthesizeKey(option2.textContent, {});
+  await asyncElementRendered();
 
-  dispatchKeyDown(" ", 32);
-  ok(select1.open, "select should now be open");
-
-  dispatchKeyDown("Escape", 27);
-  ok(!select1.open, "select should be closed");
-});
-
-add_task(async function test_clicking_on_options_maintain_one_item_always_selected() {
-  ok(!select1.open, "select should be closed by default");
-  ok(option1.selected, "option 1 should be selected by default");
-  select1.click();
-  ok(select1.open, "select should now be open");
-
-  option3.click();
-  ok(!select1.open, "select should be closed");
-  ok(!option1.selected, "option 1 should be unselected");
-  ok(option3.selected, "option 3 should be selected");
-
-  select1.click();
-  ok(select1.open, "select should open");
-  ok(!option1.selected, "option 1 should be unselected");
-  ok(option3.selected, "option 3 should be selected");
-
-  option1.click();
-  ok(!select1.open, "select should be closed");
-  ok(option1.selected, "option 1 should be selected");
-  ok(!option3.selected, "option 3 should be unselected");
+  ok(!option1.selected, "Option 1 should no longer be selected after selecting option1");
+  ok(option2.selected, "Option 2 should now have selected property set to true");
 });
 
 add_task(async function test_selected_clone_should_equal_selected_option() {
-  ok(option1.selected, "option 1 should be selected");
-  await asyncElementRendered();
+  ok(option2.selected, "option 2 should be selected");
 
-  let clonedOptions = select1.querySelectorAll(".rich-select-selected-clone");
+  let clonedOptions = select1.querySelectorAll(".rich-select-selected-option");
   is(clonedOptions.length, 1, "there should only be one cloned option");
 
   let clonedOption = clonedOptions[0];
-  for (let attrName of AddressOption.observedAttributes) {
-    if (attrName == "selected") {
-      continue;
-    }
+  for (let attrName of AddressOption.recordAttributes) {
+    is(clonedOption.attributes[attrName] && clonedOption.attributes[attrName].value,
+       option2.attributes[attrName] && option2.attributes[attrName].value,
+       "attributes should have matching value; name=" + attrName);
+  }
+
+  select1.popupBox.focus();
+  synthesizeKey(option1.textContent, {});
+  await asyncElementRendered();
+
+  clonedOptions = select1.querySelectorAll(".rich-select-selected-option");
+  is(clonedOptions.length, 1, "there should only be one cloned option");
+
+  clonedOption = clonedOptions[0];
+  for (let attrName of AddressOption.recordAttributes) {
     is(clonedOption.attributes[attrName] && clonedOption.attributes[attrName].value,
        option1.attributes[attrName] && option1.attributes[attrName].value,
        "attributes should have matching value; name=" + attrName);
   }
-
-  select1.selectedOption = option2;
-  await asyncElementRendered();
-
-  clonedOptions = select1.querySelectorAll(".rich-select-selected-clone");
-  is(clonedOptions.length, 1, "there should only be one cloned option");
-
-  clonedOption = clonedOptions[0];
-  for (let attrName of AddressOption.observedAttributes) {
-    if (attrName == "selected") {
-      continue;
-    }
-    is(clonedOption.attributes[attrName] && clonedOption.attributes[attrName].value,
-       option2.attributes[attrName] && option2.attributes[attrName].value,
-       "attributes should have matching value; name=" + attrName);
-  }
 });
-
-add_task(async function test_basic_card_simple() {
-  let select2 = document.getElementById("select2");
-  ok(select2, "basic card select should exist");
-  let popupBox = select2.querySelector(".rich-select-popup-box");
-  ok(popupBox, "basic card popup box exists");
-
-  is(popupBox.childElementCount, 3, "There should be three children in the popup box");
-
-  select2.selectedOption = popupBox.firstElementChild;
-  await asyncElementRendered();
-
-  let clonedOption = select2.querySelector(".rich-select-selected-clone");
-  let selectedOption = popupBox.firstElementChild;
-  for (let attrName of BasicCardOption.observedAttributes) {
-    if (attrName == "selected") {
-      continue;
-    }
-    is(clonedOption.attributes[attrName] && clonedOption.attributes[attrName].value,
-       selectedOption.attributes[attrName] && selectedOption.attributes[attrName].value,
-       "attributes should have matching value; name=" + attrName);
-  }
-});
-
-add_task(async function test_option_appended_after_creation() {
-  let select2 = document.getElementById("select2");
-  let select3 = new RichSelect();
-  select3.id = "select3";
-  select2.parentNode.insertBefore(select3, select2.nextElementSibling);
-
-  is(select3.childElementCount, 2, "There should be a popup child and clone");
-  let popupBox = select3.querySelector(".rich-select-popup-box");
-  ok(popupBox, "The popup box should exist");
-  is(popupBox.childElementCount, 0, "The popup should not have any children");
-
-  let newOption = new AddressOption();
-  newOption.name = "Jared FooBar";
-  select3.appendChild(newOption);
-  await asyncElementRendered();
-
-  is(select3.childElementCount, 2, "There should now be two children");
-  is(popupBox.childElementCount, 1, "The popup box should have one child");
-  ok(!popupBox.children[0].selected, "The only option should not be marked selected");
-  is(select3.selectedOption, null, "Check there is no selected option");
-  let clonedOption = select3.querySelector(".rich-select-selected-clone");
-  ok(clonedOption, "cloned option exists");
-
-  // Select the new option
-  select3.selectedOption = popupBox.lastElementChild;
-  await asyncElementRendered();
-
-  clonedOption = select3.querySelector(".rich-select-selected-clone");
-  is(clonedOption.name, "Jared FooBar", "Check cloned name");
-
-  newOption.remove();
-  await asyncElementRendered();
-
-  is(select3.childElementCount, 2, "There should still be two children");
-  is(popupBox.childElementCount, 0, "The popup box should not have any children");
-  clonedOption = select3.querySelector(".rich-select-selected-clone");
-  todo(clonedOption.textContent.includes("None selected"), "the dropdown should show no selection");
-});
-
 </script>
 
 </body>
 </html>
--- a/browser/components/payments/test/mochitest/test_shipping_option_picker.html
+++ b/browser/components/payments/test/mochitest/test_shipping_option_picker.html
@@ -68,21 +68,21 @@ add_task(async function test_initialSet(
       },
     },
     selectedShippingOption: "456",
   });
   await asyncElementRendered();
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 2, "Check dropdown has both options");
   ok(options[0].textContent.includes("Carrier Pigeon"), "Check first option");
-  is(options[0].querySelector(".amount").currency, "USD", "Check currency");
+  is(options[0].getAttribute("amount-currency"), "USD", "Check currency");
   ok(options[1].textContent.includes("Lightspeed (default)"), "Check second option");
   is(picker1.dropdown.selectedOption, options[1], "Lightspeed selected by default");
 
-  let selectedClone = picker1.dropdown.querySelector(".rich-select-selected-clone");
+  let selectedClone = picker1.dropdown.querySelector(".rich-select-selected-option");
   let text = selectedClone.textContent;
   ok(text.includes("$20.00"),
      "Shipping option clone should include amount. Value = " + text);
   ok(text.includes("Lightspeed (default)"),
      "Shipping option clone should include label. Value = " + text);
   ok(!isHidden(selectedClone),
      "Shipping option clone should be visible");
 });
@@ -115,34 +115,34 @@ add_task(async function test_update() {
     },
     selectedShippingOption: "456",
   });
 
   await promiseStateChange(picker1.requestStore);
   let options = picker1.dropdown.popupBox.children;
   is(options.length, 2, "Check dropdown still has both options");
   ok(options[0].textContent.includes("Tortoise"), "Check updated first option");
-  is(options[0].querySelector(".amount").currency, "CAD", "Check currency");
+  is(options[0].getAttribute("amount-currency"), "CAD", "Check currency");
   ok(options[1].textContent.includes("Lightspeed (default)"), "Check second option is the same");
   is(picker1.dropdown.selectedOption, options[1], "Lightspeed selected by default");
 });
 
 add_task(async function test_change_selected_option() {
   let options = picker1.dropdown.popupBox.children;
   let selectedOption = picker1.dropdown.selectedOption;
   is(options[1], selectedOption, "Should default to Lightspeed option");
   is(selectedOption.value, "456", "Selected option should have correct ID");
   let state = picker1.requestStore.getState();
   let selectedOptionFromState = state.selectedShippingOption;
   is(selectedOption.value, selectedOptionFromState,
      "store's selected option should match selected element");
 
   let stateChangedPromise = promiseStateChange(picker1.requestStore);
-  picker1.dropdown.click();
-  options[0].click();
+  picker1.dropdown.popupBox.focus();
+  synthesizeKey(options[0].textContent, {});
   state = await stateChangedPromise;
 
   selectedOption = picker1.dropdown.selectedOption;
   is(selectedOption, options[0], "Selected option should now be the first option");
   is(selectedOption.value, "123", "Selected option should have correct ID");
   selectedOptionFromState = state.selectedShippingOption;
   is(selectedOptionFromState, "123", "store should have first option selected");
 });