summaryrefslogtreecommitdiffstats
path: root/comm/calendar/test/ItemEditingHelpers.jsm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--comm/calendar/test/ItemEditingHelpers.jsm681
1 files changed, 681 insertions, 0 deletions
diff --git a/comm/calendar/test/ItemEditingHelpers.jsm b/comm/calendar/test/ItemEditingHelpers.jsm
new file mode 100644
index 0000000000..812f5008cb
--- /dev/null
+++ b/comm/calendar/test/ItemEditingHelpers.jsm
@@ -0,0 +1,681 @@
+/* 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/. */
+
+const EXPORTED_SYMBOLS = [
+ "cancelItemDialog",
+ "formatDate",
+ "formatTime",
+ "menulistSelect",
+ "saveAndCloseItemDialog",
+ "setData",
+];
+
+var { Assert } = ChromeUtils.importESModule("resource://testing-common/Assert.sys.mjs");
+var { TestUtils } = ChromeUtils.importESModule("resource://testing-common/TestUtils.sys.mjs");
+var { BrowserTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+);
+var { sendString, synthesizeKey, synthesizeMouseAtCenter } = ChromeUtils.import(
+ "resource://testing-common/mozmill/EventUtils.jsm"
+);
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+var { CalDateTime } = ChromeUtils.import("resource:///modules/CalDateTime.jsm");
+var { setTimeout } = ChromeUtils.importESModule("resource://gre/modules/Timer.sys.mjs");
+
+function sleep(window, time = 0) {
+ return new Promise(resolve => window.setTimeout(resolve, time));
+}
+
+var dateFormatter = new Services.intl.DateTimeFormat(undefined, { dateStyle: "short" });
+var dateTimeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ dateStyle: "short",
+ timeZone: "UTC",
+});
+var timeFormatter = new Services.intl.DateTimeFormat(undefined, {
+ timeStyle: "short",
+ timeZone: "UTC",
+});
+
+/**
+ * Formats a date for input in a datepicker. Don't use cal.dtz.formatter methods
+ * for this as they use the application locale but datepicker uses the OS locale.
+ *
+ * @param {calIDateTime} date
+ * @returns {string}
+ */
+function formatDate(date) {
+ if (date.isDate) {
+ return dateFormatter.format(cal.dtz.dateTimeToJsDate(date));
+ }
+
+ return dateTimeFormatter.format(cal.dtz.dateTimeToJsDate(date));
+}
+
+/**
+ * Formats a time for input in a timepicker. Don't use cal.dtz.formatter methods
+ * for this as they use the application locale but timepicker uses the OS locale.
+ *
+ * @param {calIDateTime} time
+ * @returns {string}
+ */
+function formatTime(time) {
+ return timeFormatter.format(cal.dtz.dateTimeToJsDate(time));
+}
+
+/**
+ * @callback dialogCallback
+ * @param {Window} - The calendar-event-dialog-recurrence.xhtml dialog.
+ */
+
+/**
+ * Helper function to enter event/task dialog data.
+ *
+ * @param {Window} dialogWindow - Item dialog outer window.
+ * @param {Window} iframeWindow - Item dialog inner iframe.
+ * @param {object} data
+ * @param {string} data.title - Item title.
+ * @param {string} data.location - Item location.
+ * @param {string} data.description - Item description.
+ * @param {string[]} data.categories - Category names to set - leave empty to clear.
+ * @param {string} data.calendar - ID of the calendar the item should be in.
+ * @param {boolean} data.allday
+ * @param {calIDateTime} data.startdate
+ * @param {calIDateTime} data.starttime
+ * @param {calIDateTime} data.enddate
+ * @param {calIDateTime} data.endtime
+ * @param {boolean} data.timezonedisplay - false for hidden, true for shown.
+ * @param {string} data.timezone - String identifying the timezone.
+ * @param {string|dialogCallback} data.repeat - Recurrence value, one of
+ * none/daily/weekly/every.weekday/bi.weekly/monthly/yearly or a callback function to set a
+ * custom value.
+ * @param {calIDateTime} data.repeatuntil
+ * @param {string} data.reminder -
+ * none/0minutes/5minutes/15minutes/30minutes/1hour/2hours/12hours/1day/2days/1week
+ * (Custom is not supported.)
+ * @param {string} data.priority - none/low/normal/high
+ * @param {string} data.privacy - public/confidential/private
+ * @param {string} data.status - none/tentative/confirmed/canceled for events
+ * none/needs-action/in-process/completed/cancelled for tasks
+ * @param {calIDateTime} data.completed - Completion date (tasks only)
+ * @param {string} data.percent - Percentage complete (tasks only)
+ * @param {string} data.freebusy - free/busy
+ * @param {string} data.attachment.add - URL to add
+ * @param {string} data.attachment.remove - Label of url to remove. (without http://)
+ * @param {string} data.attendees.add - Email of attendees to add, comma separated.
+ * @param {string} data.attendees.remove - Email of attendees to remove, comma separated.
+ */
+async function setData(dialogWindow, iframeWindow, data) {
+ function replaceText(input, text) {
+ synthesizeMouseAtCenter(input, {}, iframeWindow);
+ synthesizeKey("a", { accelKey: true }, iframeWindow);
+ sendString(text, iframeWindow);
+ }
+
+ let dialogDocument = dialogWindow.document;
+ let iframeDocument = iframeWindow.document;
+
+ let isEvent = iframeWindow.calendarItem.isEvent();
+ let startPicker = iframeDocument.getElementById(isEvent ? "event-starttime" : "todo-entrydate");
+ let endPicker = iframeDocument.getElementById(isEvent ? "event-endtime" : "todo-duedate");
+
+ let startdateInput = startPicker._datepicker._inputField;
+ let enddateInput = endPicker._datepicker._inputField;
+ let starttimeInput = startPicker._timepicker._inputField;
+ let endtimeInput = endPicker._timepicker._inputField;
+ let completeddateInput = iframeDocument.getElementById("completed-date-picker")._inputField;
+ let untilDateInput = iframeDocument.getElementById("repeat-until-datepicker")._inputField;
+
+ // Wait for input elements' values to be populated.
+ await sleep(iframeWindow, 500);
+
+ // title
+ if (data.title !== undefined) {
+ let titleInput = iframeDocument.getElementById("item-title");
+ replaceText(titleInput, data.title);
+ }
+
+ // location
+ if (data.location !== undefined) {
+ let locationInput = iframeDocument.getElementById("item-location");
+ replaceText(locationInput, data.location);
+ }
+
+ // categories
+ if (data.categories !== undefined) {
+ await setCategories(iframeWindow, data.categories);
+ await sleep(iframeWindow);
+ }
+
+ // calendar
+ if (data.calendar !== undefined) {
+ await menulistSelect(iframeDocument.getElementById("item-calendar"), data.calendar);
+ await sleep(iframeWindow);
+ }
+
+ // all-day
+ if (data.allday !== undefined && isEvent) {
+ let checkbox = iframeDocument.getElementById("event-all-day");
+ if (checkbox.checked != data.allday) {
+ synthesizeMouseAtCenter(checkbox, {}, iframeWindow);
+ }
+ }
+
+ // timezonedisplay
+ if (data.timezonedisplay !== undefined) {
+ let menuitem = dialogDocument.getElementById("options-timezones-menuitem");
+ if (menuitem.getAttribute("checked") != data.timezonedisplay) {
+ synthesizeMouseAtCenter(menuitem, {}, iframeWindow);
+ }
+ }
+
+ // timezone
+ if (data.timezone !== undefined) {
+ await setTimezone(dialogWindow, iframeWindow, data.timezone);
+ }
+
+ // startdate
+ if (
+ data.startdate !== undefined &&
+ (data.startdate instanceof CalDateTime || data.startdate instanceof Ci.calIDateTime)
+ ) {
+ let startdate = formatDate(data.startdate);
+
+ if (!isEvent) {
+ let checkbox = iframeDocument.getElementById("todo-has-entrydate");
+ if (!checkbox.checked) {
+ synthesizeMouseAtCenter(checkbox, {}, iframeWindow);
+ }
+ }
+ replaceText(startdateInput, startdate);
+ }
+
+ // starttime
+ if (
+ data.starttime !== undefined &&
+ (data.starttime instanceof CalDateTime || data.starttime instanceof Ci.calIDateTime)
+ ) {
+ let starttime = formatTime(data.starttime);
+ replaceText(starttimeInput, starttime);
+ await sleep(iframeWindow);
+ }
+
+ // enddate
+ if (
+ data.enddate !== undefined &&
+ (data.enddate instanceof CalDateTime || data.enddate instanceof Ci.calIDateTime)
+ ) {
+ let enddate = formatDate(data.enddate);
+ if (!isEvent) {
+ let checkbox = iframeDocument.getElementById("todo-has-duedate");
+ if (!checkbox.checked) {
+ synthesizeMouseAtCenter(checkbox, {}, iframeWindow);
+ }
+ }
+ replaceText(enddateInput, enddate);
+ }
+
+ // endtime
+ if (
+ data.endtime !== undefined &&
+ (data.endtime instanceof CalDateTime || data.endtime instanceof Ci.calIDateTime)
+ ) {
+ let endtime = formatTime(data.endtime);
+ replaceText(endtimeInput, endtime);
+ }
+
+ // recurrence
+ if (data.repeat !== undefined) {
+ if (typeof data.repeat == "function") {
+ let repeatWindowPromise = BrowserTestUtils.promiseAlertDialog(
+ undefined,
+ "chrome://calendar/content/calendar-event-dialog-recurrence.xhtml",
+ {
+ async callback(recurrenceWindow) {
+ Assert.report(false, undefined, undefined, "Recurrence dialog opened");
+ await TestUtils.waitForCondition(
+ () => Services.focus.activeWindow == recurrenceWindow,
+ "recurrence dialog active"
+ );
+
+ await new Promise(resolve => recurrenceWindow.setTimeout(resolve, 500));
+ await data.repeat(recurrenceWindow);
+ },
+ }
+ );
+ await Promise.all([
+ menulistSelect(iframeDocument.getElementById("item-repeat"), "custom"),
+ repeatWindowPromise,
+ ]);
+ Assert.report(false, undefined, undefined, "Recurrence dialog closed");
+ } else {
+ await menulistSelect(iframeDocument.getElementById("item-repeat"), data.repeat);
+ }
+ }
+ if (
+ data.repeatuntil !== undefined &&
+ (data.repeatuntil instanceof CalDateTime || data.repeatuntil instanceof Ci.calIDateTime)
+ ) {
+ // Only fill in date, when the Datepicker is visible.
+ if (!iframeDocument.getElementById("repeat-untilDate").hidden) {
+ let untildate = formatDate(data.repeatuntil);
+ replaceText(untilDateInput, untildate);
+ }
+ }
+
+ // reminder
+ if (data.reminder !== undefined) {
+ await setReminderMenulist(iframeWindow, data.reminder);
+ }
+
+ // priority
+ if (data.priority !== undefined) {
+ dialogDocument.getElementById(`options-priority-${data.priority}-label`).click();
+ }
+
+ // privacy
+ if (data.privacy !== undefined) {
+ let button = dialogDocument.getElementById("button-privacy");
+ let shownPromise = BrowserTestUtils.waitForEvent(button, "popupshown");
+ synthesizeMouseAtCenter(button, {}, dialogWindow);
+ await shownPromise;
+ let hiddenPromise = BrowserTestUtils.waitForEvent(button, "popuphidden");
+ synthesizeMouseAtCenter(
+ dialogDocument.getElementById(`event-privacy-${data.privacy}-menuitem`),
+ {},
+ dialogWindow
+ );
+ await hiddenPromise;
+ await sleep(iframeWindow);
+ }
+
+ // status
+ if (data.status !== undefined) {
+ if (isEvent) {
+ dialogDocument.getElementById(`options-status-${data.status}-menuitem`).click();
+ } else {
+ await menulistSelect(iframeDocument.getElementById("todo-status"), data.status.toUpperCase());
+ }
+ }
+
+ let currentStatus = iframeDocument.getElementById("todo-status").value;
+
+ // completed on
+ if (
+ data.completed !== undefined &&
+ (data.completed instanceof CalDateTime || data.completed instanceof Ci.calIDateTime) &&
+ !isEvent
+ ) {
+ let completeddate = formatDate(data.completed);
+ if (currentStatus == "COMPLETED") {
+ replaceText(completeddateInput, completeddate);
+ }
+ }
+
+ // percent complete
+ if (
+ data.percent !== undefined &&
+ (currentStatus == "NEEDS-ACTION" ||
+ currentStatus == "IN-PROCESS" ||
+ currentStatus == "COMPLETED")
+ ) {
+ let percentCompleteInput = iframeDocument.getElementById("percent-complete-textbox");
+ replaceText(percentCompleteInput, data.percent);
+ }
+
+ // free/busy
+ if (data.freebusy !== undefined) {
+ dialogDocument.getElementById(`options-freebusy-${data.freebusy}-menuitem`).click();
+ }
+
+ // description
+ if (data.description !== undefined) {
+ synthesizeMouseAtCenter(
+ iframeDocument.getElementById("event-grid-tab-description"),
+ {},
+ iframeWindow
+ );
+ let descField = iframeDocument.getElementById("item-description");
+ replaceText(descField, data.description);
+ }
+
+ // attachment
+ if (data.attachment !== undefined) {
+ if (data.attachment.add !== undefined) {
+ await handleAddingAttachment(dialogWindow, data.attachment.add);
+ }
+ if (data.attachment.remove !== undefined) {
+ synthesizeMouseAtCenter(
+ iframeDocument.getElementById("event-grid-tab-attachments"),
+ {},
+ iframeWindow
+ );
+ let attachmentBox = iframeDocument.getElementById("attachment-link");
+ let attachments = attachmentBox.children;
+ for (let attachment of attachments) {
+ if (attachment.tooltipText.includes(data.attachment.remove)) {
+ synthesizeMouseAtCenter(attachment, {}, iframeWindow);
+ synthesizeKey("VK_DELETE", {}, dialogWindow);
+ }
+ }
+ }
+ }
+
+ // attendees
+ if (data.attendees !== undefined) {
+ // Display attendees Tab.
+ synthesizeMouseAtCenter(
+ iframeDocument.getElementById("event-grid-tab-attendees"),
+ {},
+ iframeWindow
+ );
+ // Make sure no notifications are sent, since handling this dialog is
+ // not working when deleting a parent of a recurring event.
+ let attendeeCheckbox = iframeDocument.getElementById("notify-attendees-checkbox");
+ if (!attendeeCheckbox.disabled && attendeeCheckbox.checked) {
+ synthesizeMouseAtCenter(attendeeCheckbox, {}, iframeWindow);
+ }
+
+ // add
+ if (data.attendees.add !== undefined) {
+ await addAttendees(dialogWindow, iframeWindow, data.attendees.add);
+ }
+ // delete
+ if (data.attendees.remove !== undefined) {
+ await deleteAttendees(iframeWindow, data.attendees.remove);
+ }
+ }
+
+ await sleep(iframeWindow);
+}
+
+/**
+ * Closes an event dialog window, saving the event.
+ *
+ * @param {Window} dialogWindow
+ */
+async function saveAndCloseItemDialog(dialogWindow) {
+ let dialogClosing = BrowserTestUtils.domWindowClosed(dialogWindow);
+ synthesizeMouseAtCenter(
+ dialogWindow.document.getElementById("button-saveandclose"),
+ {},
+ dialogWindow
+ );
+ await dialogClosing;
+ Assert.report(false, undefined, undefined, "Item dialog closed");
+ await new Promise(resolve => setTimeout(resolve));
+}
+
+/**
+ * Closes an event dialog window, discarding any changes.
+ *
+ * @param {Window} dialogWindow
+ */
+function cancelItemDialog(dialogWindow) {
+ synthesizeKey("VK_ESCAPE", {}, dialogWindow);
+}
+
+/**
+ * Select an item in the reminder menulist.
+ * Custom reminders are not supported.
+ *
+ * @param {Window} iframeWindow - The event dialog iframe.
+ * @param {string} id - Identifying string of menuitem id.
+ */
+async function setReminderMenulist(iframeWindow, id) {
+ let iframeDocument = iframeWindow.document;
+ let menulist = iframeDocument.querySelector(".item-alarm");
+ let menuitem = iframeDocument.getElementById(`reminder-${id}-menuitem`);
+
+ Assert.ok(menulist, `<menulist id=${menulist.id}> exists`);
+ Assert.ok(menuitem, `<menuitem id=${id}> exists`);
+
+ menulist.focus();
+
+ synthesizeMouseAtCenter(menulist, {}, iframeWindow);
+ await BrowserTestUtils.waitForEvent(menulist, "popupshown");
+ synthesizeMouseAtCenter(menuitem, {}, iframeWindow);
+ await BrowserTestUtils.waitForEvent(menulist, "popuphidden");
+ await sleep(iframeWindow);
+}
+
+/**
+ * Set the categories in the event-dialog menulist-panel.
+ *
+ * @param {Window} iframeWindow - The event dialog iframe.
+ * @param {string[]} categories - Category names to set - leave empty to clear.
+ */
+async function setCategories(iframeWindow, categories) {
+ let iframeDocument = iframeWindow.document;
+ let menulist = iframeDocument.getElementById("item-categories");
+ let menupopup = iframeDocument.getElementById("item-categories-popup");
+
+ synthesizeMouseAtCenter(menulist, {}, iframeWindow);
+ await BrowserTestUtils.waitForEvent(menupopup, "popupshown");
+
+ // Iterate over categories and check if needed.
+ for (let item of menupopup.children) {
+ if (categories.includes(item.label)) {
+ item.setAttribute("checked", "true");
+ } else {
+ item.removeAttribute("checked");
+ }
+ }
+
+ let hiddenPromise = BrowserTestUtils.waitForEvent(menupopup, "popuphidden");
+ menupopup.hidePopup();
+ await hiddenPromise;
+}
+
+/**
+ * Add an URL attachment.
+ *
+ * @param {Window} dialogWindow - The event dialog.
+ * @param {string} url - URL to be added.
+ */
+async function handleAddingAttachment(dialogWindow, url) {
+ let dialogDocument = dialogWindow.document;
+ let attachButton = dialogDocument.querySelector("#button-url");
+ let menu = dialogDocument.querySelector("#button-attach-menupopup");
+ let menuShowing = BrowserTestUtils.waitForEvent(menu, "popupshown");
+ synthesizeMouseAtCenter(attachButton, {}, dialogWindow);
+ await menuShowing;
+
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog(undefined, undefined, {
+ async callback(attachmentWindow) {
+ Assert.report(false, undefined, undefined, "Attachment dialog opened");
+ await TestUtils.waitForCondition(
+ () => Services.focus.activeWindow == attachmentWindow,
+ "attachment dialog active"
+ );
+
+ let attachmentDocument = attachmentWindow.document;
+ attachmentDocument.getElementById("loginTextbox").value = url;
+ attachmentDocument.querySelector("dialog").getButton("accept").click();
+ },
+ });
+ synthesizeMouseAtCenter(dialogDocument.querySelector("#button-attach-url"), {}, dialogWindow);
+ await dialogPromise;
+ Assert.report(false, undefined, undefined, "Attachment dialog closed");
+ await sleep(dialogWindow);
+}
+
+/**
+ * Add attendees to the event.
+ *
+ * @param {Window} dialogWindow - The event dialog.
+ * @param {Window} iframeWindow - The event dialog iframe.
+ * @param {string} attendeesString - Comma separated list of email-addresses to add.
+ */
+async function addAttendees(dialogWindow, iframeWindow, attendeesString) {
+ let dialogDocument = dialogWindow.document;
+
+ let attendees = attendeesString.split(",");
+ for (let attendee of attendees) {
+ let calAttendee = iframeWindow.attendees.find(aAtt => aAtt.id == `mailto:${attendee}`);
+ // Only add if not already present.
+ if (!calAttendee) {
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog(
+ undefined,
+ "chrome://calendar/content/calendar-event-dialog-attendees.xhtml",
+ {
+ async callback(attendeesWindow) {
+ Assert.report(false, undefined, undefined, "Attendees dialog opened");
+ await TestUtils.waitForCondition(
+ () => Services.focus.activeWindow == attendeesWindow,
+ "attendees dialog active"
+ );
+
+ let attendeesDocument = attendeesWindow.document;
+ Assert.equal(attendeesDocument.activeElement.localName, "input");
+ Assert.equal(
+ attendeesDocument.activeElement.value,
+ "",
+ "active input value should be empty"
+ );
+ sendString(attendee, attendeesWindow);
+ Assert.report(false, undefined, undefined, `Sent attendee ${attendee}`);
+ // Windows needs the focus() call here.
+ attendeesDocument.querySelector("dialog").getButton("accept").focus();
+ synthesizeMouseAtCenter(
+ attendeesDocument.querySelector("dialog").getButton("accept"),
+ {},
+ attendeesWindow
+ );
+ },
+ }
+ );
+ synthesizeMouseAtCenter(dialogDocument.getElementById("button-attendees"), {}, dialogWindow);
+ await dialogPromise;
+ Assert.report(false, undefined, undefined, "Attendees dialog closed");
+ await sleep(iframeWindow);
+ }
+ }
+}
+
+/**
+ * Delete attendees from the event.
+ *
+ * @param {Window} iframeWindow - The event dialog iframe.
+ * @param {string} attendeesString - Comma separated list of email-addresses to delete.
+ */
+async function deleteAttendees(iframeWindow, attendeesString) {
+ let iframeDocument = iframeWindow.document;
+ let menupopup = iframeDocument.getElementById("attendee-popup");
+
+ // Now delete the attendees.
+ let attendees = attendeesString.split(",");
+ for (let attendee of attendees) {
+ let attendeeToDelete = iframeDocument.querySelector(
+ `.attendee-list [attendeeid="mailto:${attendee}"]`
+ );
+ if (attendeeToDelete) {
+ attendeeToDelete.focus();
+ synthesizeMouseAtCenter(attendeeToDelete, { type: "contextmenu" }, iframeWindow);
+ await BrowserTestUtils.waitForEvent(menupopup, "popupshown");
+ menupopup.activateItem(
+ iframeDocument.getElementById("attendee-popup-removeattendee-menuitem")
+ );
+ await BrowserTestUtils.waitForEvent(menupopup, "popuphidden");
+ }
+ }
+ await sleep(iframeWindow);
+}
+
+/**
+ * Set the timezone for the item
+ *
+ * @param {Window} dialogWindow - The event dialog.
+ * @param {Window} iframeWindow - The event dialog iframe.
+ * @param {string} timezone - String identifying the timezone.
+ */
+async function setTimezone(dialogWindow, iframeWindow, timezone) {
+ let dialogDocument = dialogWindow.document;
+ let iframeDocument = iframeWindow.document;
+
+ let menuitem = dialogDocument.getElementById("options-timezones-menuitem");
+ let label = iframeDocument.getElementById("timezone-starttime");
+ let menupopup = iframeDocument.getElementById("timezone-popup");
+ let customMenuitem = iframeDocument.getElementById("timezone-custom-menuitem");
+
+ if (!BrowserTestUtils.is_visible(label)) {
+ menuitem.click();
+ await TestUtils.waitForCondition(
+ () => BrowserTestUtils.is_visible(label),
+ "Timezone label should become visible"
+ );
+ }
+
+ await TestUtils.waitForCondition(() => !label.disabled, "Tiemzone label should become enabled");
+
+ let shownPromise = BrowserTestUtils.waitForEvent(menupopup, "popupshown");
+ let dialogPromise = BrowserTestUtils.promiseAlertDialog(
+ undefined,
+ "chrome://calendar/content/calendar-event-dialog-timezone.xhtml",
+ {
+ async callback(timezoneWindow) {
+ Assert.report(false, undefined, undefined, "Timezone dialog opened");
+ await TestUtils.waitForCondition(
+ () => Services.focus.activeWindow == timezoneWindow,
+ "timezone dialog active"
+ );
+
+ let timezoneDocument = timezoneWindow.document;
+ let timezoneMenulist = timezoneDocument.getElementById("timezone-menulist");
+ let timezoneMenuitem = timezoneMenulist.querySelector(`[value="${timezone}"]`);
+
+ let popupshown = BrowserTestUtils.waitForEvent(timezoneMenulist, "popupshown");
+ synthesizeMouseAtCenter(timezoneMenulist, {}, timezoneWindow);
+ await popupshown;
+
+ timezoneMenuitem.scrollIntoView();
+
+ let popuphidden = BrowserTestUtils.waitForEvent(timezoneMenulist, "popuphidden");
+ synthesizeMouseAtCenter(timezoneMenuitem, {}, timezoneWindow);
+ await popuphidden;
+
+ synthesizeMouseAtCenter(
+ timezoneDocument.querySelector("dialog").getButton("accept"),
+ {},
+ timezoneWindow
+ );
+ },
+ }
+ );
+
+ synthesizeMouseAtCenter(label, {}, iframeWindow);
+ await shownPromise;
+
+ synthesizeMouseAtCenter(customMenuitem, {}, iframeWindow);
+ await dialogPromise;
+ Assert.report(false, undefined, undefined, "Timezone dialog closed");
+
+ await new Promise(resolve => iframeWindow.setTimeout(resolve, 500));
+}
+
+/**
+ * Selects an item from a menulist.
+ *
+ * @param {Element} menulist
+ * @param {string} value
+ */
+async function menulistSelect(menulist, value) {
+ let win = menulist.ownerGlobal;
+ Assert.ok(menulist, `<menulist id=${menulist.id}> exists`);
+ let menuitem = menulist.querySelector(`menupopup > menuitem[value='${value}']`);
+ Assert.ok(menuitem, `<menuitem value=${value}> exists`);
+
+ menulist.focus();
+
+ let shownPromise = BrowserTestUtils.waitForEvent(menulist, "popupshown");
+ synthesizeMouseAtCenter(menulist, {}, win);
+ await shownPromise;
+
+ let hiddenPromise = BrowserTestUtils.waitForEvent(menulist, "popuphidden");
+ synthesizeMouseAtCenter(menuitem, {}, win);
+ await hiddenPromise;
+
+ await new Promise(resolve => win.setTimeout(resolve));
+ Assert.equal(menulist.value, value);
+}