tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

browser_menu_api.js (6861B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Test that the Menu API works
      7 
      8 const URL = "data:text/html;charset=utf8,test page for menu api";
      9 const Menu = require("resource://devtools/client/framework/menu.js");
     10 const MenuItem = require("resource://devtools/client/framework/menu-item.js");
     11 
     12 add_task(async function () {
     13  info("Create a test tab and open the toolbox");
     14  const tab = await addTab(URL);
     15  const toolbox = await gDevTools.showToolboxForTab(tab, {
     16    toolId: "webconsole",
     17  });
     18 
     19  // This test will involve localized strings, make sure the necessary FTL file is
     20  // available in the toolbox top window.
     21  toolbox.topWindow.MozXULElement.insertFTLIfNeeded(
     22    "toolkit/global/textActions.ftl"
     23  );
     24 
     25  loadFTL(toolbox, "toolkit/global/textActions.ftl");
     26 
     27  await testMenuItems();
     28  await testMenuPopup(toolbox);
     29  await testSubmenu(toolbox);
     30 });
     31 
     32 function testMenuItems() {
     33  const menu = new Menu();
     34  const menuItem1 = new MenuItem();
     35  const menuItem2 = new MenuItem();
     36 
     37  menu.append(menuItem1);
     38  menu.append(menuItem2);
     39 
     40  is(menu.items.length, 2, "Correct number of 'items'");
     41  is(menu.items[0], menuItem1, "Correct reference to MenuItem");
     42  is(menu.items[1], menuItem2, "Correct reference to MenuItem");
     43 }
     44 
     45 async function testMenuPopup(toolbox) {
     46  let clickFired = false;
     47 
     48  const menu = new Menu({
     49    id: "menu-popup",
     50  });
     51  menu.append(new MenuItem({ type: "separator" }));
     52 
     53  const MENU_ITEMS = [
     54    new MenuItem({
     55      id: "menu-item-1",
     56      label: "Normal Item",
     57      click: () => {
     58        info("Click callback has fired for menu item");
     59        clickFired = true;
     60      },
     61    }),
     62    new MenuItem({
     63      label: "Checked Item",
     64      type: "checkbox",
     65      checked: true,
     66    }),
     67    new MenuItem({
     68      label: "Radio Item",
     69      type: "radio",
     70    }),
     71    new MenuItem({
     72      label: "Disabled Item",
     73      disabled: true,
     74    }),
     75    new MenuItem({
     76      l10nID: "text-action-undo",
     77    }),
     78  ];
     79 
     80  for (const item of MENU_ITEMS) {
     81    menu.append(item);
     82  }
     83 
     84  // Append an invisible MenuItem, which shouldn't show up in the DOM
     85  menu.append(
     86    new MenuItem({
     87      label: "Invisible",
     88      visible: false,
     89    })
     90  );
     91 
     92  menu.popup(0, 0, toolbox.doc);
     93  const popup = toolbox.topDoc.querySelector("#menu-popup");
     94  ok(popup, "A popup is in the DOM");
     95 
     96  const menuSeparators = toolbox.topDoc.querySelectorAll(
     97    "#menu-popup > menuseparator"
     98  );
     99  is(menuSeparators.length, 1, "A separator is in the menu");
    100 
    101  const menuItems = toolbox.topDoc.querySelectorAll("#menu-popup > menuitem");
    102  is(menuItems.length, MENU_ITEMS.length, "Correct number of menuitems");
    103 
    104  is(menuItems[0].id, MENU_ITEMS[0].id, "Correct id for menuitem");
    105  is(menuItems[0].getAttribute("label"), MENU_ITEMS[0].label, "Correct label");
    106 
    107  is(menuItems[1].getAttribute("label"), MENU_ITEMS[1].label, "Correct label");
    108  is(menuItems[1].getAttribute("type"), "checkbox", "Correct type attr");
    109  ok(menuItems[1].hasAttribute("checked"), "Has checked attr");
    110 
    111  is(menuItems[2].getAttribute("label"), MENU_ITEMS[2].label, "Correct label");
    112  is(menuItems[2].getAttribute("type"), "radio", "Correct type attr");
    113  ok(!menuItems[2].hasAttribute("checked"), "Doesn't have checked attr");
    114 
    115  is(menuItems[3].getAttribute("label"), MENU_ITEMS[3].label, "Correct label");
    116  ok(menuItems[3].hasAttribute("disabled"), "disabled attr menuitem");
    117 
    118  is(
    119    menuItems[4].getAttribute("data-l10n-id"),
    120    MENU_ITEMS[4].l10nID,
    121    "Correct localization attribute"
    122  );
    123 
    124  await once(menu, "open");
    125  const closed = once(menu, "close");
    126  popup.activateItem(menuItems[0]);
    127  await closed;
    128  ok(clickFired, "Click has fired");
    129 
    130  ok(
    131    !toolbox.topDoc.querySelector("#menu-popup"),
    132    "Popup removed from the DOM"
    133  );
    134 }
    135 
    136 async function testSubmenu(toolbox) {
    137  let clickFired = false;
    138  const menu = new Menu({
    139    id: "menu-popup",
    140  });
    141  const submenu = new Menu({
    142    id: "submenu-popup",
    143  });
    144  submenu.append(
    145    new MenuItem({
    146      label: "Submenu item",
    147      click: () => {
    148        info("Click callback has fired for submenu item");
    149        clickFired = true;
    150      },
    151    })
    152  );
    153  menu.append(
    154    new MenuItem({
    155      l10nID: "text-action-copy",
    156      submenu,
    157    })
    158  );
    159  menu.append(
    160    new MenuItem({
    161      label: "Submenu parent with attributes",
    162      id: "submenu-parent-with-attrs",
    163      submenu,
    164      accesskey: "A",
    165      disabled: true,
    166    })
    167  );
    168 
    169  menu.popup(0, 0, toolbox.doc);
    170  const popup = toolbox.topDoc.querySelector("#menu-popup");
    171  ok(popup, "A popup is in the DOM");
    172  is(
    173    toolbox.topDoc.querySelectorAll("#menu-popup > menuitem").length,
    174    0,
    175    "No menuitem children"
    176  );
    177 
    178  const menus = toolbox.topDoc.querySelectorAll("#menu-popup > menu");
    179  is(menus.length, 2, "Correct number of menus");
    180  ok(
    181    !menus[0].hasAttribute("label"),
    182    "No label: should be set by localization"
    183  );
    184  ok(!menus[0].hasAttribute("disabled"), "Correct disabled state");
    185  is(
    186    menus[0].getAttribute("data-l10n-id"),
    187    "text-action-copy",
    188    "Correct localization attribute"
    189  );
    190 
    191  is(menus[1].getAttribute("accesskey"), "A", "Correct accesskey");
    192  ok(menus[1].hasAttribute("disabled"), "Correct disabled state");
    193  is(menus[1].id, "submenu-parent-with-attrs", "Correct id");
    194 
    195  const subMenuItems = menus[0].querySelectorAll("menupopup > menuitem");
    196  is(subMenuItems.length, 1, "Correct number of submenu items");
    197  is(subMenuItems[0].getAttribute("label"), "Submenu item", "Correct label");
    198 
    199  await once(menu, "open");
    200  const closed = once(menu, "close");
    201 
    202  // The following section tests keyboard navigation of the context menus.
    203  // This doesn't work on macOS when native context menus are enabled.
    204  if (Services.prefs.getBoolPref("widget.macos.native-context-menus", false)) {
    205    info("Using openMenu semantics because of macOS native context menus.");
    206    let shown = once(menus[0], "popupshown");
    207    menus[0].openMenu(true);
    208    await shown;
    209 
    210    const hidden = once(menus[0], "popuphidden");
    211    menus[0].openMenu(false);
    212    await hidden;
    213 
    214    shown = once(menus[0], "popupshown");
    215    menus[0].openMenu(true);
    216    await shown;
    217  } else {
    218    info("Using keyboard navigation to open, close, and reopen the submenu");
    219    let shown = once(menus[0], "popupshown");
    220    EventUtils.synthesizeKey("KEY_ArrowDown");
    221    EventUtils.synthesizeKey("KEY_ArrowRight");
    222    await shown;
    223 
    224    const hidden = once(menus[0], "popuphidden");
    225    EventUtils.synthesizeKey("KEY_ArrowLeft");
    226    await hidden;
    227 
    228    shown = once(menus[0], "popupshown");
    229    EventUtils.synthesizeKey("KEY_ArrowRight");
    230    await shown;
    231  }
    232 
    233  info("Clicking the submenu item");
    234  const subMenu = subMenuItems[0].closest("menupopup");
    235  subMenu.activateItem(subMenuItems[0]);
    236 
    237  await closed;
    238  ok(clickFired, "Click has fired");
    239 }