"use strict"; /** * Test keyboard navigation in the app menu panel. */ const { PanelView } = ChromeUtils.import( "resource:///modules/PanelMultiView.jsm" ); const kHelpButtonId = "appMenu-help-button2"; function getEnabledNavigableElementsForView(panelView) { return Array.from( panelView.querySelectorAll("button,toolbarbutton,menulist,.text-link") ).filter(element => { let bounds = element.getBoundingClientRect(); return !element.disabled && bounds.width > 0 && bounds.height > 0; }); } add_task(async function testUpDownKeys() { await gCUITestUtils.openMainMenu(); let buttons = getEnabledNavigableElementsForView(PanelUI.mainView); for (let button of buttons) { if (button.disabled) { continue; } EventUtils.synthesizeKey("KEY_ArrowDown"); Assert.equal( document.commandDispatcher.focusedElement, button, "The correct button should be focused after navigating downward" ); } EventUtils.synthesizeKey("KEY_ArrowDown"); Assert.equal( document.commandDispatcher.focusedElement, buttons[0], "Pressing upwards should cycle around and select the first button again" ); for (let i = buttons.length - 1; i >= 0; --i) { let button = buttons[i]; if (button.disabled) { continue; } EventUtils.synthesizeKey("KEY_ArrowUp"); Assert.equal( document.commandDispatcher.focusedElement, button, "The first button should be focused after navigating upward" ); } await gCUITestUtils.hideMainMenu(); }); add_task(async function testHomeEndKeys() { await gCUITestUtils.openMainMenu(); let buttons = getEnabledNavigableElementsForView(PanelUI.mainView); let enabledButtons = buttons.filter(btn => !btn.disabled); let firstButton = enabledButtons[0]; let lastButton = enabledButtons.pop(); Assert.ok(firstButton != lastButton, "There is more than one button"); EventUtils.synthesizeKey("KEY_End"); Assert.equal( document.commandDispatcher.focusedElement, lastButton, "The last button should be focused after pressing End" ); EventUtils.synthesizeKey("KEY_Home"); Assert.equal( document.commandDispatcher.focusedElement, firstButton, "The first button should be focused after pressing Home" ); await gCUITestUtils.hideMainMenu(); }); add_task(async function testEnterKeyBehaviors() { await gCUITestUtils.openMainMenu(); let buttons = getEnabledNavigableElementsForView(PanelUI.mainView); // Navigate to the 'Help' button, which points to a subview. EventUtils.synthesizeKey("KEY_ArrowUp"); let focusedElement = document.commandDispatcher.focusedElement; Assert.equal( focusedElement, buttons[buttons.length - 1], "The last button should be focused after navigating upward" ); // Make sure the Help button is in focus. while ( !focusedElement || !focusedElement.id || focusedElement.id != kHelpButtonId ) { EventUtils.synthesizeKey("KEY_ArrowUp"); focusedElement = document.commandDispatcher.focusedElement; } EventUtils.synthesizeKey("KEY_Enter"); let helpView = document.getElementById("PanelUI-helpView"); await BrowserTestUtils.waitForEvent(helpView, "ViewShown"); let helpButtons = getEnabledNavigableElementsForView(helpView); Assert.ok( helpButtons[0].classList.contains("subviewbutton-back"), "First button in help view should be a back button" ); // For posterity, check navigating the subview using up/ down arrow keys as well. // When opening a subview, the first control *after* the Back button gets // focus. EventUtils.synthesizeKey("KEY_ArrowUp"); focusedElement = document.commandDispatcher.focusedElement; Assert.equal( focusedElement, helpButtons[0], "The Back button should be focused after navigating upward" ); for (let i = helpButtons.length - 1; i >= 0; --i) { let button = helpButtons[i]; if (button.disabled) { continue; } EventUtils.synthesizeKey("KEY_ArrowUp"); focusedElement = document.commandDispatcher.focusedElement; Assert.equal( focusedElement, button, "The previous button should be focused after navigating upward" ); } // Make sure the back button is in focus again. while (focusedElement != helpButtons[0]) { EventUtils.synthesizeKey("KEY_ArrowDown"); focusedElement = document.commandDispatcher.focusedElement; } // The first button is the back button. Hittin Enter should navigate us back. let promise = BrowserTestUtils.waitForEvent(PanelUI.mainView, "ViewShown"); EventUtils.synthesizeKey("KEY_Enter"); await promise; // Let's test a 'normal' command button. focusedElement = document.commandDispatcher.focusedElement; const kFindButtonId = "appMenu-find-button2"; while ( !focusedElement || !focusedElement.id || focusedElement.id != kFindButtonId ) { EventUtils.synthesizeKey("KEY_ArrowUp"); focusedElement = document.commandDispatcher.focusedElement; } let findBarPromise = gBrowser.isFindBarInitialized() ? null : BrowserTestUtils.waitForEvent(gBrowser.selectedTab, "TabFindInitialized"); Assert.equal( focusedElement.id, kFindButtonId, "Find button should be selected" ); await gCUITestUtils.hidePanelMultiView(PanelUI.panel, () => EventUtils.synthesizeKey("KEY_Enter") ); await findBarPromise; Assert.ok(!gFindBar.hidden, "Findbar should have opened"); gFindBar.close(); }); add_task(async function testLeftRightKeys() { await gCUITestUtils.openMainMenu(); // Navigate to the 'Help' button, which points to a subview. let focusedElement = document.commandDispatcher.focusedElement; while ( !focusedElement || !focusedElement.id || focusedElement.id != kHelpButtonId ) { EventUtils.synthesizeKey("KEY_ArrowUp"); focusedElement = document.commandDispatcher.focusedElement; } Assert.equal( focusedElement.id, kHelpButtonId, "The last button should be focused after navigating upward" ); // Hitting ArrowRight on a button that points to a subview should navigate us // there. EventUtils.synthesizeKey("KEY_ArrowRight"); let helpView = document.getElementById("PanelUI-helpView"); await BrowserTestUtils.waitForEvent(helpView, "ViewShown"); // Hitting ArrowLeft should navigate us back. let promise = BrowserTestUtils.waitForEvent(PanelUI.mainView, "ViewShown"); EventUtils.synthesizeKey("KEY_ArrowLeft"); await promise; focusedElement = document.commandDispatcher.focusedElement; Assert.equal( focusedElement.id, kHelpButtonId, "Help button should be focused again now that we're back in the main view" ); await gCUITestUtils.hideMainMenu(); }); add_task(async function testTabKey() { await gCUITestUtils.openMainMenu(); let buttons = getEnabledNavigableElementsForView(PanelUI.mainView); for (let button of buttons) { if (button.disabled) { continue; } EventUtils.synthesizeKey("KEY_Tab"); Assert.equal( document.commandDispatcher.focusedElement, button, "The correct button should be focused after tabbing" ); } EventUtils.synthesizeKey("KEY_Tab"); Assert.equal( document.commandDispatcher.focusedElement, buttons[0], "Pressing tab should cycle around and select the first button again" ); for (let i = buttons.length - 1; i >= 0; --i) { let button = buttons[i]; if (button.disabled) { continue; } EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); Assert.equal( document.commandDispatcher.focusedElement, button, "The correct button should be focused after shift + tabbing" ); } EventUtils.synthesizeKey("KEY_Tab", { shiftKey: true }); Assert.equal( document.commandDispatcher.focusedElement, buttons[buttons.length - 1], "Pressing shift + tab should cycle around and select the last button again" ); await gCUITestUtils.hideMainMenu(); }); add_task(async function testInterleavedTabAndArrowKeys() { await gCUITestUtils.openMainMenu(); let buttons = getEnabledNavigableElementsForView(PanelUI.mainView); let tab = false; for (let button of buttons) { if (button.disabled) { continue; } if (tab) { EventUtils.synthesizeKey("KEY_Tab"); } else { EventUtils.synthesizeKey("KEY_ArrowDown"); } tab = !tab; } Assert.equal( document.commandDispatcher.focusedElement, buttons[buttons.length - 1], "The last button should be focused after a mix of Tab and ArrowDown" ); await gCUITestUtils.hideMainMenu(); }); add_task(async function testSpaceDownAfterTabNavigation() { await gCUITestUtils.openMainMenu(); let buttons = getEnabledNavigableElementsForView(PanelUI.mainView); let button; for (button of buttons) { if (button.disabled) { continue; } EventUtils.synthesizeKey("KEY_Tab"); if (button.id == kHelpButtonId) { break; } } Assert.equal( document.commandDispatcher.focusedElement, button, "Help button should be focused after tabbing to it." ); // Pressing down space on a button that points to a subview should navigate us // there, before keyup. EventUtils.synthesizeKey(" ", { type: "keydown" }); let helpView = document.getElementById("PanelUI-helpView"); await BrowserTestUtils.waitForEvent(helpView, "ViewShown"); await gCUITestUtils.hideMainMenu(); });