diff options
Diffstat (limited to '')
-rw-r--r-- | toolkit/content/tests/browser/datetime/browser_datetime_datepicker_keynav.js | 576 |
1 files changed, 576 insertions, 0 deletions
diff --git a/toolkit/content/tests/browser/datetime/browser_datetime_datepicker_keynav.js b/toolkit/content/tests/browser/datetime/browser_datetime_datepicker_keynav.js new file mode 100644 index 0000000000..0b271ed77a --- /dev/null +++ b/toolkit/content/tests/browser/datetime/browser_datetime_datepicker_keynav.js @@ -0,0 +1,576 @@ +/* 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,<input id=date type=date value=${inputValue}>` + ); + 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 <svg> 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,<input id=date type=date value=${inputValue}>` + ); + let pickerDoc = helper.panel.querySelector( + "#dateTimePopupFrame" + ).contentDocument; + Assert.equal(helper.panel.state, "open", "Panel should be opened"); + + // Move focus from 2016-12-10 to 2016-12-11: + EventUtils.synthesizeKey("KEY_ArrowRight", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "11", + "Arrow Right moves focus to the next day" + ); + + // Move focus from 2016-12-11 to 2016-12-04: + EventUtils.synthesizeKey("KEY_ArrowUp", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "4", + "Arrow Up moves focus to the same weekday of the previous week" + ); + + // Move focus from 2016-12-04 to 2016-12-03: + EventUtils.synthesizeKey("KEY_ArrowLeft", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "3", + "Arrow Left moves focus to the previous day" + ); + + // Move focus from 2016-12-03 to 2016-11-26: + EventUtils.synthesizeKey("KEY_ArrowUp", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "26", + "Arrow Up updates the view to be on the previous month, if needed" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevMonth)), + "Arrow Up updates the spinner to show the previous month, if needed" + ); + + // Move focus from 2016-11-26 to 2016-12-03: + EventUtils.synthesizeKey("KEY_ArrowDown", {}); + Assert.equal( + pickerDoc.activeElement.textContent, + "3", + "Arrow Down updates the view to be on the next month, if needed" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(inputValue)), + "Arrow Down updates the spinner to show the next month, if needed" + ); + + // Move focus from 2016-12-03 to 2016-12-10: + EventUtils.synthesizeKey("KEY_ArrowDown", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "10", + "Arrow Down moves focus to the same day of the next week" + ); + + await helper.tearDown(); +}); + +/** + * Ensure calendar follows Home/End key bindings appropriately. + */ +add_task(async function test_datepicker_keyboard_home_end() { + info("Ensure calendar follows Home/End key bindings appropriately."); + + const inputValue = "2016-12-15"; + const prevMonth = "2016-11-01"; + await helper.openPicker( + `data:text/html,<input id=date type=date value=${inputValue}>` + ); + let pickerDoc = helper.panel.querySelector( + "#dateTimePopupFrame" + ).contentDocument; + Assert.equal(helper.panel.state, "open", "Panel should be opened"); + + // Move focus from 2016-12-15 to 2016-12-11 (in the en-US locale): + EventUtils.synthesizeKey("KEY_Home", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "11", + "Home key moves focus to the first day/Sunday of the current week" + ); + + // Move focus from 2016-12-11 to 2016-12-17 (in the en-US locale): + EventUtils.synthesizeKey("KEY_End", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "17", + "End key moves focus to the last day/Saturday of the current week" + ); + + // Move focus from 2016-12-17 to 2016-12-31: + EventUtils.synthesizeKey("KEY_End", { ctrlKey: true }); + + Assert.equal( + pickerDoc.activeElement.textContent, + "31", + "Ctrl + End keys move focus to the last day of the current month" + ); + + // Move focus from 2016-12-31 to 2016-12-01: + EventUtils.synthesizeKey("KEY_Home", { ctrlKey: true }); + + Assert.equal( + pickerDoc.activeElement.textContent, + "1", + "Ctrl + Home keys move focus to the first day of the current month" + ); + + // Move focus from 2016-12-01 to 2016-11-27 (in the en-US locale): + EventUtils.synthesizeKey("KEY_Home", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "27", + "Home key updates the view to be on the previous month, if needed" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevMonth)), + "Home key updates the spinner to show the previous month, if needed" + ); + + // Move focus from 2016-11-27 to 2016-12-03 (in the en-US locale): + EventUtils.synthesizeKey("KEY_End", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "3", + "End key updates the view to be on the next month, if needed" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(inputValue)), + "End key updates the spinner to show the next month, if needed" + ); + + await helper.tearDown(); +}); + +/** + * Ensure calendar follows Page Up/Down key bindings appropriately. + */ +add_task(async function test_datepicker_keyboard_pgup_pgdown() { + info("Ensure calendar follows Page Up/Down key bindings appropriately."); + + const inputValue = "2023-01-31"; + const prevMonth = "2022-12-31"; + const prevYear = "2021-12-01"; + const nextMonth = "2023-01-31"; + const nextShortMonth = "2023-03-03"; + await helper.openPicker( + `data:text/html,<input id=date type=date value=${inputValue}>` + ); + let pickerDoc = helper.panel.querySelector( + "#dateTimePopupFrame" + ).contentDocument; + Assert.equal(helper.panel.state, "open", "Panel should be opened"); + + // Move focus from 2023-01-31 to 2022-12-31: + EventUtils.synthesizeKey("KEY_PageUp", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "31", + "Page Up key moves focus to the same day of the previous month" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevMonth)), + "Page Up key updates the month-year button to show the previous month" + ); + + // Move focus from 2022-12-31 to 2022-12-01 + // (because 2022-11-31 does not exist): + EventUtils.synthesizeKey("KEY_PageUp", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "1", + `When the same day does not exists in the previous month Page Up key moves + focus to the same day of the same week of the current month` + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevMonth)), + `When the same day does not exist in the previous month + Page Up key does not update the month-year button and shows the current month` + ); + + // Move focus from 2022-12-01 to 2021-12-01: + EventUtils.synthesizeKey("KEY_PageUp", { shiftKey: true }); + Assert.equal( + pickerDoc.activeElement.textContent, + "1", + "Page Up with Shift key moves focus to the same day of the same month of the previous year" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevYear)), + "Page Up with Shift key updates the month-year button to show the same month of the previous year" + ); + + // Move focus from 2021-12-01 to 2022-12-01 month by month (bug 1806645): + EventUtils.synthesizeKey("KEY_PageDown", { repeat: 12 }); + Assert.equal( + pickerDoc.activeElement.textContent, + "1", + "When repeated, Page Down key moves focus to the same day of the same month of the next year" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevMonth)), + "When repeated, Page Down key updates the month-year button to show the same month of the next year" + ); + + // Move focus from 2022-12-01 to 2021-12-01 month by month (bug 1806645): + EventUtils.synthesizeKey("KEY_PageUp", { repeat: 12 }); + Assert.equal( + pickerDoc.activeElement.textContent, + "1", + "When repeated, Page Up moves focus to the same day of the same month of the previous year" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevYear)), + "When repeated, Page Up key updates the month-year button to show the same month of the previous year" + ); + + // Move focus from 2021-12-01 to 2022-12-01: + EventUtils.synthesizeKey("KEY_PageDown", { shiftKey: true }); + Assert.equal( + pickerDoc.activeElement.textContent, + "1", + "Page Down with Shift key moves focus to the same day of the same month of the next year" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(prevMonth)), + "Page Down with Shift key updates the month-year button to show the same month of the next year" + ); + + // Move focus from 2016-12-01 to 2016-12-31: + EventUtils.synthesizeKey("KEY_End", { ctrlKey: true }); + // Move focus from 2022-12-31 to 2023-01-31: + EventUtils.synthesizeKey("KEY_PageDown", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "31", + "Page Down key moves focus to the same day of the next month" + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(nextMonth)), + "Page Down key updates the month-year button to show the next month" + ); + + // Move focus from 2023-01-31 to 2023-03-03: + EventUtils.synthesizeKey("KEY_PageDown", {}); + + Assert.equal( + pickerDoc.activeElement.textContent, + "3", + `When the same day does not exists in the next month, Page Down key moves + focus to the same day of the same week of the month after` + ); + Assert.equal( + helper.getElement(MONTH_YEAR).textContent, + DATE_FORMAT(new Date(nextShortMonth)), + "Page Down key updates the month-year button to show the month after" + ); + + await helper.tearDown(); +}); |