1202 строки
39 KiB
JavaScript
1202 строки
39 KiB
JavaScript
/* 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/. */
|
|
|
|
import * as EventUtils from "resource://testing-common/mail/EventUtils.sys.mjs";
|
|
import { BrowserTestUtils } from "resource://testing-common/BrowserTestUtils.sys.mjs";
|
|
import { TestUtils } from "resource://testing-common/TestUtils.sys.mjs";
|
|
import { Assert } from "resource://testing-common/Assert.sys.mjs";
|
|
import {
|
|
cancelItemDialog,
|
|
saveAndCloseItemDialog,
|
|
setData,
|
|
} from "resource://testing-common/calendar/ItemEditingHelpers.sys.mjs";
|
|
|
|
import { cal } from "resource:///modules/calendar/calUtils.sys.mjs";
|
|
|
|
async function clickAndWait(win, button) {
|
|
EventUtils.synthesizeMouseAtCenter(button, { clickCount: 1 }, win);
|
|
await new Promise(resolve => win.setTimeout(resolve));
|
|
}
|
|
|
|
/**
|
|
* @typedef EditItemAtResult
|
|
* @property {Window} dialogWindow - The window of the dialog.
|
|
* @property {Document} dialogDocument - The document of the dialog window.
|
|
* @property {Window} iframeWindow - The contentWindow property of the embedded
|
|
* iframe.
|
|
* @property {Document} iframeDocument - The contentDocument of the embedded
|
|
* iframe.
|
|
*/
|
|
|
|
/**
|
|
* Helper class for testing the day view of the calendar.
|
|
*/
|
|
class CalendarDayViewTestUtils {
|
|
#helper = new CalendarWeekViewTestUtils("#day-view");
|
|
|
|
/**
|
|
* Provides the column container element for the displayed day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
*
|
|
* @returns {HTMLElement} - The column container element.
|
|
*/
|
|
getColumnContainer(win) {
|
|
return this.#helper.getColumnContainer(win, 1);
|
|
}
|
|
|
|
/**
|
|
* Provides the element containing the formatted date for the displayed day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
*
|
|
* @returns {HTMLElement} - The column heading container.
|
|
*/
|
|
getColumnHeading(win) {
|
|
return this.#helper.getColumnHeading(win, 1);
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-event-column for the day displayed.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
*
|
|
* @returns {MozCalendarEventColumn} - The column.
|
|
*/
|
|
getEventColumn(win) {
|
|
return this.#helper.getEventColumn(win, 1);
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-event-box elements for the day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
*
|
|
* @returns {MozCalendarEventBox[]} - The event boxes.
|
|
*/
|
|
getEventBoxes(win) {
|
|
return this.#helper.getEventBoxes(win, 1);
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-event-box at "index" located in the event column for
|
|
* the day displayed.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {MozCalendarEventBox|undefined} - The event box, if it exists.
|
|
*/
|
|
getEventBoxAt(win, index) {
|
|
return this.#helper.getEventBoxAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Provides the .multiday-hour-box element for the specified hour. This
|
|
* element can be double clicked to create a new event at that hour.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} hour - Must be between 0-23.
|
|
*
|
|
* @returns {XULElement} - The hour box.
|
|
*/
|
|
getHourBoxAt(win, hour) {
|
|
return this.#helper.getHourBoxAt(win, 1, hour);
|
|
}
|
|
|
|
/**
|
|
* Provides the all-day header, which can be double clicked to create a new
|
|
* all-day event.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
*
|
|
* @returns {CalendarHeaderContainer} - The all-day header.
|
|
*/
|
|
getAllDayHeader(win) {
|
|
return this.#helper.getAllDayHeader(win, 1);
|
|
}
|
|
|
|
/**
|
|
* Provides the all-day calendar-editable-item located at index for the
|
|
* current day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which item to select (1-based).
|
|
*
|
|
* @returns {MozCalendarEditableItem|undefined} - The all-day item, if it
|
|
* exists.
|
|
*/
|
|
getAllDayItemAt(win, index) {
|
|
return this.#helper.getAllDayItemAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Waits for the calendar-event-box at "index", located in the event
|
|
* column for the day displayed to appear.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which item to select (1-based).
|
|
*
|
|
* @returns {Promise<MozCalendarEventBox>} - The event box.
|
|
*/
|
|
async waitForEventBoxAt(win, index) {
|
|
return this.#helper.waitForEventBoxAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Waits for the calendar-event-box at "index", located in the event column
|
|
* for the current day to disappear.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates the event box (1-based).
|
|
*/
|
|
async waitForNoEventBoxAt(win, index) {
|
|
return this.#helper.waitForNoEventBoxAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Wait for the all-day calendar-editable-item for the day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which item to select (1-based).
|
|
*
|
|
* @returns {Promise<MozCalendarEditableItem>} - The all-day item.
|
|
*/
|
|
async waitForAllDayItemAt(win, index) {
|
|
return this.#helper.waitForAllDayItemAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for viewing for the event box located at the
|
|
* specified index.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which event to select.
|
|
*
|
|
* @returns {Promise<Window>} - The summary event dialog window.
|
|
*/
|
|
async viewEventAt(win, index) {
|
|
return this.#helper.viewEventAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing for the event box located at the
|
|
* specified index.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which event to select.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editEventAt(win, index) {
|
|
return this.#helper.editEventAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing for a single occurrence of the event
|
|
* box located at the specified index.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editEventOccurrenceAt(win, index) {
|
|
return this.#helper.editEventOccurrenceAt(win, 1, index);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing all occurrences of the event box
|
|
* located at the specified index.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editEventOccurrencesAt(win, index) {
|
|
return this.#helper.editEventOccurrencesAt(win, 1, index);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper class for testing the week view of the calendar.
|
|
*/
|
|
class CalendarWeekViewTestUtils {
|
|
constructor(rootSelector = "#week-view") {
|
|
this.rootSelector = rootSelector;
|
|
}
|
|
|
|
/**
|
|
* Provides the column container element for the day specified.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
*
|
|
* @throws If the day parameter is out of range.
|
|
* @returns {HTMLElement} - The column container element.
|
|
*/
|
|
getColumnContainer(win, day) {
|
|
if (day < 1 || day > 7) {
|
|
throw new Error(
|
|
`Invalid parameter to #getColumnContainer(): expected day=1-7, got day=${day}.`
|
|
);
|
|
}
|
|
|
|
const containers = win.document.documentElement.querySelectorAll(
|
|
`${this.rootSelector} .day-column-container`
|
|
);
|
|
return containers[day - 1];
|
|
}
|
|
|
|
/**
|
|
* Provides the element containing the formatted date for the day specified.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
*
|
|
* @throws If the day parameter is out of range.
|
|
* @returns {HTMLElement} - The column heading container element.
|
|
*/
|
|
getColumnHeading(win, day) {
|
|
const container = this.getColumnContainer(win, day);
|
|
return container.querySelector(".day-column-heading");
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-event-column for the day specified.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7
|
|
*
|
|
* @throws - If the day parameter is out of range.
|
|
* @returns {MozCalendarEventColumn} - The column.
|
|
*/
|
|
getEventColumn(win, day) {
|
|
const container = this.getColumnContainer(win, day);
|
|
return container.querySelector("calendar-event-column");
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-event-box elements for the day specified.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
*
|
|
* @returns {MozCalendarEventBox[]} - The event boxes.
|
|
*/
|
|
getEventBoxes(win, day) {
|
|
const container = this.getColumnContainer(win, day);
|
|
return container.querySelectorAll(".multiday-events-list calendar-event-box");
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-event-box at "index" located in the event column for
|
|
* the specified day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {MozCalendarEventBox|undefined} - The event box, if it exists.
|
|
*/
|
|
getEventBoxAt(win, day, index) {
|
|
return this.getEventBoxes(win, day)[index - 1];
|
|
}
|
|
|
|
/**
|
|
* Provides the .multiday-hour-box element for the specified hour. This
|
|
* element can be double clicked to create a new event at that hour.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Day of the week, between 1-7.
|
|
* @param {number} hour - Must be between 0-23.
|
|
*
|
|
* @throws If the day or hour are out of range.
|
|
* @returns {XULElement} - The hour box.
|
|
*/
|
|
getHourBoxAt(win, day, hour) {
|
|
const container = this.getColumnContainer(win, day);
|
|
return container.querySelectorAll(".multiday-hour-box")[hour];
|
|
}
|
|
|
|
/**
|
|
* Provides the all-day header, which can be double clicked to create a new
|
|
* all-day event for the specified day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Day of the week, between 1-7.
|
|
*
|
|
* @throws If the day is out of range.
|
|
* @returns {CalendarHeaderContainer} - The all-day header.
|
|
*/
|
|
getAllDayHeader(win, day) {
|
|
const container = this.getColumnContainer(win, day);
|
|
return container.querySelector("calendar-header-container");
|
|
}
|
|
|
|
/**
|
|
* Provides the all-day calendar-editable-item located at "index" for the
|
|
* specified day.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Day of the week, between 1-7.
|
|
* @param {number} index - Indicates which item to select (starting from 1).
|
|
*
|
|
* @throws If the day or index are out of range.
|
|
* @returns {MozCalendarEditableItem|undefined} - The all-day item, if it
|
|
* exists.
|
|
*/
|
|
getAllDayItemAt(win, day, index) {
|
|
const allDayHeader = this.getAllDayHeader(win, day);
|
|
return allDayHeader.querySelectorAll("calendar-editable-item")[index - 1];
|
|
}
|
|
|
|
/**
|
|
* Waits for the calendar-event-box at "index", located in the event column
|
|
* for the day specified to appear.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Day of the week, between 1-7.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {MozCalendarEventBox} - The event box.
|
|
*/
|
|
async waitForEventBoxAt(win, day, index) {
|
|
return TestUtils.waitForCondition(
|
|
() => this.getEventBoxAt(win, day, index),
|
|
`calendar-event-box at day=${day}, index=${index} did not appear in time`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Waits until the calendar-event-box at "index", located in the event column
|
|
* for the day specified disappears.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Day of the week, between 1-7.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*/
|
|
async waitForNoEventBoxAt(win, day, index) {
|
|
await TestUtils.waitForCondition(
|
|
() => !this.getEventBoxAt(win, day, index),
|
|
`calendar-event-box at day=${day}, index=${index} still present`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Waits for the all-day calendar-editable-item at "index", located in the
|
|
* event column for the day specified to appear.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Day of the week, between 1-7.
|
|
* @param {number} index - Indicates which item to select (starting from 1).
|
|
*
|
|
* @returns {Promise<MozCalendarEditableItem>} - The all-day item.
|
|
*/
|
|
async waitForAllDayItemAt(win, day, index) {
|
|
return TestUtils.waitForCondition(
|
|
() => this.getAllDayItemAt(win, day, index),
|
|
`All-day calendar-editable-item at day=${day}, index=${index} did not appear in time`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for viewing for the event box located at the
|
|
* specified parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which event to select.
|
|
*
|
|
* @returns {Promise<Window>} - The summary event dialog window.
|
|
*/
|
|
async viewEventAt(win, day, index) {
|
|
const item = await this.waitForEventBoxAt(win, day, index);
|
|
return CalendarTestUtils.viewItem(win, item);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing for the event box located at the
|
|
* specified parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which event to select.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editEventAt(win, day, index) {
|
|
const item = await this.waitForEventBoxAt(win, day, index);
|
|
return CalendarTestUtils.editItem(win, item);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing for a single occurrence of the event
|
|
* box located at the specified parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editEventOccurrenceAt(win, day, index) {
|
|
const item = await this.waitForEventBoxAt(win, day, index);
|
|
return CalendarTestUtils.editItemOccurrence(win, item);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing all occurrences of the event box
|
|
* located at the specified parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which event box to select.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editEventOccurrencesAt(win, day, index) {
|
|
const item = await this.waitForEventBoxAt(win, day, index);
|
|
return CalendarTestUtils.editItemOccurrences(win, item);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper class for testing the multiweek and month views of the calendar.
|
|
*/
|
|
class CalendarMonthViewTestUtils {
|
|
/**
|
|
* @param {string} rootSelector
|
|
*/
|
|
constructor(rootSelector) {
|
|
this.rootSelector = rootSelector;
|
|
}
|
|
|
|
/**
|
|
* Provides the calendar-month-day-box element located at the specified day,
|
|
* week combination.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6. The cap may be as low as 1
|
|
* depending on the user preference calendar.weeks.inview.
|
|
* @param {number} day - Must be between 1-7.
|
|
*
|
|
* @throws If the day or week parameters are out of range.
|
|
* @returns {MozCalendarMonthDayBox}
|
|
*/
|
|
getDayBox(win, week, day) {
|
|
if (!(week >= 1 && week <= 6 && day >= 1 && day <= 7)) {
|
|
throw new Error(
|
|
`Invalid parameters to getDayBox(): ` +
|
|
`expected week=1-6, day=1-7, got week=${week}, day=${day},`
|
|
);
|
|
}
|
|
|
|
return win.document.documentElement.querySelector(
|
|
`${this.rootSelector} .monthbody > tr:nth-of-type(${week}) >
|
|
td:nth-of-type(${day}) > calendar-month-day-box`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get the calendar-month-day-box-item located in the specified day box, at
|
|
* the target index.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which item to select.
|
|
*
|
|
* @throws If the index, day or week parameters are out of range.
|
|
* @returns {MozCalendarMonthDayBoxItem}
|
|
*/
|
|
getItemAt(win, week, day, index) {
|
|
if (!(index >= 1)) {
|
|
throw new Error(`Invalid parameters to getItemAt(): expected index>=1, got index=${index}.`);
|
|
}
|
|
|
|
const dayBox = this.getDayBox(win, week, day);
|
|
return dayBox.querySelector(`li:nth-of-type(${index}) calendar-month-day-box-item`);
|
|
}
|
|
|
|
/**
|
|
* Waits for the calendar-month-day-box-item at "index", located in the
|
|
* specified week,day combination to appear.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which item to select.
|
|
*
|
|
* @returns {MozCalendarMonthDayBoxItem}
|
|
*/
|
|
async waitForItemAt(win, week, day, index) {
|
|
return TestUtils.waitForCondition(
|
|
() => this.getItemAt(win, week, day, index),
|
|
`calendar-month-day-box-item at week=${week}, day=${day}, index=${index} did not appear in time`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Waits for the calendar-month-day-box-item at "index", located in the
|
|
* specified week,day combination to disappear.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates the item that should no longer be present.
|
|
*/
|
|
async waitForNoItemAt(win, week, day, index) {
|
|
await TestUtils.waitForCondition(
|
|
() => !this.getItemAt(win, week, day, index),
|
|
`calendar-month-day-box-item at week=${week}, day=${day}, index=${index} still present`
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for viewing for the item located at the specified
|
|
* parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which item to select.
|
|
*
|
|
* @returns {Window} - The summary event dialog window.
|
|
*/
|
|
async viewItemAt(win, week, day, index) {
|
|
const item = await this.waitForItemAt(win, week, day, index);
|
|
return CalendarTestUtils.viewItem(win, item);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing for the item located at the specified
|
|
* parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which item to select.
|
|
*
|
|
* @returns {EditItemAtResult}
|
|
*/
|
|
async editItemAt(win, week, day, index) {
|
|
const item = await this.waitForItemAt(win, week, day, index);
|
|
return CalendarTestUtils.editItem(win, item);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing for a single occurrence of the item
|
|
* located at the specified parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which item to select.
|
|
*
|
|
* @returns {EditItemAtResult}
|
|
*/
|
|
async editItemOccurrenceAt(win, week, day, index) {
|
|
const item = await this.waitForItemAt(win, week, day, index);
|
|
return CalendarTestUtils.editItemOccurrence(win, item);
|
|
}
|
|
|
|
/**
|
|
* Opens the event dialog for editing all occurrences of the item
|
|
* located at the specified parameters.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} week - Must be between 1-6.
|
|
* @param {number} day - Must be between 1-7.
|
|
* @param {number} index - Indicates which item to select.
|
|
*
|
|
* @returns {EditItemAtResult}
|
|
*/
|
|
async editItemOccurrencesAt(win, week, day, index) {
|
|
const item = await this.waitForItemAt(win, week, day, index);
|
|
return CalendarTestUtils.editItemOccurrences(win, item);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Non-mozmill calendar helper utility.
|
|
*/
|
|
export const CalendarTestUtils = {
|
|
/**
|
|
* Helper methods for item editing.
|
|
*/
|
|
items: {
|
|
cancelItemDialog,
|
|
saveAndCloseItemDialog,
|
|
setData,
|
|
},
|
|
|
|
/**
|
|
* Helpers specific to the day view.
|
|
*/
|
|
dayView: new CalendarDayViewTestUtils(),
|
|
|
|
/**
|
|
* Helpers specific to the week view.
|
|
*/
|
|
weekView: new CalendarWeekViewTestUtils(),
|
|
|
|
/**
|
|
* Helpers specific to the multiweek view.
|
|
*/
|
|
multiweekView: new CalendarMonthViewTestUtils("#multiweek-view"),
|
|
|
|
/**
|
|
* Helpers specific to the month view.
|
|
*/
|
|
monthView: new CalendarMonthViewTestUtils("#month-view"),
|
|
|
|
/**
|
|
* Dedent the template string tagged with this function to make indented data
|
|
* easier to read. Usage:
|
|
*
|
|
* let data = dedent`
|
|
* This is indented data it will be unindented so that the first line has
|
|
* no leading spaces and the second is indented by two spaces.
|
|
* `;
|
|
*
|
|
* @param strings The string fragments from the template string
|
|
* @param ...values The interpolated values
|
|
* @returns The interpolated, dedented string
|
|
*/
|
|
dedent(strings, ...values) {
|
|
const parts = [];
|
|
// Perform variable interpolation
|
|
let minIndent = Infinity;
|
|
for (const [i, string] of strings.entries()) {
|
|
const innerparts = string.split("\n");
|
|
if (i == 0) {
|
|
innerparts.shift();
|
|
}
|
|
if (i == strings.length - 1) {
|
|
innerparts.pop();
|
|
}
|
|
for (const [j, ip] of innerparts.entries()) {
|
|
const match = ip.match(/^(\s*)\S*/);
|
|
if (j != 0) {
|
|
minIndent = Math.min(minIndent, match[1].length);
|
|
}
|
|
}
|
|
parts.push(innerparts);
|
|
}
|
|
|
|
return parts
|
|
.map((part, i) => {
|
|
return (
|
|
part
|
|
.map((line, j) => {
|
|
return j == 0 && i > 0 ? line : line.substr(minIndent);
|
|
})
|
|
.join("\n") + (i < values.length ? values[i] : "")
|
|
);
|
|
})
|
|
.join("");
|
|
},
|
|
|
|
/**
|
|
* Creates and registers a new calendar with the calendar manager. The
|
|
* created calendar will be set as the default calendar.
|
|
*
|
|
* @param {string} - name
|
|
* @param {string} - type
|
|
*
|
|
* @returns {calICalendar}
|
|
*/
|
|
createCalendar(name = "Test", type = "storage") {
|
|
const calendar = cal.manager.createCalendar(
|
|
type,
|
|
Services.io.newURI(`moz-${type}-calendar://`)
|
|
);
|
|
calendar.name = name;
|
|
calendar.setProperty("calendar-main-default", true);
|
|
cal.manager.registerCalendar(calendar);
|
|
return calendar;
|
|
},
|
|
|
|
/**
|
|
* Convenience method for removing a calendar using its proxy.
|
|
*
|
|
* @param {calICalendar} calendar - A calendar to remove.
|
|
*/
|
|
removeCalendar(calendar) {
|
|
cal.manager.unregisterCalendar(calendar);
|
|
},
|
|
|
|
/**
|
|
* Ensures the calendar tab is open
|
|
*
|
|
* @param {Window} win
|
|
*/
|
|
async openCalendarTab(win) {
|
|
const tabmail = win.document.getElementById("tabmail");
|
|
const calendarMode = tabmail.tabModes.calendar;
|
|
|
|
if (calendarMode.tabs.length == 1) {
|
|
tabmail.selectedTab = calendarMode.tabs[0];
|
|
} else {
|
|
const calendarTabButton = win.document.getElementById("calendarButton");
|
|
EventUtils.synthesizeMouseAtCenter(calendarTabButton, { clickCount: 1 }, win);
|
|
}
|
|
|
|
Assert.equal(calendarMode.tabs.length, 1, "calendar tab is open");
|
|
Assert.equal(tabmail.selectedTab, calendarMode.tabs[0], "calendar tab is selected");
|
|
|
|
await new Promise(resolve => win.setTimeout(resolve));
|
|
},
|
|
|
|
/**
|
|
* Make sure the current view has finished loading.
|
|
*
|
|
* @param {Window} win
|
|
*/
|
|
async ensureViewLoaded(win) {
|
|
await win.currentView().ready;
|
|
},
|
|
|
|
/**
|
|
* Ensures the calendar view is in the specified mode.
|
|
*
|
|
* @param {Window} win
|
|
* @param {string} viewName
|
|
*/
|
|
async setCalendarView(win, viewName) {
|
|
await CalendarTestUtils.openCalendarTab(win);
|
|
await CalendarTestUtils.ensureViewLoaded(win);
|
|
|
|
const viewTabButton = win.document.querySelector(
|
|
`.calview-toggle-item[aria-controls="${viewName}-view"]`
|
|
);
|
|
EventUtils.synthesizeMouseAtCenter(viewTabButton, { clickCount: 1 }, win);
|
|
Assert.equal(win.currentView().id, `${viewName}-view`);
|
|
|
|
await CalendarTestUtils.ensureViewLoaded(win);
|
|
},
|
|
|
|
/**
|
|
* Step forward in the calendar view.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} n - Number of times to move the view forward.
|
|
*/
|
|
async calendarViewForward(win, n) {
|
|
const viewForwardButton = win.document.getElementById("nextViewButton");
|
|
for (let i = 0; i < n; i++) {
|
|
await clickAndWait(win, viewForwardButton);
|
|
await CalendarTestUtils.ensureViewLoaded(win);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Step backward in the calendar view.
|
|
*
|
|
* @param {Window} win - The window the calendar is displayed in.
|
|
* @param {number} n - Number of times to move the view backward.
|
|
*/
|
|
async calendarViewBackward(win, n) {
|
|
const viewBackwardButton = win.document.getElementById("previousViewButton");
|
|
for (let i = 0; i < n; i++) {
|
|
await clickAndWait(win, viewBackwardButton);
|
|
await CalendarTestUtils.ensureViewLoaded(win);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Ensures the calendar tab is not open.
|
|
*
|
|
* @param {Window} win
|
|
*/
|
|
async closeCalendarTab(win) {
|
|
const tabmail = win.document.getElementById("tabmail");
|
|
const calendarMode = tabmail.tabModes.calendar;
|
|
|
|
if (calendarMode.tabs.length == 1) {
|
|
tabmail.closeTab(calendarMode.tabs[0]);
|
|
}
|
|
|
|
Assert.equal(calendarMode.tabs.length, 0, "calendar tab is not open");
|
|
|
|
await new Promise(resolve => win.setTimeout(resolve));
|
|
},
|
|
|
|
/**
|
|
* Opens the event dialog for viewing by clicking on the provided event item.
|
|
*
|
|
* @param {Window} win - The window containing the calendar.
|
|
* @param {MozCalendarEditableItem} item - An event box item that can be
|
|
* clicked on to open the dialog.
|
|
*
|
|
* @returns {Promise<Window>}
|
|
*/
|
|
async viewItem(win, item) {
|
|
if (Services.focus.activeWindow != win) {
|
|
await BrowserTestUtils.waitForEvent(win, "focus");
|
|
}
|
|
|
|
const promise = this.waitForEventDialog("view");
|
|
EventUtils.synthesizeMouseAtCenter(item, { clickCount: 2 }, win);
|
|
return promise;
|
|
},
|
|
|
|
async _editNewItem(win, target, type) {
|
|
const dialogPromise = CalendarTestUtils.waitForEventDialog("edit");
|
|
|
|
if (target) {
|
|
this.scrollViewToTarget(target, true);
|
|
await new Promise(resolve => win.setTimeout(resolve));
|
|
EventUtils.synthesizeMouse(target, 1, 1, { clickCount: 2 }, win);
|
|
} else {
|
|
const buttonId = `sidePanelNew${type[0].toUpperCase()}${type.slice(1).toLowerCase()}`;
|
|
EventUtils.synthesizeMouseAtCenter(win.document.getElementById(buttonId), {}, win);
|
|
}
|
|
|
|
const dialogWindow = await dialogPromise;
|
|
const iframe = dialogWindow.document.querySelector("#calendar-item-panel-iframe");
|
|
await new Promise(resolve => iframe.contentWindow.setTimeout(resolve));
|
|
Assert.report(false, undefined, undefined, `New ${type} dialog opened`);
|
|
return {
|
|
dialogWindow,
|
|
dialogDocument: dialogWindow.document,
|
|
iframeWindow: iframe.contentWindow,
|
|
iframeDocument: iframe.contentDocument,
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Opens the dialog for editing a new event. An optional day/week view
|
|
* hour box or multiweek/month view calendar-month-day-box can be specified
|
|
* to simulate creation of the event at that target.
|
|
*
|
|
* @param {Window} win - The window containing the calendar.
|
|
* @param {XULElement?} target - The <spacer> or <calendar-month-day-box>
|
|
* to click on, if not specified, the new event
|
|
* button is used.
|
|
*/
|
|
async editNewEvent(win, target) {
|
|
return this._editNewItem(win, target, "event");
|
|
},
|
|
|
|
/**
|
|
* Opens the dialog for editing a new task.
|
|
*
|
|
* @param {Promise<Window>} win - The window containing the task tree.
|
|
*/
|
|
async editNewTask(win) {
|
|
return this._editNewItem(win, null, "task");
|
|
},
|
|
|
|
async _editItem(win, item, selector) {
|
|
const summaryWin = await this.viewItem(win, item);
|
|
const promise = this.waitForEventDialog("edit");
|
|
const button = summaryWin.document.querySelector(selector);
|
|
button.click();
|
|
|
|
const dialogWindow = await promise;
|
|
const iframe = dialogWindow.document.querySelector("#calendar-item-panel-iframe");
|
|
return {
|
|
dialogWindow,
|
|
dialogDocument: dialogWindow.document,
|
|
iframeWindow: iframe.contentWindow,
|
|
iframeDocument: iframe.contentDocument,
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Opens the event dialog for editing by clicking on the provided event item.
|
|
*
|
|
* @param {Window} win - The window containing the calendar.
|
|
* @param {MozCalendarEditableItem} item - An event box item that can be
|
|
* clicked on to open the dialog.
|
|
*
|
|
* @returns {Promise<EditItemAtResult>}
|
|
*/
|
|
async editItem(win, item) {
|
|
return this._editItem(win, item, "#calendar-summary-dialog-edit-button");
|
|
},
|
|
|
|
/**
|
|
* Opens the event dialog for editing a single occurrence of a repeating event
|
|
* by clicking on the provided event item.
|
|
*
|
|
* @param {Window} win - The window containing the calendar.
|
|
* @param {MozCalendarEditableItem} item - An event box item that can be
|
|
* clicked on to open the dialog.
|
|
*
|
|
* @returns {Window}
|
|
*/
|
|
async editItemOccurrence(win, item) {
|
|
return this._editItem(win, item, "#edit-button-context-menu-this-occurrence");
|
|
},
|
|
|
|
/**
|
|
* Opens the event dialog for editing all occurrences of a repeating event
|
|
* by clicking on the provided event box.
|
|
*
|
|
* @param {Window} win - The window containing the calendar.
|
|
* @param {MozCalendarEditableItem} item - An event box item that can be
|
|
* clicked on to open the dialog.
|
|
*
|
|
* @returns {Window}
|
|
*/
|
|
async editItemOccurrences(win, item) {
|
|
return this._editItem(win, item, "#edit-button-context-menu-all-occurrences");
|
|
},
|
|
|
|
/**
|
|
* This produces a Promise for waiting on an event dialog to open.
|
|
* The mode parameter can be specified to indicate which of the dialogs to
|
|
* wait for.
|
|
*
|
|
* @param {string} [mode="view"] Determines which dialog we are waiting on,
|
|
* can be "view" for the summary or "edit" for the editing one.
|
|
*
|
|
* @returns {Promise<Window>}
|
|
*/
|
|
waitForEventDialog(mode = "view") {
|
|
const uri =
|
|
mode === "edit"
|
|
? "chrome://calendar/content/calendar-event-dialog.xhtml"
|
|
: "chrome://calendar/content/calendar-summary-dialog.xhtml";
|
|
|
|
return BrowserTestUtils.domWindowOpenedAndLoaded(null, async win => {
|
|
if (win.document.documentURI != uri) {
|
|
return false;
|
|
}
|
|
|
|
Assert.report(false, undefined, undefined, "Event dialog opened");
|
|
await TestUtils.waitForCondition(
|
|
() => Services.focus.activeWindow == win,
|
|
"event dialog active"
|
|
);
|
|
|
|
if (mode === "edit") {
|
|
const iframe = win.document.getElementById("calendar-item-panel-iframe");
|
|
await TestUtils.waitForCondition(
|
|
() => iframe.contentWindow?.onLoad?.hasLoaded,
|
|
"waiting for iframe to be loaded"
|
|
);
|
|
await TestUtils.waitForCondition(
|
|
() => Services.focus.focusedWindow == iframe.contentWindow,
|
|
"waiting for iframe to be focused"
|
|
);
|
|
}
|
|
return true;
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Go to a specific date using the minimonth.
|
|
*
|
|
* @param {Window} win - Main window
|
|
* @param {number} year - Four-digit year
|
|
* @param {number} month - 1-based index of a month
|
|
* @param {number} day - 1-based index of a day
|
|
*/
|
|
async goToDate(win, year, month, day) {
|
|
const miniMonth = win.document.getElementById("calMinimonth");
|
|
|
|
const activeYear = miniMonth.querySelector(".minimonth-year-name").getAttribute("value");
|
|
|
|
const activeMonth = miniMonth.querySelector(".minimonth-month-name").getAttribute("monthIndex");
|
|
|
|
async function doScroll(name, difference, sleepTime) {
|
|
if (difference === 0) {
|
|
return;
|
|
}
|
|
const query = `.${name}s-${difference > 0 ? "back" : "forward"}-button`;
|
|
const scrollArrow = await TestUtils.waitForCondition(
|
|
() => miniMonth.querySelector(query),
|
|
`Query for scroll: ${query}`
|
|
);
|
|
|
|
for (let i = 0; i < Math.abs(difference); i++) {
|
|
EventUtils.synthesizeMouseAtCenter(scrollArrow, {}, win);
|
|
await new Promise(resolve => win.setTimeout(resolve, sleepTime));
|
|
}
|
|
}
|
|
|
|
await doScroll("year", activeYear - year, 10);
|
|
await doScroll("month", activeMonth - (month - 1), 25);
|
|
|
|
function getMiniMonthDay(week, day) {
|
|
return miniMonth.querySelector(
|
|
`.minimonth-cal-box > tr.minimonth-row-body:nth-of-type(${week + 1}) > ` +
|
|
`td.minimonth-day:nth-of-type(${day})`
|
|
);
|
|
}
|
|
|
|
const positionOfFirst = 7 - getMiniMonthDay(1, 7).textContent;
|
|
const weekDay = ((positionOfFirst + day - 1) % 7) + 1;
|
|
const week = Math.floor((positionOfFirst + day - 1) / 7) + 1;
|
|
|
|
// Pick day.
|
|
EventUtils.synthesizeMouseAtCenter(getMiniMonthDay(week, weekDay), {}, win);
|
|
await CalendarTestUtils.ensureViewLoaded(win);
|
|
},
|
|
|
|
/**
|
|
* Go to today.
|
|
*
|
|
* @param {Window} win - Main window
|
|
*/
|
|
async goToToday(win) {
|
|
EventUtils.synthesizeMouseAtCenter(this.getNavBarTodayButton(win), {}, win);
|
|
await CalendarTestUtils.ensureViewLoaded(win);
|
|
},
|
|
|
|
/**
|
|
* Assert whether the given event box's edges are visually draggable (and
|
|
* hence, editable) at its edges or not.
|
|
*
|
|
* @param {MozCalendarEventBox} eventBox - The event box to test.
|
|
* @param {boolean} startDraggable - Whether we expect the start edge to be
|
|
* draggable.
|
|
* @param {boolean} endDraggable - Whether we expect the end edge to be
|
|
* draggable.
|
|
* @param {string} message - A message for assertions.
|
|
*/
|
|
async assertEventBoxDraggable(eventBox, startDraggable, endDraggable, message) {
|
|
this.scrollViewToTarget(eventBox, true);
|
|
// Hover to see if the drag gripbars appear.
|
|
const enterPromise = BrowserTestUtils.waitForEvent(eventBox, "mouseenter");
|
|
// Hover over start.
|
|
EventUtils.synthesizeMouse(eventBox, 8, 8, { type: "mouseover" }, eventBox.ownerGlobal);
|
|
await enterPromise;
|
|
Assert.equal(
|
|
BrowserTestUtils.isVisible(eventBox.startGripbar),
|
|
startDraggable,
|
|
`Start gripbar should be ${startDraggable ? "visible" : "hidden"} on hover: ${message}`
|
|
);
|
|
Assert.equal(
|
|
BrowserTestUtils.isVisible(eventBox.endGripbar),
|
|
endDraggable,
|
|
`End gripbar should be ${endDraggable ? "visible" : "hidden"} on hover: ${message}`
|
|
);
|
|
},
|
|
|
|
/**
|
|
* Scroll the calendar view to show the given target.
|
|
*
|
|
* @param {Element} target - The target to scroll to. A descendent of a
|
|
* calendar view.
|
|
* @param {boolean} alignStart - Whether to scroll the inline and block start
|
|
* edges of the target into view, else scrolls the end edges into view.
|
|
*/
|
|
scrollViewToTarget(target, alignStart) {
|
|
const multidayView = target.closest("calendar-day-view, calendar-week-view");
|
|
if (multidayView) {
|
|
// Multiday view has sticky headers, so scrollIntoView doesn't actually
|
|
// scroll far enough.
|
|
const scrollRect = multidayView.getScrollAreaRect();
|
|
const targetRect = target.getBoundingClientRect();
|
|
// We want to move the view by the difference between the starting/ending
|
|
// edge of the view and the starting/ending edge of the target.
|
|
const yDiff = alignStart
|
|
? targetRect.top - scrollRect.top
|
|
: targetRect.bottom - scrollRect.bottom;
|
|
// In left-to-right, starting edge is the left edge. Otherwise, it is the
|
|
// right edge.
|
|
const xDiff =
|
|
alignStart == (target.ownerDocument.dir == "ltr")
|
|
? targetRect.left - scrollRect.left
|
|
: targetRect.right - scrollRect.right;
|
|
multidayView.grid.scrollBy(xDiff, yDiff);
|
|
} else {
|
|
target.scrollIntoView(alignStart);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Save the current calendar views' UI states to be restored later.
|
|
*
|
|
* This is used with restoreCalendarViewsState to reset the view back to its
|
|
* initial loaded state after a test, so that later tests in the same group
|
|
* will receive the calendar view as if it was first opened after launching.
|
|
*
|
|
* @param {Window} win - The window that contains the calendar views.
|
|
*
|
|
* @returns {object} - An opaque object with data to pass to
|
|
* restoreCalendarViewsState.
|
|
*/
|
|
saveCalendarViewsState(win) {
|
|
return {
|
|
multidayViewsData: ["day", "week"].map(viewName => {
|
|
// Save the scroll state since test utilities may change the scroll
|
|
// position, and this is currently not reset on re-opening the tab.
|
|
const view = win.document.getElementById(`${viewName}-view`);
|
|
return { view, viewName, scrollMinute: view.scrollMinute };
|
|
}),
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Clean up the calendar views after a test by restoring their UI to the saved
|
|
* state, and close the calendar tab.
|
|
*
|
|
* @param {Window} win - The window that contains the calendar views.
|
|
* @param {object} data - The data returned by saveCalendarViewsState.
|
|
*/
|
|
async restoreCalendarViewsState(win, data) {
|
|
for (const { view, viewName, scrollMinute } of data.multidayViewsData) {
|
|
await this.setCalendarView(win, viewName);
|
|
// The scrollMinute is rounded to the nearest integer.
|
|
// As is the scroll pixels.
|
|
// When we scrollToMinute, the scroll position is rounded to the nearest
|
|
// integer, as is the subsequent scroll minute. So calling
|
|
// scrollToMinute(min)
|
|
// will set
|
|
// scrollMinute = round(round(min * P) / P)
|
|
// where P is the pixelsPerMinute of the view. Thus
|
|
// scrollMinute = min +- round(0.5 / P)
|
|
const roundingError = Math.round(0.5 / view.pixelsPerMinute);
|
|
view.scrollToMinute(scrollMinute);
|
|
await TestUtils.waitForCondition(
|
|
() => Math.abs(view.scrollMinute - scrollMinute) <= roundingError,
|
|
"Waiting for scroll minute to restore"
|
|
);
|
|
}
|
|
await CalendarTestUtils.closeCalendarTab(win);
|
|
},
|
|
|
|
/**
|
|
* Get the Today button from the navigation bar.
|
|
*
|
|
* @param {Window} win - The window which contains the calendar.
|
|
*
|
|
* @returns {HTMLElement} - The today button.
|
|
*/
|
|
getNavBarTodayButton(win) {
|
|
return win.document.getElementById("todayViewButton");
|
|
},
|
|
|
|
/**
|
|
* Get the label element containing a human-readable description of the
|
|
* displayed interval.
|
|
*
|
|
* @param {Window} win - The window which contains the calendar.
|
|
*
|
|
* @returns {HTMLLabelElement} - The interval description label.
|
|
*/
|
|
getNavBarIntervalDescription(win) {
|
|
return win.document.getElementById("intervalDescription");
|
|
},
|
|
|
|
/**
|
|
* Get the label element containing an indication of which week or weeks are
|
|
* displayed.
|
|
*
|
|
* @param {Window} win - The window which contains the calendar.
|
|
*
|
|
* @returns {HTMLLabelElement} - The calendar week label.
|
|
*/
|
|
getNavBarCalendarWeekBox(win) {
|
|
return win.document.getElementById("calendarWeek");
|
|
},
|
|
};
|