Bug 1463554 - Fix the tests that are broken due to change in the internal structure of rich-select.r=MattN
authorprathiksha <prathikshaprasadsuman@gmail.com>
Wed, 27 Jun 2018 17:32:54 -0700
changeset 426603 74971b656b9232052ebdeaf52e60ef9253547e90
parent 426602 e47e23fcb698e7b00844015f8e934b2b8b245301
child 426604 3acee2d5cd9d94252bbffba40799b09aee9daa64
push id34277
push useraciure@mozilla.com
push dateSun, 15 Jul 2018 09:51:53 +0000
treeherdermozilla-central@315103f6db6b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN
bugs1463554
milestone63.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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");
 });