forked from mirrors/gecko-dev
Differential Revision: https://phabricator.services.mozilla.com/D28431 --HG-- extra : moz-landing-system : lando
272 lines
9.3 KiB
JavaScript
272 lines
9.3 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Test the keyboard behavior of PanelViews.
|
|
*/
|
|
|
|
const {PanelMultiView} = ChromeUtils.import("resource:///modules/PanelMultiView.jsm");
|
|
|
|
let gAnchor;
|
|
let gPanel;
|
|
let gPanelMultiView;
|
|
let gMainView;
|
|
let gMainButton1;
|
|
let gMainMenulist;
|
|
let gMainTextbox;
|
|
let gMainButton2;
|
|
let gMainButton3;
|
|
let gMainTabOrder;
|
|
let gMainArrowOrder;
|
|
let gSubView;
|
|
let gSubButton;
|
|
let gSubTextarea;
|
|
|
|
async function openPopup() {
|
|
let shown = BrowserTestUtils.waitForEvent(gMainView, "ViewShown");
|
|
PanelMultiView.openPopup(gPanel, gAnchor, "bottomcenter topright");
|
|
await shown;
|
|
}
|
|
|
|
async function hidePopup() {
|
|
let hidden = BrowserTestUtils.waitForEvent(gPanel, "popuphidden");
|
|
PanelMultiView.hidePopup(gPanel);
|
|
await hidden;
|
|
}
|
|
|
|
async function showSubView() {
|
|
let shown = BrowserTestUtils.waitForEvent(gSubView, "ViewShown");
|
|
gPanelMultiView.showSubView(gSubView);
|
|
await shown;
|
|
}
|
|
|
|
async function expectFocusAfterKey(aKey, aFocus) {
|
|
let res = aKey.match(/^(Shift\+)?(.+)$/);
|
|
let shift = Boolean(res[1]);
|
|
let key;
|
|
if (res[2].length == 1) {
|
|
key = res[2]; // Character.
|
|
} else {
|
|
key = "KEY_" + res[2]; // Tab, ArrowRight, etc.
|
|
}
|
|
info("Waiting for focus on " + aFocus.id);
|
|
let focused = BrowserTestUtils.waitForEvent(aFocus, "focus");
|
|
EventUtils.synthesizeKey(key, {shiftKey: shift});
|
|
await focused;
|
|
ok(true, aFocus.id + " focused after " + aKey + " pressed");
|
|
}
|
|
|
|
add_task(async function setup() {
|
|
let navBar = document.getElementById("nav-bar");
|
|
gAnchor = document.createXULElement("toolbarbutton");
|
|
navBar.appendChild(gAnchor);
|
|
gPanel = document.createXULElement("panel");
|
|
navBar.appendChild(gPanel);
|
|
gPanelMultiView = document.createXULElement("panelmultiview");
|
|
gPanelMultiView.setAttribute("mainViewId", "testMainView");
|
|
gPanel.appendChild(gPanelMultiView);
|
|
|
|
gMainView = document.createXULElement("panelview");
|
|
gMainView.id = "testMainView";
|
|
gPanelMultiView.appendChild(gMainView);
|
|
gMainButton1 = document.createXULElement("button");
|
|
gMainButton1.id = "gMainButton1";
|
|
gMainView.appendChild(gMainButton1);
|
|
gMainMenulist = document.createXULElement("menulist");
|
|
gMainMenulist.id = "gMainMenulist";
|
|
gMainView.appendChild(gMainMenulist);
|
|
let menuPopup = document.createXULElement("menupopup");
|
|
gMainMenulist.appendChild(menuPopup);
|
|
let item = document.createXULElement("menuitem");
|
|
item.setAttribute("value", "1");
|
|
item.setAttribute("selected", "true");
|
|
menuPopup.appendChild(item);
|
|
item = document.createXULElement("menuitem");
|
|
item.setAttribute("value", "2");
|
|
menuPopup.appendChild(item);
|
|
gMainTextbox = document.createXULElement("textbox");
|
|
gMainTextbox.id = "gMainTextbox";
|
|
gMainView.appendChild(gMainTextbox);
|
|
gMainTextbox.setAttribute("value", "value");
|
|
gMainButton2 = document.createXULElement("button");
|
|
gMainButton2.id = "gMainButton2";
|
|
gMainView.appendChild(gMainButton2);
|
|
gMainButton3 = document.createXULElement("button");
|
|
gMainButton3.id = "gMainButton3";
|
|
gMainView.appendChild(gMainButton3);
|
|
gMainTabOrder = [gMainButton1, gMainMenulist, gMainTextbox, gMainButton2,
|
|
gMainButton3];
|
|
gMainArrowOrder = [gMainButton1, gMainButton2, gMainButton3];
|
|
|
|
gSubView = document.createXULElement("panelview");
|
|
gSubView.id = "testSubView";
|
|
gPanelMultiView.appendChild(gSubView);
|
|
gSubButton = document.createXULElement("button");
|
|
gSubView.appendChild(gSubButton);
|
|
gSubTextarea = document.createElementNS("http://www.w3.org/1999/xhtml",
|
|
"textarea");
|
|
gSubTextarea.id = "gSubTextarea";
|
|
gSubView.appendChild(gSubTextarea);
|
|
gSubTextarea.value = "value";
|
|
|
|
registerCleanupFunction(() => {
|
|
gAnchor.remove();
|
|
gPanel.remove();
|
|
});
|
|
});
|
|
|
|
// Test that the tab key focuses all expected controls.
|
|
add_task(async function testTab() {
|
|
await openPopup();
|
|
for (let elem of gMainTabOrder) {
|
|
await expectFocusAfterKey("Tab", elem);
|
|
}
|
|
// Wrap around.
|
|
await expectFocusAfterKey("Tab", gMainTabOrder[0]);
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test that the shift+tab key focuses all expected controls.
|
|
add_task(async function testShiftTab() {
|
|
await openPopup();
|
|
for (let i = gMainTabOrder.length - 1; i >= 0; --i) {
|
|
await expectFocusAfterKey("Shift+Tab", gMainTabOrder[i]);
|
|
}
|
|
// Wrap around.
|
|
await expectFocusAfterKey("Shift+Tab",
|
|
gMainTabOrder[gMainTabOrder.length - 1]);
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test that the down arrow key skips menulists and textboxes.
|
|
add_task(async function testDownArrow() {
|
|
await openPopup();
|
|
for (let elem of gMainArrowOrder) {
|
|
await expectFocusAfterKey("ArrowDown", elem);
|
|
}
|
|
// Wrap around.
|
|
await expectFocusAfterKey("ArrowDown", gMainArrowOrder[0]);
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test that the up arrow key skips menulists and textboxes.
|
|
add_task(async function testUpArrow() {
|
|
await openPopup();
|
|
for (let i = gMainArrowOrder.length - 1; i >= 0; --i) {
|
|
await expectFocusAfterKey("ArrowUp", gMainArrowOrder[i]);
|
|
}
|
|
// Wrap around.
|
|
await expectFocusAfterKey("ArrowUp",
|
|
gMainArrowOrder[gMainArrowOrder.length - 1]);
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test that the home/end keys move to the first/last controls.
|
|
add_task(async function testHomeEnd() {
|
|
await openPopup();
|
|
await expectFocusAfterKey("Home", gMainArrowOrder[0]);
|
|
await expectFocusAfterKey("End",
|
|
gMainArrowOrder[gMainArrowOrder.length - 1]);
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test that the up/down arrow keys work as expected in menulists.
|
|
add_task(async function testArrowsMenulist() {
|
|
await openPopup();
|
|
gMainMenulist.focus();
|
|
is(document.activeElement, gMainMenulist, "menulist focused");
|
|
is(gMainMenulist.value, "1", "menulist initial value 1");
|
|
if (AppConstants.platform == "macosx") {
|
|
// On Mac, down/up arrows just open the menulist.
|
|
let popup = gMainMenulist.menupopup;
|
|
for (let key of ["ArrowDown", "ArrowUp"]) {
|
|
let shown = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
|
EventUtils.synthesizeKey("KEY_" + key);
|
|
await shown;
|
|
ok(gMainMenulist.open, "menulist open after " + key);
|
|
let hidden = BrowserTestUtils.waitForEvent(popup, "popuphidden");
|
|
EventUtils.synthesizeKey("KEY_Escape");
|
|
await hidden;
|
|
ok(!gMainMenulist.open, "menulist closed after Escape");
|
|
}
|
|
} else {
|
|
// On other platforms, down/up arrows change the value without opening the
|
|
// menulist.
|
|
EventUtils.synthesizeKey("KEY_ArrowDown");
|
|
is(document.activeElement, gMainMenulist,
|
|
"menulist still focused after ArrowDown");
|
|
is(gMainMenulist.value, "2", "menulist value 2 after ArrowDown");
|
|
EventUtils.synthesizeKey("KEY_ArrowUp");
|
|
is(document.activeElement, gMainMenulist,
|
|
"menulist still focused after ArrowUp");
|
|
is(gMainMenulist.value, "1", "menulist value 1 after ArrowUp");
|
|
}
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test that pressing space in a textbox inserts a space (instead of trying to
|
|
// activate the control).
|
|
add_task(async function testSpaceTextbox() {
|
|
await openPopup();
|
|
gMainTextbox.focus();
|
|
gMainTextbox.selectionStart = gMainTextbox.selectionEnd = 0;
|
|
EventUtils.synthesizeKey(" ");
|
|
is(gMainTextbox.value, " value", "Space typed into textbox");
|
|
gMainTextbox.value = "value";
|
|
await hidePopup();
|
|
});
|
|
|
|
// Tests that the left arrow key normally moves back to the previous view.
|
|
add_task(async function testLeftArrow() {
|
|
await openPopup();
|
|
await showSubView();
|
|
let shown = BrowserTestUtils.waitForEvent(gMainView, "ViewShown");
|
|
EventUtils.synthesizeKey("KEY_ArrowLeft");
|
|
await shown;
|
|
ok("Moved to previous view after ArrowLeft");
|
|
await hidePopup();
|
|
});
|
|
|
|
// Tests that the left arrow key moves the caret in a textarea in a subview
|
|
// (instead of going back to the previous view).
|
|
add_task(async function testLeftArrowTextarea() {
|
|
await openPopup();
|
|
await showSubView();
|
|
gSubTextarea.focus();
|
|
is(document.activeElement, gSubTextarea, "textarea focused");
|
|
EventUtils.synthesizeKey("KEY_End");
|
|
is(gSubTextarea.selectionStart, 5, "selectionStart 5 after End");
|
|
EventUtils.synthesizeKey("KEY_ArrowLeft");
|
|
is(gSubTextarea.selectionStart, 4, "selectionStart 4 after ArrowLeft");
|
|
is(document.activeElement, gSubTextarea, "textarea still focused");
|
|
await hidePopup();
|
|
});
|
|
|
|
// Test navigation to a button which is initially disabled and later enabled.
|
|
add_task(async function testDynamicButton() {
|
|
gMainButton2.disabled = true;
|
|
await openPopup();
|
|
await expectFocusAfterKey("ArrowDown", gMainButton1);
|
|
await expectFocusAfterKey("ArrowDown", gMainButton3);
|
|
gMainButton2.disabled = false;
|
|
await expectFocusAfterKey("ArrowUp", gMainButton2);
|
|
await hidePopup();
|
|
});
|
|
|
|
add_task(async function testActivation() {
|
|
function checkActivated(elem, activationFn, reason) {
|
|
let activated = false;
|
|
elem.onclick = function() { activated = true; };
|
|
activationFn();
|
|
ok(activated, "Should have activated button after " + reason);
|
|
elem.onclick = null;
|
|
}
|
|
await openPopup();
|
|
await expectFocusAfterKey("ArrowDown", gMainButton1);
|
|
checkActivated(gMainButton1, () => EventUtils.synthesizeKey("KEY_Enter"), "pressing enter");
|
|
checkActivated(gMainButton1, () => EventUtils.synthesizeKey(" "), "pressing space");
|
|
checkActivated(gMainButton1, () => EventUtils.synthesizeKey("KEY_Enter", {code: "NumpadEnter"}), "pressing numpad enter");
|
|
await hidePopup();
|
|
});
|