/* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; /** * Ensure picker opens, closes, and updates its value with key bindings appropriately. */ add_task(async function test_datepicker_keyboard_nav() { info( "Ensure picker opens, closes, and updates its value with key bindings appropriately." ); const inputValue = "2016-12-15"; const prevMonth = "2016-11-01"; await helper.openPicker( `data:text/html,` ); let browser = helper.tab.linkedBrowser; Assert.equal(helper.panel.state, "open", "Panel should be opened"); await testCalendarBtnAttribute("aria-expanded", "true"); let closed = helper.promisePickerClosed(); // Close on Escape anywhere EventUtils.synthesizeKey("KEY_Escape", {}); await closed; Assert.equal( helper.panel.state, "closed", "Panel should be closed after Escape from anywhere on the window" ); await testCalendarBtnAttribute("aria-expanded", "false"); let ready = helper.waitForPickerReady(); // Ensure focus is on the input field await SpecialPowers.spawn(browser, [], () => { content.document.querySelector("#date").focus(); }); info("Test that input updates with the keyboard update the picker"); // NOTE: After a Tab, the first input field (the month one) is focused, // so down arrow will change the selected month. // // This assumes en-US locale, which seems fine for testing purposes (as // DATE_FORMAT and other bits around do the same). BrowserTestUtils.synthesizeKey("KEY_ArrowDown", {}, browser); // Toggle the picker on Space anywhere within the input BrowserTestUtils.synthesizeKey(" ", {}, browser); await ready; await testCalendarBtnAttribute("aria-expanded", "true"); Assert.equal( helper.panel.state, "open", "Panel should be opened on Space from anywhere within the input field" ); Assert.equal( helper.panel.querySelector("#dateTimePopupFrame").contentDocument .activeElement.textContent, "15", "Picker is opened with a focus set to the currently selected date" ); let monthYearEl = helper.getElement(MONTH_YEAR); await BrowserTestUtils.waitForMutationCondition( monthYearEl, { childList: true }, () => { return monthYearEl.textContent == DATE_FORMAT(new Date(prevMonth)); }, `Should change to November 2016, instead got ${ helper.getElement(MONTH_YEAR).textContent }` ); Assert.ok( true, "The date on both the Calendar and Month-Year button was updated when updating months with Down arrow key" ); closed = helper.promisePickerClosed(); // Close on Escape and return the focus to the input field (the month input in en-US locale) EventUtils.synthesizeKey("KEY_Escape", {}, window); await closed; Assert.equal( helper.panel.state, "closed", "Panel should be closed on Escape" ); // Check the focus is returned to the Month field await SpecialPowers.spawn(browser, [], async () => { const input = content.document.querySelector("input"); const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot; // Separators "/" are odd children of the wrapper const monthField = shadowRoot.getElementById("edit-wrapper").children[0]; // Testing the focus position within content: Assert.equal( input, content.document.activeElement, `The input field includes programmatic focus` ); // Testing the focus indication within the shadow-root: Assert.ok( monthField.matches(":focus"), `The keyboard focus was returned to the Month field` ); }); // Move focus to the second field (the day input in en-US locale) BrowserTestUtils.synthesizeKey("KEY_ArrowRight", {}, browser); // Change the day to 2016-12-16 BrowserTestUtils.synthesizeKey("KEY_ArrowUp", {}, browser); ready = helper.waitForPickerReady(); // Open the picker on Space within the input to check the date update await BrowserTestUtils.synthesizeKey(" ", {}, browser); await ready; await testCalendarBtnAttribute("aria-expanded", "true"); Assert.equal(helper.panel.state, "open", "Panel should be opened on Space"); let selectedDayEl = helper.getElement(DAY_SELECTED); await BrowserTestUtils.waitForMutationCondition( selectedDayEl, { childList: true }, () => { return selectedDayEl.textContent === "16"; }, `Should change to the 16th, instead got ${ helper.getElement(DAY_SELECTED).textContent }` ); Assert.ok( true, "The date on the Calendar was updated when updating days with Up arrow key" ); closed = helper.promisePickerClosed(); // Close on Escape and return the focus to the input field (the day input in en-US locale) EventUtils.synthesizeKey("KEY_Escape", {}, window); await closed; Assert.equal( helper.panel.state, "closed", "Panel should be closed on Escape" ); await testCalendarBtnAttribute("aria-expanded", "false"); // Check the focus is returned to the Day field await SpecialPowers.spawn(browser, [], async () => { const input = content.document.querySelector("input"); const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot; // Separators "/" are odd children of the wrapper const dayField = shadowRoot.getElementById("edit-wrapper").children[2]; // Testing the focus position within content: Assert.equal( input, content.document.activeElement, `The input field includes programmatic focus` ); // Testing the focus indication within the shadow-root: Assert.ok( dayField.matches(":focus"), `The keyboard focus was returned to the Day field` ); }); info("Test the Calendar button can toggle the picker with Enter/Space"); // Move focus to the Calendar button BrowserTestUtils.synthesizeKey("KEY_Tab", {}, browser); BrowserTestUtils.synthesizeKey("KEY_Tab", {}, browser); // Toggle the picker on Enter on Calendar button await BrowserTestUtils.synthesizeKey("KEY_Enter", {}, browser); await helper.waitForPickerReady(); Assert.equal( helper.panel.state, "open", "Panel should be opened on Enter from the Calendar button" ); await testCalendarBtnAttribute("aria-expanded", "true"); // Move focus from 2016-11-16 to 2016-11-17 EventUtils.synthesizeKey("KEY_ArrowRight", {}); // Make a selection by pressing Space on date gridcell await EventUtils.synthesizeKey(" ", {}); await helper.promisePickerClosed(); Assert.equal( helper.panel.state, "closed", "Panel should be closed on Space from the date gridcell" ); await testCalendarBtnAttribute("aria-expanded", "false"); // Check the focus is returned to the Calendar button await SpecialPowers.spawn(browser, [], async () => { const input = content.document.querySelector("input"); const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot; const calendarBtn = shadowRoot.getElementById("calendar-button"); // Testing the focus position within content: Assert.equal( input, content.document.activeElement, `The input field includes programmatic focus` ); // Testing the focus indication within the shadow-root: Assert.ok( calendarBtn.matches(":focus"), `The keyboard focus was returned to the Calendar button` ); }); // Check the Backspace on Calendar button is not doing anything await EventUtils.synthesizeKey("KEY_Backspace", {}); // The Calendar button is on its place and the input value is not changed // (bug 1804669) await SpecialPowers.spawn(browser, [], () => { const input = content.document.querySelector("input"); const shadowRoot = SpecialPowers.wrap(input).openOrClosedShadowRoot; const calendarBtn = shadowRoot.getElementById("calendar-button"); Assert.equal( calendarBtn.children[0].tagName, "svg", `Calendar button has an child` ); Assert.equal(input.value, "2016-11-17", `Input's value is not removed`); }); // Toggle the picker on Space on Calendar button await EventUtils.synthesizeKey(" ", {}); await helper.waitForPickerReady(); Assert.equal( helper.panel.state, "open", "Panel should be opened on Space from the Calendar button" ); await testCalendarBtnAttribute("aria-expanded", "true"); await helper.tearDown(); }); /** * Ensure calendar follows Arrow key bindings appropriately. */ add_task(async function test_datepicker_keyboard_arrows() { info("Ensure calendar follows Arrow key bindings appropriately."); const inputValue = "2016-12-10"; const prevMonth = "2016-11-01"; await helper.openPicker( `data:text/html,