/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the Menu API works
const URL =
"data:text/html;charset=utf8,test page for menu api";
const Menu = require(
"resource://devtools/client/framework/menu.js");
const MenuItem = require(
"resource://devtools/client/framework/menu-item.js");
add_task(async
function () {
info(
"Create a test tab and open the toolbox");
const tab = await addTab(URL);
const toolbox = await gDevTools.showToolboxForTab(tab, {
toolId:
"webconsole",
});
// This test will involve localized strings, make sure the necessary FTL file is
// available in the toolbox top window.
toolbox.topWindow.MozXULElement.insertFTLIfNeeded(
"toolkit/global/textActions.ftl"
);
loadFTL(toolbox,
"toolkit/global/textActions.ftl");
await testMenuItems();
await testMenuPopup(toolbox);
await testSubmenu(toolbox);
});
function testMenuItems() {
const menu =
new Menu();
const menuItem1 =
new MenuItem();
const menuItem2 =
new MenuItem();
menu.append(menuItem1);
menu.append(menuItem2);
is(menu.items.length, 2,
"Correct number of 'items'");
is(menu.items[0], menuItem1,
"Correct reference to MenuItem");
is(menu.items[1], menuItem2,
"Correct reference to MenuItem");
}
async
function testMenuPopup(toolbox) {
let clickFired =
false;
const menu =
new Menu({
id:
"menu-popup",
});
menu.append(
new MenuItem({ type:
"separator" }));
const MENU_ITEMS = [
new MenuItem({
id:
"menu-item-1",
label:
"Normal Item",
click: () => {
info(
"Click callback has fired for menu item");
clickFired =
true;
},
}),
new MenuItem({
label:
"Checked Item",
type:
"checkbox",
checked:
true,
}),
new MenuItem({
label:
"Radio Item",
type:
"radio",
}),
new MenuItem({
label:
"Disabled Item",
disabled:
true,
}),
new MenuItem({
l10nID:
"text-action-undo",
}),
];
for (
const item of MENU_ITEMS) {
menu.append(item);
}
// Append an invisible MenuItem, which shouldn't show up in the DOM
menu.append(
new MenuItem({
label:
"Invisible",
visible:
false,
})
);
menu.popup(0, 0, toolbox.doc);
const popup = toolbox.topDoc.querySelector(
"#menu-popup");
ok(popup,
"A popup is in the DOM");
const menuSeparators = toolbox.topDoc.querySelectorAll(
"#menu-popup > menuseparator"
);
is(menuSeparators.length, 1,
"A separator is in the menu");
const menuItems = toolbox.topDoc.querySelectorAll(
"#menu-popup > menuitem");
is(menuItems.length, MENU_ITEMS.length,
"Correct number of menuitems");
is(menuItems[0].id, MENU_ITEMS[0].id,
"Correct id for menuitem");
is(menuItems[0].getAttribute(
"label"), MENU_ITEMS[0].label,
"Correct label");
is(menuItems[1].getAttribute(
"label"), MENU_ITEMS[1].label,
"Correct label");
is(menuItems[1].getAttribute(
"type"),
"checkbox",
"Correct type attr");
is(menuItems[1].getAttribute(
"checked"),
"true",
"Has checked attr");
is(menuItems[2].getAttribute(
"label"), MENU_ITEMS[2].label,
"Correct label");
is(menuItems[2].getAttribute(
"type"),
"radio",
"Correct type attr");
ok(!menuItems[2].hasAttribute(
"checked"),
"Doesn't have checked attr");
is(menuItems[3].getAttribute(
"label"), MENU_ITEMS[3].label,
"Correct label");
is(menuItems[3].getAttribute(
"disabled"),
"true",
"disabled attr menuitem");
is(
menuItems[4].getAttribute(
"data-l10n-id"),
MENU_ITEMS[4].l10nID,
"Correct localization attribute"
);
await once(menu,
"open");
const closed = once(menu,
"close");
popup.activateItem(menuItems[0]);
await closed;
ok(clickFired,
"Click has fired");
ok(
!toolbox.topDoc.querySelector(
"#menu-popup"),
"Popup removed from the DOM"
);
}
async
function testSubmenu(toolbox) {
let clickFired =
false;
const menu =
new Menu({
id:
"menu-popup",
});
const submenu =
new Menu({
id:
"submenu-popup",
});
submenu.append(
new MenuItem({
label:
"Submenu item",
click: () => {
info(
"Click callback has fired for submenu item");
clickFired =
true;
},
})
);
menu.append(
new MenuItem({
l10nID:
"text-action-copy",
submenu,
})
);
menu.append(
new MenuItem({
label:
"Submenu parent with attributes",
id:
"submenu-parent-with-attrs",
submenu,
accesskey:
"A",
disabled:
true,
})
);
menu.popup(0, 0, toolbox.doc);
const popup = toolbox.topDoc.querySelector(
"#menu-popup");
ok(popup,
"A popup is in the DOM");
is(
toolbox.topDoc.querySelectorAll(
"#menu-popup > menuitem").length,
0,
"No menuitem children"
);
const menus = toolbox.topDoc.querySelectorAll(
"#menu-popup > menu");
is(menus.length, 2,
"Correct number of menus");
ok(
!menus[0].hasAttribute(
"label"),
"No label: should be set by localization"
);
ok(!menus[0].hasAttribute(
"disabled"),
"Correct disabled state");
is(
menus[0].getAttribute(
"data-l10n-id"),
"text-action-copy",
"Correct localization attribute"
);
is(menus[1].getAttribute(
"accesskey"),
"A",
"Correct accesskey");
ok(menus[1].hasAttribute(
"disabled"),
"Correct disabled state");
is(menus[1].id,
"submenu-parent-with-attrs",
"Correct id");
const subMenuItems = menus[0].querySelectorAll(
"menupopup > menuitem");
is(subMenuItems.length, 1,
"Correct number of submenu items");
is(subMenuItems[0].getAttribute(
"label"),
"Submenu item",
"Correct label");
await once(menu,
"open");
const closed = once(menu,
"close");
// The following section tests keyboard navigation of the context menus.
// This doesn't work on macOS when native context menus are enabled.
if (Services.prefs.getBoolPref(
"widget.macos.native-context-menus",
false)) {
info(
"Using openMenu semantics because of macOS native context menus.");
let shown = once(menus[0],
"popupshown");
menus[0].openMenu(
true);
await shown;
const hidden = once(menus[0],
"popuphidden");
menus[0].openMenu(
false);
await hidden;
shown = once(menus[0],
"popupshown");
menus[0].openMenu(
true);
await shown;
}
else {
info(
"Using keyboard navigation to open, close, and reopen the submenu");
let shown = once(menus[0],
"popupshown");
EventUtils.synthesizeKey(
"KEY_ArrowDown");
EventUtils.synthesizeKey(
"KEY_ArrowRight");
await shown;
const hidden = once(menus[0],
"popuphidden");
EventUtils.synthesizeKey(
"KEY_ArrowLeft");
await hidden;
shown = once(menus[0],
"popupshown");
EventUtils.synthesizeKey(
"KEY_ArrowRight");
await shown;
}
info(
"Clicking the submenu item");
const subMenu = subMenuItems[0].closest(
"menupopup");
subMenu.activateItem(subMenuItems[0]);
await closed;
ok(clickFired,
"Click has fired");
}