summaryrefslogtreecommitdiffstats
path: root/toolkit/content/tests/browser/browser_datetime_datepicker.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /toolkit/content/tests/browser/browser_datetime_datepicker.js
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/content/tests/browser/browser_datetime_datepicker.js')
-rw-r--r--toolkit/content/tests/browser/browser_datetime_datepicker.js791
1 files changed, 791 insertions, 0 deletions
diff --git a/toolkit/content/tests/browser/browser_datetime_datepicker.js b/toolkit/content/tests/browser/browser_datetime_datepicker.js
new file mode 100644
index 0000000000..a354dda0ec
--- /dev/null
+++ b/toolkit/content/tests/browser/browser_datetime_datepicker.js
@@ -0,0 +1,791 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const MONTH_YEAR = ".month-year",
+ DAYS_VIEW = ".days-view",
+ BTN_PREV_MONTH = ".prev",
+ BTN_NEXT_MONTH = ".next";
+const DATE_FORMAT = new Intl.DateTimeFormat("en-US", {
+ year: "numeric",
+ month: "long",
+ timeZone: "UTC",
+}).format;
+const DATE_FORMAT_LOCAL = new Intl.DateTimeFormat("en-US", {
+ year: "numeric",
+ month: "long",
+}).format;
+
+// Create a list of abbreviations for calendar class names
+const W = "weekend",
+ O = "outside",
+ S = "selection",
+ R = "out-of-range",
+ T = "today",
+ P = "off-step";
+
+// Calendar classlist for 2016-12. Used to verify the classNames are correct.
+const calendarClasslist_201612 = [
+ [W, O],
+ [O],
+ [O],
+ [O],
+ [],
+ [],
+ [W],
+ [W],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [W],
+ [W],
+ [],
+ [],
+ [],
+ [S],
+ [],
+ [W],
+ [W],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [W],
+ [W],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [W],
+ [W, O],
+ [O],
+ [O],
+ [O],
+ [O],
+ [O],
+ [W, O],
+];
+
+function getCalendarText() {
+ return helper.getChildren(DAYS_VIEW).map(child => child.textContent);
+}
+
+function getCalendarClassList() {
+ return helper
+ .getChildren(DAYS_VIEW)
+ .map(child => Array.from(child.classList));
+}
+
+function mergeArrays(a, b) {
+ return a.map((classlist, index) => classlist.concat(b[index]));
+}
+
+async function verifyPickerPosition(browsingContext, inputId) {
+ let inputRect = await SpecialPowers.spawn(
+ browsingContext,
+ [inputId],
+ async function(inputIdChild) {
+ let rect = content.document
+ .getElementById(inputIdChild)
+ .getBoundingClientRect();
+ return {
+ left: content.mozInnerScreenX + rect.left,
+ bottom: content.mozInnerScreenY + rect.bottom,
+ };
+ }
+ );
+
+ function is_close(got, exp, msg) {
+ // on some platforms we see differences of a fraction of a pixel - so
+ // allow any difference of < 1 pixels as being OK.
+ ok(
+ Math.abs(got - exp) < 1,
+ msg + ": " + got + " should be equal(-ish) to " + exp
+ );
+ }
+ is_close(helper.panel.screenX, inputRect.left, "datepicker x position");
+ is_close(helper.panel.screenY, inputRect.bottom, "datepicker y position");
+}
+
+let helper = new DateTimeTestHelper();
+
+registerCleanupFunction(() => {
+ helper.cleanup();
+});
+
+/**
+ * Test that date picker opens to today's date when input field is blank
+ */
+add_task(async function test_datepicker_today() {
+ const date = new Date();
+
+ await helper.openPicker("data:text/html, <input type='date'>");
+
+ if (date.getMonth() === new Date().getMonth()) {
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT_LOCAL(date)
+ );
+ } else {
+ Assert.ok(
+ true,
+ "Skipping datepicker today test if month changes when opening picker."
+ );
+ }
+
+ await helper.tearDown();
+});
+
+/**
+ * Test that date picker opens to the correct month, with calendar days
+ * displayed correctly, given a date value is set.
+ */
+add_task(async function test_datepicker_open() {
+ const inputValue = "2016-12-15";
+
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}">`
+ );
+
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT(new Date(inputValue))
+ );
+ Assert.deepEqual(
+ getCalendarText(),
+ [
+ "27",
+ "28",
+ "29",
+ "30",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "17",
+ "18",
+ "19",
+ "20",
+ "21",
+ "22",
+ "23",
+ "24",
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "30",
+ "31",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ ],
+ "2016-12"
+ );
+ Assert.deepEqual(
+ getCalendarClassList(),
+ calendarClasslist_201612,
+ "2016-12 classNames"
+ );
+
+ await helper.tearDown();
+});
+
+/**
+ * Ensure picker closes when focus moves to a different input.
+ */
+add_task(async function test_datepicker_focus_change() {
+ await helper.openPicker(
+ `data:text/html,<input id=date type=date><input id=other>`
+ );
+ let browser = helper.tab.linkedBrowser;
+ await verifyPickerPosition(browser, "date");
+
+ isnot(helper.panel.hidden, "Panel should be visible");
+
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.querySelector("#other").focus();
+ });
+
+ // NOTE: Would be cool to be able to use promisePickerClosed(), but
+ // popuphidden isn't really triggered for this code path it seems, so oh
+ // well.
+ await BrowserTestUtils.waitForCondition(
+ () => helper.panel.hidden,
+ "waiting for close"
+ );
+ await helper.tearDown();
+});
+
+/**
+ * Ensure picker opens and closes with key bindings appropriately.
+ */
+/*
+disabled for bug 1676078
+add_task(async function test_datepicker_keyboard_open() {
+ 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;
+ await verifyPickerPosition(browser, "date");
+
+ BrowserTestUtils.synthesizeKey(" ", {}, browser);
+
+ await BrowserTestUtils.waitForCondition(
+ () => helper.panel.hidden,
+ "waiting for close"
+ );
+
+ BrowserTestUtils.synthesizeKey(" ", {}, browser);
+
+ await BrowserTestUtils.waitForCondition(
+ () => !helper.panel.hidden,
+ "waiting for open"
+ );
+
+ // NOTE: After the click, 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("VK_DOWN", {}, browser);
+
+ // It'd be good to use something else than waitForCondition for this but
+ // there's no exposed event atm when the value changes from the child.
+ await BrowserTestUtils.waitForCondition(() => {
+ return (
+ helper.getElement(MONTH_YEAR).textContent ==
+ DATE_FORMAT(new Date(prevMonth))
+ );
+ }, "Should update date when updating months");
+
+ await helper.tearDown();
+});
+*/
+
+/**
+ * When the prev month button is clicked, calendar should display the dates for
+ * the previous month.
+ */
+add_task(async function test_datepicker_prev_month_btn() {
+ const inputValue = "2016-12-15";
+ const prevMonth = "2016-11-01";
+
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}">`
+ );
+ helper.click(helper.getElement(BTN_PREV_MONTH));
+
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT(new Date(prevMonth))
+ );
+ Assert.deepEqual(
+ getCalendarText(),
+ [
+ "30",
+ "31",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "17",
+ "18",
+ "19",
+ "20",
+ "21",
+ "22",
+ "23",
+ "24",
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "30",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ ],
+ "2016-11"
+ );
+
+ await helper.tearDown();
+});
+
+/**
+ * When the next month button is clicked, calendar should display the dates for
+ * the next month.
+ */
+add_task(async function test_datepicker_next_month_btn() {
+ const inputValue = "2016-12-15";
+ const nextMonth = "2017-01-01";
+
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}">`
+ );
+ helper.click(helper.getElement(BTN_NEXT_MONTH));
+
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT(new Date(nextMonth))
+ );
+ Assert.deepEqual(
+ getCalendarText(),
+ [
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "30",
+ "31",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "17",
+ "18",
+ "19",
+ "20",
+ "21",
+ "22",
+ "23",
+ "24",
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "30",
+ "31",
+ "1",
+ "2",
+ "3",
+ "4",
+ ],
+ "2017-01"
+ );
+
+ await helper.tearDown();
+});
+
+/**
+ * When a date on the calendar is clicked, date picker should close and set
+ * value to the input box.
+ */
+add_task(async function test_datepicker_clicked() {
+ const inputValue = "2016-12-15";
+ const firstDayOnCalendar = "2016-11-27";
+
+ await helper.openPicker(
+ `data:text/html, <input id="date" type="date" value="${inputValue}">`
+ );
+
+ let browser = helper.tab.linkedBrowser;
+ await verifyPickerPosition(browser, "date");
+
+ // Click the first item (top-left corner) of the calendar
+ let promise = BrowserTestUtils.waitForContentEvent(
+ helper.tab.linkedBrowser,
+ "input"
+ );
+ helper.click(helper.getElement(DAYS_VIEW).children[0]);
+ await promise;
+
+ let value = await SpecialPowers.spawn(
+ browser,
+ [],
+ () => content.document.querySelector("input").value
+ );
+ Assert.equal(value, firstDayOnCalendar);
+
+ await helper.tearDown();
+});
+
+/**
+ * Ensure that the datepicker popop appears correctly positioned when
+ * the input field has been transformed.
+ */
+add_task(async function test_datepicker_transformed_position() {
+ const inputValue = "2016-12-15";
+
+ const style =
+ "transform: translateX(7px) translateY(13px); border-top: 2px; border-left: 5px; margin: 30px;";
+ const iframeContent = `<input id="date" type="date" value="${inputValue}" style="${style}">`;
+ await helper.openPicker(
+ "data:text/html,<iframe id='iframe' src='http://example.net/document-builder.sjs?html=" +
+ encodeURI(iframeContent) +
+ "'>",
+ true
+ );
+
+ let bc = helper.tab.linkedBrowser.browsingContext.children[0];
+ await verifyPickerPosition(bc, "date");
+
+ await helper.tearDown();
+});
+
+/**
+ * Make sure picker is in correct state when it is reopened.
+ */
+add_task(async function test_datepicker_reopen_state() {
+ const inputValue = "2016-12-15";
+ const nextMonth = "2017-01-01";
+
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}">`
+ );
+ // Navigate to the next month but does not commit the change
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT(new Date(inputValue))
+ );
+ helper.click(helper.getElement(BTN_NEXT_MONTH));
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT(new Date(nextMonth))
+ );
+ EventUtils.synthesizeKey("VK_ESCAPE", {}, window);
+
+ // Ensures the picker opens to the month of the input value
+ await BrowserTestUtils.synthesizeMouseAtCenter(
+ "input",
+ {},
+ gBrowser.selectedBrowser
+ );
+ await helper.waitForPickerReady();
+ Assert.equal(
+ helper.getElement(MONTH_YEAR).textContent,
+ DATE_FORMAT(new Date(inputValue))
+ );
+
+ await helper.tearDown();
+});
+
+/**
+ * When min and max attributes are set, calendar should show some dates as
+ * out-of-range.
+ */
+add_task(async function test_datepicker_min_max() {
+ const inputValue = "2016-12-15";
+ const inputMin = "2016-12-05";
+ const inputMax = "2016-12-25";
+
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}" min="${inputMin}" max="${inputMax}">`
+ );
+
+ Assert.deepEqual(
+ getCalendarClassList(),
+ mergeArrays(calendarClasslist_201612, [
+ // R denotes out-of-range
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ [R],
+ ]),
+ "2016-12 with min & max"
+ );
+
+ await helper.tearDown();
+});
+
+/**
+ * When step attribute is set, calendar should show some dates as off-step.
+ */
+add_task(async function test_datepicker_step() {
+ const inputValue = "2016-12-15";
+ const inputStep = "5";
+
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}" step="${inputStep}">`
+ );
+
+ Assert.deepEqual(
+ getCalendarClassList(),
+ mergeArrays(calendarClasslist_201612, [
+ // P denotes off-step
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ [P],
+ [],
+ [P],
+ [P],
+ [P],
+ ]),
+ "2016-12 with step"
+ );
+
+ await helper.tearDown();
+});
+
+add_task(async function test_datepicker_abs_min() {
+ const inputValue = "0001-01-01";
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}">`
+ );
+
+ Assert.deepEqual(
+ getCalendarText(),
+ [
+ "",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "14",
+ "15",
+ "16",
+ "17",
+ "18",
+ "19",
+ "20",
+ "21",
+ "22",
+ "23",
+ "24",
+ "25",
+ "26",
+ "27",
+ "28",
+ "29",
+ "30",
+ "31",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ ],
+ "0001-01"
+ );
+
+ await helper.tearDown();
+});
+
+// This test checks if the change event is considered as user input event.
+add_task(async function test_datepicker_handling_user_input() {
+ await helper.openPicker(`data:text/html, <input type="date">`);
+
+ let changeEventPromise = SpecialPowers.spawn(
+ helper.tab.linkedBrowser,
+ [],
+ async () => {
+ let input = content.document.querySelector("input");
+ await ContentTaskUtils.waitForEvent(input, "change", false, e => {
+ ok(
+ content.window.windowUtils.isHandlingUserInput,
+ "isHandlingUserInput should be true"
+ );
+ return true;
+ });
+ }
+ );
+ // Click the first item (top-left corner) of the calendar
+ helper.click(helper.getElement(DAYS_VIEW).children[0]);
+ await changeEventPromise;
+
+ await helper.tearDown();
+});
+
+add_task(async function test_datepicker_abs_max() {
+ const inputValue = "275760-09-13";
+ await helper.openPicker(
+ `data:text/html, <input type="date" value="${inputValue}">`
+ );
+
+ Assert.deepEqual(
+ getCalendarText(),
+ [
+ "31",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "10",
+ "11",
+ "12",
+ "13",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ],
+ "275760-09"
+ );
+
+ await helper.tearDown();
+});