summaryrefslogtreecommitdiffstats
path: root/comm/calendar/test/browser/recurrence
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/calendar/test/browser/recurrence
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/calendar/test/browser/recurrence')
-rw-r--r--comm/calendar/test/browser/recurrence/browser.ini23
-rw-r--r--comm/calendar/test/browser/recurrence/browser_annual.js69
-rw-r--r--comm/calendar/test/browser/recurrence/browser_biweekly.js85
-rw-r--r--comm/calendar/test/browser/recurrence/browser_daily.js162
-rw-r--r--comm/calendar/test/browser/recurrence/browser_lastDayOfMonth.js112
-rw-r--r--comm/calendar/test/browser/recurrence/browser_recurrenceNavigation.js138
-rw-r--r--comm/calendar/test/browser/recurrence/browser_rotated.ini24
-rw-r--r--comm/calendar/test/browser/recurrence/browser_weeklyN.js268
-rw-r--r--comm/calendar/test/browser/recurrence/browser_weeklyUntil.js175
-rw-r--r--comm/calendar/test/browser/recurrence/browser_weeklyWithException.js264
-rw-r--r--comm/calendar/test/browser/recurrence/head.js26
11 files changed, 1346 insertions, 0 deletions
diff --git a/comm/calendar/test/browser/recurrence/browser.ini b/comm/calendar/test/browser/recurrence/browser.ini
new file mode 100644
index 0000000000..633ed27dde
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser.ini
@@ -0,0 +1,23 @@
+[default]
+dupe-manifest =
+prefs =
+ calendar.item.promptDelete=false
+ calendar.timezone.local=UTC
+ calendar.timezone.useSystemTimezone=false
+ calendar.week.start=0
+ mail.provider.suppress_dialog_on_startup=true
+ mail.spotlight.firstRunDone=true
+ mail.winsearch.firstRunDone=true
+ mailnews.start_page.override_url=about:blank
+ mailnews.start_page.url=about:blank
+subsuite = thunderbird
+tags = recurrence
+
+[browser_annual.js]
+[browser_biweekly.js]
+[browser_daily.js]
+[browser_lastDayOfMonth.js]
+[browser_recurrenceNavigation.js]
+[browser_weeklyN.js]
+[browser_weeklyUntil.js]
+[browser_weeklyWithException.js]
diff --git a/comm/calendar/test/browser/recurrence/browser_annual.js b/comm/calendar/test/browser/recurrence/browser_annual.js
new file mode 100644
index 0000000000..54c7198f05
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_annual.js
@@ -0,0 +1,69 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var { dayView, weekView, multiweekView, monthView } = CalendarTestUtils;
+
+const STARTYEAR = 1950;
+const EPOCH = 1970;
+
+add_task(async function testAnnualRecurrence() {
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, STARTYEAR, 1, 1);
+
+ // Create yearly recurring all-day event.
+ let eventBox = dayView.getAllDayHeader(window);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: "Event", repeat: "yearly" });
+ await saveAndCloseItemDialog(dialogWindow);
+ await TestUtils.waitForCondition(
+ () => CalendarTestUtils.dayView.getAllDayItemAt(window, 1),
+ "recurring all-day event created"
+ );
+
+ let checkYears = [STARTYEAR, STARTYEAR + 1, EPOCH - 1, EPOCH, EPOCH + 1];
+ for (let year of checkYears) {
+ await CalendarTestUtils.goToDate(window, year, 1, 1);
+ let date = new Date(Date.UTC(year, 0, 1));
+ let column = date.getUTCDay() + 1;
+
+ // day view
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await dayView.waitForAllDayItemAt(window, 1);
+
+ // week view
+ await CalendarTestUtils.setCalendarView(window, "week");
+ await weekView.waitForAllDayItemAt(window, column, 1);
+
+ // multiweek view
+ await CalendarTestUtils.setCalendarView(window, "multiweek");
+ await multiweekView.waitForItemAt(window, 1, column, 1);
+
+ // month view
+ await CalendarTestUtils.setCalendarView(window, "month");
+ await monthView.waitForItemAt(window, 1, column, 1);
+ }
+
+ // Delete event.
+ await CalendarTestUtils.goToDate(window, checkYears[0], 1, 1);
+ await CalendarTestUtils.setCalendarView(window, "day");
+ const box = await dayView.waitForAllDayItemAt(window, 1);
+ EventUtils.synthesizeMouseAtCenter(box, {}, window);
+ await handleDeleteOccurrencePrompt(window, box, true);
+ await TestUtils.waitForCondition(() => !dayView.getAllDayItemAt(window, 1), "No all-day events");
+
+ Assert.ok(true, "Test ran to completion");
+});
diff --git a/comm/calendar/test/browser/recurrence/browser_biweekly.js b/comm/calendar/test/browser/recurrence/browser_biweekly.js
new file mode 100644
index 0000000000..6889822375
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_biweekly.js
@@ -0,0 +1,85 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var { dayView, weekView, multiweekView, monthView } = CalendarTestUtils;
+
+const HOUR = 8;
+
+add_task(async function testBiweeklyRecurrence() {
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 31);
+
+ // Create biweekly event.
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: "Event", repeat: "bi.weekly" });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Check day view.
+ await CalendarTestUtils.setCalendarView(window, "day");
+ for (let i = 0; i < 4; i++) {
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 14);
+ }
+
+ // Check week view.
+ await CalendarTestUtils.setCalendarView(window, "week");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 31);
+
+ for (let i = 0; i < 4; i++) {
+ await weekView.waitForEventBoxAt(window, 7, 1);
+ await CalendarTestUtils.calendarViewForward(window, 2);
+ }
+
+ // Check multiweek view.
+ await CalendarTestUtils.setCalendarView(window, "multiweek");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 31);
+
+ // Always two occurrences in view, 1st and 3rd or 2nd and 4th week.
+ for (let i = 0; i < 5; i++) {
+ await multiweekView.waitForItemAt(window, (i % 2) + 1, 7, 1);
+ Assert.ok(multiweekView.getItemAt(window, (i % 2) + 3, 7, 1));
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ }
+
+ // Check month view.
+ await CalendarTestUtils.setCalendarView(window, "month");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 31);
+
+ // January
+ await monthView.waitForItemAt(window, 5, 7, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ // February
+ await monthView.waitForItemAt(window, 2, 7, 1);
+ Assert.ok(monthView.getItemAt(window, 4, 7, 1));
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ // March
+ await monthView.waitForItemAt(window, 2, 7, 1);
+
+ let box = monthView.getItemAt(window, 4, 7, 1);
+ Assert.ok(box);
+
+ // Delete event.
+ EventUtils.synthesizeMouseAtCenter(box, {}, window);
+ await handleDeleteOccurrencePrompt(window, box, true);
+
+ await monthView.waitForNoItemAt(window, 4, 7, 1);
+
+ Assert.ok(true, "Test ran to completion");
+});
diff --git a/comm/calendar/test/browser/recurrence/browser_daily.js b/comm/calendar/test/browser/recurrence/browser_daily.js
new file mode 100644
index 0000000000..42ffc4c8db
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_daily.js
@@ -0,0 +1,162 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+var { saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var {
+ calendarViewBackward,
+ calendarViewForward,
+ setCalendarView,
+ dayView,
+ weekView,
+ multiweekView,
+ monthView,
+} = CalendarTestUtils;
+
+const HOUR = 8;
+const TITLE = "Event";
+
+add_task(async function testDailyRecurrence() {
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ // Create daily event.
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, {
+ title: TITLE,
+ repeat: "daily",
+ repeatuntil: cal.createDateTime("20090320T000000Z"),
+ });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Check day view for 7 days.
+ for (let day = 1; day <= 7; day++) {
+ await dayView.waitForEventBoxAt(window, 1);
+ await calendarViewForward(window, 1);
+ }
+
+ // Check week view for 2 weeks.
+ await setCalendarView(window, "week");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ for (let day = 5; day <= 7; day++) {
+ await weekView.waitForEventBoxAt(window, day, 1);
+ }
+
+ await calendarViewForward(window, 1);
+
+ for (let day = 1; day <= 7; day++) {
+ await weekView.waitForEventBoxAt(window, day, 1);
+ }
+
+ // Check multiweek view for 4 weeks.
+ await setCalendarView(window, "multiweek");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ for (let day = 5; day <= 7; day++) {
+ await multiweekView.waitForItemAt(window, 1, day, 1);
+ }
+
+ for (let week = 2; week <= 4; week++) {
+ for (let day = 1; day <= 7; day++) {
+ await multiweekView.waitForItemAt(window, week, day, 1);
+ }
+ }
+ // Check month view for all 5 weeks.
+ await setCalendarView(window, "month");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ for (let day = 5; day <= 7; day++) {
+ await monthView.waitForItemAt(window, 1, day, 1);
+ }
+
+ for (let week = 2; week <= 5; week++) {
+ for (let day = 1; day <= 7; day++) {
+ await monthView.waitForItemAt(window, week, day, 1);
+ }
+ }
+
+ // Delete 3rd January occurrence.
+ let saturday = await monthView.waitForItemAt(window, 1, 7, 1);
+ EventUtils.synthesizeMouseAtCenter(saturday, {}, window);
+ await handleDeleteOccurrencePrompt(window, saturday, false);
+
+ // Verify in all views.
+ await monthView.waitForNoItemAt(window, 1, 7, 1);
+
+ await setCalendarView(window, "multiweek");
+ Assert.ok(!multiweekView.getItemAt(window, 1, 7, 1));
+
+ await setCalendarView(window, "week");
+ Assert.ok(!weekView.getEventBoxAt(window, 7, 1));
+
+ await setCalendarView(window, "day");
+ Assert.ok(!dayView.getEventBoxAt(window, 1));
+
+ // Go to previous day to edit event to occur only on weekdays.
+ await calendarViewBackward(window, 1);
+
+ ({ dialogWindow, iframeWindow } = await dayView.editEventOccurrencesAt(window, 1));
+ await setData(dialogWindow, iframeWindow, { repeat: "every.weekday" });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Check day view for 7 days.
+ let dates = [
+ [2009, 1, 3],
+ [2009, 1, 4],
+ ];
+ for (let [y, m, d] of dates) {
+ await CalendarTestUtils.goToDate(window, y, m, d);
+ Assert.ok(!dayView.getEventBoxAt(window, 1));
+ }
+
+ // Check week view for 2 weeks.
+ await setCalendarView(window, "week");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ for (let i = 0; i <= 1; i++) {
+ await weekView.waitForNoEventBoxAt(window, 1, 1);
+ Assert.ok(!weekView.getEventBoxAt(window, 7, 1));
+ await calendarViewForward(window, 1);
+ }
+
+ // Check multiweek view for 4 weeks.
+ await setCalendarView(window, "multiweek");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ for (let i = 1; i <= 4; i++) {
+ await multiweekView.waitForNoItemAt(window, i, 1, 1);
+ Assert.ok(!multiweekView.getItemAt(window, i, 7, 1));
+ }
+
+ // Check month view for all 5 weeks.
+ await setCalendarView(window, "month");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 1);
+
+ for (let i = 1; i <= 5; i++) {
+ await monthView.waitForNoItemAt(window, i, 1, 1);
+ Assert.ok(!monthView.getItemAt(window, i, 7, 1));
+ }
+
+ // Delete event.
+ let day = monthView.getItemAt(window, 1, 5, 1);
+ EventUtils.synthesizeMouseAtCenter(day, {}, window);
+ await handleDeleteOccurrencePrompt(window, day, true);
+ await monthView.waitForNoItemAt(window, 1, 5, 1);
+
+ Assert.ok(true, "Test ran to completion");
+});
diff --git a/comm/calendar/test/browser/recurrence/browser_lastDayOfMonth.js b/comm/calendar/test/browser/recurrence/browser_lastDayOfMonth.js
new file mode 100644
index 0000000000..bc7e01556a
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_lastDayOfMonth.js
@@ -0,0 +1,112 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { menulistSelect } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+var { saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var { setCalendarView, dayView, weekView, multiweekView, monthView } = CalendarTestUtils;
+
+const HOUR = 8;
+
+add_task(async function testLastDayOfMonthRecurrence() {
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2008, 1, 31); // Start with a leap year.
+
+ // Create monthly recurring event.
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: "Event", repeat: setRecurrence });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // data tuple: [year, month, day, row in month view]
+ // note: Month starts here with 1 for January.
+ let checkingData = [
+ [2008, 1, 31, 5],
+ [2008, 2, 29, 5],
+ [2008, 3, 31, 6],
+ [2008, 4, 30, 5],
+ [2008, 5, 31, 5],
+ [2008, 6, 30, 5],
+ [2008, 7, 31, 5],
+ [2008, 8, 31, 6],
+ [2008, 9, 30, 5],
+ [2008, 10, 31, 5],
+ [2008, 11, 30, 6],
+ [2008, 12, 31, 5],
+ [2009, 1, 31, 5],
+ [2009, 2, 28, 4],
+ [2009, 3, 31, 5],
+ ];
+ // Check all dates.
+ for (let [y, m, d, correctRow] of checkingData) {
+ let date = new Date(Date.UTC(y, m - 1, d));
+ let column = date.getUTCDay() + 1;
+
+ await CalendarTestUtils.goToDate(window, y, m, d);
+
+ // day view
+ await setCalendarView(window, "day");
+ await dayView.waitForEventBoxAt(window, 1);
+
+ // week view
+ await setCalendarView(window, "week");
+ await weekView.waitForEventBoxAt(window, column, 1);
+
+ // multiweek view
+ await setCalendarView(window, "multiweek");
+ await multiweekView.waitForItemAt(window, 1, column, 1);
+
+ // month view
+ await setCalendarView(window, "month");
+ await monthView.waitForItemAt(window, correctRow, column, 1);
+ }
+
+ // Delete event.
+ await CalendarTestUtils.goToDate(
+ window,
+ checkingData[0][0],
+ checkingData[0][1],
+ checkingData[0][2]
+ );
+ await setCalendarView(window, "day");
+ let box = await dayView.waitForEventBoxAt(window, 1);
+ EventUtils.synthesizeMouseAtCenter(box, {}, window);
+ await handleDeleteOccurrencePrompt(window, box, true);
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ Assert.ok(true, "Test ran to completion");
+});
+
+async function setRecurrence(recurrenceWindow) {
+ let recurrenceDocument = recurrenceWindow.document;
+ // monthly
+ await menulistSelect(recurrenceDocument.getElementById("period-list"), "2");
+
+ // last day of month
+ EventUtils.synthesizeMouseAtCenter(
+ recurrenceDocument.getElementById("montly-period-relative-date-radio"),
+ {},
+ recurrenceWindow
+ );
+ await menulistSelect(recurrenceDocument.getElementById("monthly-ordinal"), "-1");
+ await menulistSelect(recurrenceDocument.getElementById("monthly-weekday"), "-1");
+
+ let button = recurrenceDocument.querySelector("dialog").getButton("accept");
+ button.scrollIntoView();
+ // Close dialog.
+ EventUtils.synthesizeMouseAtCenter(button, {}, recurrenceWindow);
+}
diff --git a/comm/calendar/test/browser/recurrence/browser_recurrenceNavigation.js b/comm/calendar/test/browser/recurrence/browser_recurrenceNavigation.js
new file mode 100644
index 0000000000..8dfe7287ce
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_recurrenceNavigation.js
@@ -0,0 +1,138 @@
+/* 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 { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+const calendar = CalendarTestUtils.createCalendar("Minimonths", "memory");
+
+registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+});
+
+add_task(async function testRecurrenceNavigation() {
+ await CalendarTestUtils.setCalendarView(window, "month");
+
+ let eventDate = cal.createDateTime("20200201T000001Z");
+ window.goToDate(eventDate);
+
+ let newEventBtn = document.querySelector("#sidePanelNewEvent");
+ let getEventWin = CalendarTestUtils.waitForEventDialog("edit");
+ EventUtils.synthesizeMouseAtCenter(newEventBtn, {});
+
+ let eventWin = await getEventWin;
+ let iframe = eventWin.document.querySelector("iframe");
+
+ let getRepeatWin = BrowserTestUtils.promiseAlertDialogOpen(
+ "",
+ "chrome://calendar/content/calendar-event-dialog-recurrence.xhtml",
+ {
+ async callback(win) {
+ let container = await TestUtils.waitForCondition(() => {
+ return win.document.querySelector("#recurrencePreviewContainer");
+ }, `The recurrence container exists`);
+
+ let initialMonth = await TestUtils.waitForCondition(() => {
+ return container.querySelector(`calendar-minimonth[month="1"][year="2020"]`);
+ }, `Initial month exists`);
+ Assert.ok(!initialMonth.hidden, `Initial month is visible on load`);
+
+ let nextButton = container.querySelector("#recurrenceNext");
+ Assert.ok(nextButton, `Next button exists`);
+ nextButton.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(nextButton, {}, win);
+
+ let nextMonth = container.querySelector(`calendar-minimonth[month="2"][year="2020"]`);
+ Assert.ok(nextMonth, `Next month exists`);
+ Assert.ok(!nextMonth.hidden, `Next month is visible`);
+
+ let previousButton = container.querySelector("#recurrencePrevious");
+ Assert.ok(previousButton, `Previous button exists`);
+ previousButton.scrollIntoView();
+ EventUtils.synthesizeMouseAtCenter(previousButton, {}, win);
+ Assert.ok(!initialMonth.hidden, `Previous month is visible after using previous button`);
+
+ // Check that future dates display
+ nextButton.scrollIntoView();
+ for (let index = 0; index < 5; index++) {
+ EventUtils.synthesizeMouseAtCenter(nextButton, {}, win);
+ }
+
+ let futureMonth = await TestUtils.waitForCondition(() => {
+ return container.querySelector(`calendar-minimonth[month="6"][year="2020"]`);
+ }, `Future month exist`);
+ Assert.ok(!futureMonth.hidden, `Future month is visible after using next button`);
+
+ // Ensure the number of minimonths shown is the amount we expect.
+ let defaultMinimonthCount = "3";
+ let actualVisibleMinimonthCount = container.querySelectorAll(
+ `calendar-minimonth:not([hidden])`
+ ).length;
+ Assert.equal(
+ defaultMinimonthCount,
+ actualVisibleMinimonthCount,
+ `Default minimonth visible count matches actual: ${actualVisibleMinimonthCount}`
+ );
+
+ // Go back 5 times; we should go back to the initial month.
+ for (let index = 0; index < 5; index++) {
+ EventUtils.synthesizeMouseAtCenter(previousButton, {}, win);
+ }
+ Assert.ok(!initialMonth.hidden, `Initial month is visible`);
+
+ // Close window at end of tests for this item
+ await BrowserTestUtils.closeWindow(win);
+ },
+ }
+ );
+
+ let repeatMenu = iframe.contentDocument.querySelector("#item-repeat");
+ repeatMenu.value = "custom";
+ repeatMenu.doCommand();
+ await getRepeatWin;
+
+ await BrowserTestUtils.closeWindow(eventWin);
+});
+
+add_task(async function testRecurrenceCreationOfMonths() {
+ await CalendarTestUtils.setCalendarView(window, "month");
+
+ let eventDate = cal.createDateTime("20200101T000001Z");
+ window.goToDate(eventDate);
+
+ let newEventBtn = document.querySelector("#sidePanelNewEvent");
+ let getEventWin = CalendarTestUtils.waitForEventDialog("edit");
+ EventUtils.synthesizeMouseAtCenter(newEventBtn, {});
+
+ let eventWin = await getEventWin;
+ let iframe = eventWin.document.querySelector("iframe");
+
+ let getRepeatWin = BrowserTestUtils.promiseAlertDialogOpen(
+ "",
+ "chrome://calendar/content/calendar-event-dialog-recurrence.xhtml",
+ {
+ async callback(win) {
+ let container = win.document.querySelector("#recurrencePreviewContainer");
+ let nextButton = container.querySelector("#recurrenceNext");
+ nextButton.scrollIntoView();
+ for (let index = 0; index < 10; index++) {
+ EventUtils.synthesizeMouseAtCenter(nextButton, {}, win);
+ }
+
+ let futureMonth = container.querySelector(`calendar-minimonth[month="10"][year="2020"]`);
+ Assert.ok(futureMonth, `Dynamically created future month exists`);
+ Assert.ok(!futureMonth.hidden, `Dynamically created future month is visible`);
+
+ // Close window at end of tests for this item
+ await BrowserTestUtils.closeWindow(win);
+ },
+ }
+ );
+
+ let repeatMenu = iframe.contentDocument.querySelector("#item-repeat");
+ repeatMenu.value = "custom";
+ repeatMenu.doCommand();
+ await getRepeatWin;
+
+ await BrowserTestUtils.closeWindow(eventWin);
+});
diff --git a/comm/calendar/test/browser/recurrence/browser_rotated.ini b/comm/calendar/test/browser/recurrence/browser_rotated.ini
new file mode 100644
index 0000000000..2385ed9324
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_rotated.ini
@@ -0,0 +1,24 @@
+[default]
+head = head.js
+dupe-manifest =
+prefs =
+ calendar.item.promptDelete=false
+ calendar.test.rotateViews=true
+ calendar.timezone.local=UTC
+ calendar.timezone.useSystemTimezone=false
+ calendar.week.start=0
+ mail.provider.suppress_dialog_on_startup=true
+ mail.spotlight.firstRunDone=true
+ mail.winsearch.firstRunDone=true
+ mailnews.start_page.override_url=about:blank
+ mailnews.start_page.url=about:blank
+subsuite = thunderbird
+tags = recurrence-rotated
+
+[browser_annual.js]
+[browser_biweekly.js]
+[browser_daily.js]
+[browser_lastDayOfMonth.js]
+[browser_weeklyN.js]
+[browser_weeklyUntil.js]
+[browser_weeklyWithException.js]
diff --git a/comm/calendar/test/browser/recurrence/browser_weeklyN.js b/comm/calendar/test/browser/recurrence/browser_weeklyN.js
new file mode 100644
index 0000000000..e32ab470f1
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_weeklyN.js
@@ -0,0 +1,268 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { menulistSelect, saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+var { dayView, weekView, multiweekView, monthView } = CalendarTestUtils;
+
+const HOUR = 8;
+
+/*
+ * This test is intended to verify that events recurring on a weekly basis are
+ * correctly created and displayed. The event should recur on multiple days in
+ * the week, skip days, and be limited to a certain number of recurrences in
+ * order to verify that these parameters are respected. Deletion should delete
+ * all event occurrences when appropriate.
+ */
+add_task(async function testWeeklyNRecurrence() {
+ async function setRecurrence(recurrenceWindow) {
+ let recurrenceDocument = recurrenceWindow.document;
+
+ // Select weekly recurrence
+ await menulistSelect(recurrenceDocument.getElementById("period-list"), "1");
+
+ let monLabel = cal.l10n.getDateFmtString("day.2.Mmm");
+ let tueLabel = cal.l10n.getDateFmtString("day.3.Mmm");
+ let wedLabel = cal.l10n.getDateFmtString("day.4.Mmm");
+ let friLabel = cal.l10n.getDateFmtString("day.6.Mmm");
+ let satLabel = cal.l10n.getDateFmtString("day.7.Mmm");
+
+ let dayPicker = recurrenceDocument.getElementById("daypicker-weekday");
+
+ // Selected date is a Monday, so it should already be selected
+ Assert.ok(
+ dayPicker.querySelector(`[label="${monLabel}"]`).checked,
+ "Monday should already be selected"
+ );
+
+ // Select Tuesday, Wednesday, Friday, and Saturday as additional days for
+ // event occurrences
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${tueLabel}"]`),
+ {},
+ recurrenceWindow
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${wedLabel}"]`),
+ {},
+ recurrenceWindow
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${friLabel}"]`),
+ {},
+ recurrenceWindow
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${satLabel}"]`),
+ {},
+ recurrenceWindow
+ );
+
+ // Create a total of four events
+ EventUtils.synthesizeMouseAtCenter(
+ recurrenceDocument.getElementById("recurrence-range-for"),
+ {},
+ recurrenceWindow
+ );
+ recurrenceDocument.getElementById("repeat-ntimes-count").value = "4";
+
+ let button = recurrenceDocument.querySelector("dialog").getButton("accept");
+ button.scrollIntoView();
+ // Close dialog
+ EventUtils.synthesizeMouseAtCenter(button, {}, recurrenceWindow);
+ }
+
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+
+ // Create event recurring on a weekly basis
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: "Event", repeat: setRecurrence });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Verify in the day view that events were created for Monday through Wednesday
+ for (let i = 0; i < 3; i++) {
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ }
+
+ // No event should have been created on Thursday because it was not selected
+ await dayView.waitForNoEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ // An event should have been created for Friday because it was selected
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ // No event should have been created on Saturday due to four event limit
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ // Validate event creation and lack of Saturday event in week view
+ await CalendarTestUtils.setCalendarView(window, "week");
+
+ for (let i = 2; i < 5; i++) {
+ await weekView.waitForEventBoxAt(window, i, 1);
+ }
+
+ // No event Thursday or Saturday, event on Friday
+ await weekView.waitForNoEventBoxAt(window, 5, 1);
+ await weekView.waitForEventBoxAt(window, 6, 1);
+ await weekView.waitForNoEventBoxAt(window, 7, 1);
+
+ // Validate event creation and lack of Saturday event in multiweek view
+ await CalendarTestUtils.setCalendarView(window, "multiweek");
+
+ for (let i = 2; i < 5; i++) {
+ await multiweekView.waitForItemAt(window, 1, i, 1);
+ }
+
+ // No event Thursday or Saturday, event on Friday
+ await multiweekView.waitForNoItemAt(window, 1, 5, 1);
+ await multiweekView.waitForItemAt(window, 1, 6, 1);
+ await multiweekView.waitForNoItemAt(window, 1, 7, 1);
+
+ // Validate event creation and lack of Saturday event in month view
+ await CalendarTestUtils.setCalendarView(window, "month");
+
+ for (let i = 2; i < 5; i++) {
+ // This should be the second week in the month
+ await monthView.waitForItemAt(window, 2, i, 1);
+ }
+
+ // No event Thursday or Saturday, event on Friday
+ await monthView.waitForNoItemAt(window, 2, 5, 1);
+ await monthView.waitForItemAt(window, 2, 6, 1);
+ await monthView.waitForNoItemAt(window, 2, 7, 1);
+
+ // Delete event
+ let box = await monthView.waitForItemAt(window, 2, 2, 1);
+ EventUtils.synthesizeMouseAtCenter(box, {}, window);
+ await handleDeleteOccurrencePrompt(window, box, true);
+
+ // All occurrences should have been deleted
+ for (let i = 2; i < 5; i++) {
+ await monthView.waitForNoItemAt(window, 2, i, 1);
+ }
+
+ await monthView.waitForNoItemAt(window, 2, 6, 1);
+});
+
+/*
+ * This test is intended to catch instances in which we aren't correctly setting
+ * the week start value of recurrences. For example, if the user has set their
+ * week to start on Saturday, then creates a recurring event running every other
+ * Saturday, Sunday, and Monday, they expect to see events on the initial
+ * Saturday, Sunday, Monday, skip a week, repeat. However, week start defaults
+ * to Monday, so if it is not correctly set, they would see events on the
+ * initial Saturday and Sunday, nothing on Monday, but an event on the following
+ * Monday.
+ */
+add_task(async function testRecurrenceAcrossWeekStart() {
+ // Sanity check that we're not testing against a default value
+ const initialWeekStart = Services.prefs.getIntPref("calendar.week.start", 0);
+ Assert.notEqual(initialWeekStart, 6, "week start should not be Saturday");
+
+ // Set week start to Saturday
+ Services.prefs.setIntPref("calendar.week.start", 6);
+ registerCleanupFunction(() => {
+ Services.prefs.setIntPref("calendar.week.start", initialWeekStart);
+ });
+
+ async function setRecurrence(recurrenceWindow) {
+ let recurrenceDocument = recurrenceWindow.document;
+
+ // Select weekly recurrence
+ await menulistSelect(recurrenceDocument.getElementById("period-list"), "1");
+
+ // Recur every two weeks
+ recurrenceDocument.getElementById("weekly-weeks").value = "2";
+
+ let satLabel = cal.l10n.getDateFmtString("day.7.Mmm");
+ let sunLabel = cal.l10n.getDateFmtString("day.1.Mmm");
+ let monLabel = cal.l10n.getDateFmtString("day.2.Mmm");
+
+ let dayPicker = recurrenceDocument.getElementById("daypicker-weekday");
+
+ // Selected date is a Saturday, so it should already be selected
+ Assert.ok(
+ dayPicker.querySelector(`[label="${satLabel}"]`).checked,
+ "Saturday should already be checked"
+ );
+
+ // Select Sunday and Monday as additional days for event occurrences
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${sunLabel}"]`),
+ {},
+ recurrenceWindow
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${monLabel}"]`),
+ {},
+ recurrenceWindow
+ );
+
+ // Create a total of six events
+ EventUtils.synthesizeMouseAtCenter(
+ recurrenceDocument.getElementById("recurrence-range-for"),
+ {},
+ recurrenceWindow
+ );
+ recurrenceDocument.getElementById("repeat-ntimes-count").value = "6";
+
+ let button = recurrenceDocument.querySelector("dialog").getButton("accept");
+ button.scrollIntoView();
+ // Close dialog
+ EventUtils.synthesizeMouseAtCenter(button, {}, recurrenceWindow);
+ }
+
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2022, 10, 15);
+
+ // Create event recurring every other week
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: "Event", repeat: setRecurrence });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Open week view
+ await CalendarTestUtils.setCalendarView(window, "week");
+
+ // Verify events created on Saturday, Sunday, Monday of first week
+ for (let i = 1; i < 4; i++) {
+ await weekView.waitForEventBoxAt(window, i, 1);
+ }
+
+ // Verify no events created on Saturday, Sunday, Monday of second week
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ for (let i = 1; i < 4; i++) {
+ await weekView.waitForNoEventBoxAt(window, i, 1);
+ }
+
+ // Verify events created on Saturday, Sunday, Monday of third week
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ for (let i = 1; i < 4; i++) {
+ await weekView.waitForEventBoxAt(window, i, 1);
+ }
+});
diff --git a/comm/calendar/test/browser/recurrence/browser_weeklyUntil.js b/comm/calendar/test/browser/recurrence/browser_weeklyUntil.js
new file mode 100644
index 0000000000..c9780e9428
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_weeklyUntil.js
@@ -0,0 +1,175 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { formatDate, menulistSelect, saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+var { dayView, weekView, multiweekView, monthView } = CalendarTestUtils;
+
+const ENDDATE = cal.createDateTime("20090126T000000Z"); // Last Monday in month.
+const HOUR = 8;
+
+add_task(async function testWeeklyUntilRecurrence() {
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5); // Monday
+
+ // Create weekly recurring event.
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: "Event", repeat: setRecurrence });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Check day view.
+ for (let week = 0; week < 3; week++) {
+ // Monday
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 2);
+
+ // Wednesday
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 2);
+
+ // Friday
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 3);
+ }
+
+ // Monday, last occurrence
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 2);
+
+ // Wednesday
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ // Check week view.
+ await CalendarTestUtils.setCalendarView(window, "week");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+ for (let week = 0; week < 3; week++) {
+ // Monday
+ await weekView.waitForEventBoxAt(window, 2, 1);
+
+ // Wednesday
+ await weekView.waitForEventBoxAt(window, 4, 1);
+
+ // Friday
+ await weekView.waitForEventBoxAt(window, 6, 1);
+
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ }
+
+ // Monday, last occurrence
+ await weekView.waitForEventBoxAt(window, 2, 1);
+ // Wednesday
+ await weekView.waitForNoEventBoxAt(window, 4, 1);
+
+ // Check multiweek view.
+ await CalendarTestUtils.setCalendarView(window, "multiweek");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+ for (let week = 1; week < 4; week++) {
+ // Monday
+ await multiweekView.waitForItemAt(window, week, 2, 1);
+ // Wednesday
+ await multiweekView.waitForItemAt(window, week, 4, 1);
+ // Friday
+ await multiweekView.waitForItemAt(window, week, 6, 1);
+ }
+
+ // Monday, last occurrence
+ await multiweekView.waitForItemAt(window, 4, 2, 1);
+
+ // Wednesday
+ await multiweekView.waitForNoItemAt(window, 4, 4, 1);
+
+ // Check month view.
+ await CalendarTestUtils.setCalendarView(window, "month");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+ // starts on week 2 in month-view
+ for (let week = 2; week < 5; week++) {
+ // Monday
+ await monthView.waitForItemAt(window, week, 2, 1);
+ // Wednesday
+ await monthView.waitForItemAt(window, week, 4, 1);
+ // Friday
+ await monthView.waitForItemAt(window, week, 6, 1);
+ }
+
+ // Monday, last occurrence
+ await monthView.waitForItemAt(window, 5, 2, 1);
+
+ // Wednesday
+ await monthView.waitForNoItemAt(window, 5, 4, 1);
+
+ // Delete event.
+ let box = monthView.getItemAt(window, 2, 2, 1);
+ EventUtils.synthesizeMouseAtCenter(box, {}, window);
+ await handleDeleteOccurrencePrompt(window, box, true);
+ await monthView.waitForNoItemAt(window, 2, 2, 1);
+
+ Assert.ok(true, "Test ran to completion");
+});
+
+async function setRecurrence(recurrenceWindow) {
+ let recurrenceDocument = recurrenceWindow.document;
+
+ // weekly
+ await menulistSelect(recurrenceDocument.getElementById("period-list"), "1");
+
+ let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+ let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+ let fri = cal.l10n.getDateFmtString("day.6.Mmm");
+
+ let dayPicker = recurrenceDocument.getElementById("daypicker-weekday");
+
+ // Starting from Monday so it should be checked.
+ Assert.ok(dayPicker.querySelector(`[label="${mon}"]`).checked, "mon checked");
+ // Check Wednesday and Friday too.
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${wed}"]`),
+ {},
+ recurrenceWindow
+ );
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${fri}"]`),
+ {},
+ recurrenceWindow
+ );
+
+ // Set until date.
+ EventUtils.synthesizeMouseAtCenter(
+ recurrenceDocument.getElementById("recurrence-range-until"),
+ {},
+ recurrenceWindow
+ );
+
+ // Delete previous date.
+ let untilInput = recurrenceDocument.getElementById("repeat-until-date");
+ untilInput.focus();
+ EventUtils.synthesizeKey("a", { accelKey: true }, recurrenceWindow);
+ untilInput.focus();
+ EventUtils.synthesizeKey("VK_DELETE", {}, recurrenceWindow);
+
+ let endDateString = formatDate(ENDDATE);
+ EventUtils.sendString(endDateString, recurrenceWindow);
+
+ // Move focus to ensure the date is selected.
+ untilInput.focus();
+ EventUtils.synthesizeKey("VK_TAB", {}, recurrenceWindow);
+
+ let button = recurrenceDocument.querySelector("dialog").getButton("accept");
+ button.scrollIntoView();
+ // Close dialog.
+ EventUtils.synthesizeMouseAtCenter(button, {}, recurrenceWindow);
+}
diff --git a/comm/calendar/test/browser/recurrence/browser_weeklyWithException.js b/comm/calendar/test/browser/recurrence/browser_weeklyWithException.js
new file mode 100644
index 0000000000..fbe007ea45
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/browser_weeklyWithException.js
@@ -0,0 +1,264 @@
+/* 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/. */
+
+var { handleDeleteOccurrencePrompt } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarUtils.jsm"
+);
+
+var { menulistSelect, saveAndCloseItemDialog, setData } = ChromeUtils.import(
+ "resource://testing-common/calendar/ItemEditingHelpers.jsm"
+);
+
+var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
+
+var { dayView, weekView, multiweekView, monthView } = CalendarTestUtils;
+
+const HOUR = 8;
+const STARTDATE = cal.createDateTime("20090106T000000Z");
+const TITLE = "Event";
+
+add_task(async function testWeeklyWithExceptionRecurrence() {
+ let calendar = CalendarTestUtils.createCalendar();
+ registerCleanupFunction(() => {
+ CalendarTestUtils.removeCalendar(calendar);
+ });
+
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+
+ // Create weekly recurring event.
+ let eventBox = dayView.getHourBoxAt(window, HOUR);
+ let { dialogWindow, iframeWindow } = await CalendarTestUtils.editNewEvent(window, eventBox);
+ await setData(dialogWindow, iframeWindow, { title: TITLE, repeat: setRecurrence });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ let eventItem = await dayView.waitForEventBoxAt(window, 1);
+ let icon = eventItem.querySelector(".item-recurrence-icon");
+ Assert.equal(icon.src, "chrome://messenger/skin/icons/new/recurrence.svg");
+ Assert.ok(!icon.hidden);
+
+ // Move 5th January occurrence to 6th January.
+ ({ dialogWindow, iframeWindow } = await dayView.editEventOccurrenceAt(window, 1));
+ await setData(dialogWindow, iframeWindow, {
+ title: TITLE,
+ startdate: STARTDATE,
+ enddate: STARTDATE,
+ });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ await CalendarTestUtils.goToDate(window, 2009, 1, 6);
+ eventItem = await dayView.waitForEventBoxAt(window, 1);
+ icon = eventItem.querySelector(".item-recurrence-icon");
+ Assert.equal(icon.src, "chrome://messenger/skin/icons/new/recurrence-exception.svg");
+
+ // Change recurrence rule.
+ await CalendarTestUtils.goToDate(window, 2009, 1, 7);
+ ({ dialogWindow, iframeWindow } = await dayView.editEventOccurrencesAt(window, 1));
+ await setData(dialogWindow, iframeWindow, {
+ title: "Event",
+ repeat: changeRecurrence,
+ });
+ await saveAndCloseItemDialog(dialogWindow);
+
+ // Check two weeks.
+ // day view
+ await CalendarTestUtils.setCalendarView(window, "day");
+
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ await CalendarTestUtils.calendarViewForward(window, 1);
+
+ // Assert exactly two.
+ Assert.ok(!!(await dayView.waitForEventBoxAt(window, 1)));
+ Assert.ok(!!(await dayView.waitForEventBoxAt(window, 2)));
+
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForNoEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForNoEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ // next week
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForNoEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForEventBoxAt(window, 1);
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ // week view
+ await CalendarTestUtils.setCalendarView(window, "week");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+
+ // Assert exactly two on Tuesday.
+ Assert.ok(!!(await weekView.waitForEventBoxAt(window, 3, 1)));
+ Assert.ok(!!(await weekView.waitForEventBoxAt(window, 3, 2)));
+
+ // Wait for the last occurrence because this appears last.
+ eventItem = await weekView.waitForEventBoxAt(window, 6, 1);
+ icon = eventItem.querySelector(".item-recurrence-icon");
+ Assert.equal(icon.src, "chrome://messenger/skin/icons/new/recurrence.svg");
+ Assert.ok(!icon.hidden);
+
+ Assert.ok(!weekView.getEventBoxAt(window, 1, 1));
+ Assert.ok(!weekView.getEventBoxAt(window, 2, 1));
+ Assert.ok(!!weekView.getEventBoxAt(window, 4, 1));
+ Assert.ok(!weekView.getEventBoxAt(window, 5, 1));
+ Assert.ok(!weekView.getEventBoxAt(window, 7, 1));
+
+ await CalendarTestUtils.calendarViewForward(window, 1);
+ await weekView.waitForEventBoxAt(window, 6, 1);
+ Assert.ok(!weekView.getEventBoxAt(window, 1, 1));
+ Assert.ok(!!weekView.getEventBoxAt(window, 2, 1));
+ Assert.ok(!!weekView.getEventBoxAt(window, 3, 1));
+ Assert.ok(!!weekView.getEventBoxAt(window, 4, 1));
+ Assert.ok(!weekView.getEventBoxAt(window, 5, 1));
+ Assert.ok(!weekView.getEventBoxAt(window, 7, 1));
+
+ // multiweek view
+ await CalendarTestUtils.setCalendarView(window, "multiweek");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 5);
+ // Wait for the first items, then check the ones not to be present.
+ // Assert exactly two.
+ await multiweekView.waitForItemAt(window, 1, 3, 1, 1);
+ Assert.ok(multiweekView.getItemAt(window, 1, 3, 2, 1));
+ Assert.ok(!multiweekView.getItemAt(window, 1, 3, 3, 1));
+ // Then check no item on the 5th.
+ Assert.ok(!multiweekView.getItemAt(window, 1, 2, 1));
+ Assert.ok(multiweekView.getItemAt(window, 1, 4, 1));
+ Assert.ok(!multiweekView.getItemAt(window, 1, 5, 1));
+ Assert.ok(multiweekView.getItemAt(window, 1, 6, 1));
+ Assert.ok(!multiweekView.getItemAt(window, 1, 7, 1));
+
+ Assert.ok(!multiweekView.getItemAt(window, 2, 1, 1));
+ Assert.ok(multiweekView.getItemAt(window, 2, 2, 1));
+ Assert.ok(multiweekView.getItemAt(window, 2, 3, 1));
+ Assert.ok(multiweekView.getItemAt(window, 2, 4, 1));
+ Assert.ok(!multiweekView.getItemAt(window, 2, 5, 1));
+ Assert.ok(multiweekView.getItemAt(window, 2, 6, 1));
+ Assert.ok(!multiweekView.getItemAt(window, 2, 7, 1));
+
+ eventItem = multiweekView.getItemAt(window, 2, 4, 1);
+ icon = eventItem.querySelector(".item-recurrence-icon");
+ Assert.equal(icon.src, "chrome://messenger/skin/icons/new/recurrence.svg");
+ Assert.ok(!icon.hidden);
+
+ // month view
+ await CalendarTestUtils.setCalendarView(window, "month");
+ // Wait for the first items, then check the ones not to be present.
+ // Assert exactly two.
+ // start on the second week
+ await monthView.waitForItemAt(window, 2, 3, 1);
+ Assert.ok(monthView.getItemAt(window, 2, 3, 2));
+ Assert.ok(!monthView.getItemAt(window, 2, 3, 3));
+ // Then check no item on the 5th.
+ Assert.ok(!monthView.getItemAt(window, 2, 2, 1));
+ Assert.ok(monthView.getItemAt(window, 2, 4, 1));
+ Assert.ok(!monthView.getItemAt(window, 2, 5, 1));
+ Assert.ok(monthView.getItemAt(window, 2, 6, 1));
+ Assert.ok(!monthView.getItemAt(window, 2, 7, 1));
+
+ Assert.ok(!monthView.getItemAt(window, 3, 1, 1));
+ Assert.ok(monthView.getItemAt(window, 3, 2, 1));
+ Assert.ok(monthView.getItemAt(window, 3, 3, 1));
+ Assert.ok(monthView.getItemAt(window, 3, 4, 1));
+ Assert.ok(!monthView.getItemAt(window, 3, 5, 1));
+ Assert.ok(monthView.getItemAt(window, 3, 6, 1));
+ Assert.ok(!monthView.getItemAt(window, 3, 7, 1));
+
+ eventItem = monthView.getItemAt(window, 3, 6, 1);
+ icon = eventItem.querySelector(".item-recurrence-icon");
+ Assert.equal(icon.src, "chrome://messenger/skin/icons/new/recurrence.svg");
+ Assert.ok(!icon.hidden);
+
+ // Delete event.
+ await CalendarTestUtils.setCalendarView(window, "day");
+ await CalendarTestUtils.goToDate(window, 2009, 1, 12);
+ eventBox = await dayView.waitForEventBoxAt(window, 1);
+ EventUtils.synthesizeMouseAtCenter(eventBox, {}, window);
+ await handleDeleteOccurrencePrompt(window, eventBox, true);
+ await dayView.waitForNoEventBoxAt(window, 1);
+
+ Assert.ok(true, "Test ran to completion");
+});
+
+async function setRecurrence(recurrenceWindow) {
+ let recurrenceDocument = recurrenceWindow.document;
+
+ // weekly
+ await menulistSelect(recurrenceDocument.getElementById("period-list"), "1");
+
+ let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+ let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+ let fri = cal.l10n.getDateFmtString("day.6.Mmm");
+
+ let dayPicker = recurrenceDocument.getElementById("daypicker-weekday");
+
+ // Starting from Monday so it should be checked.
+ Assert.ok(dayPicker.querySelector(`[label="${mon}"]`).checked, "mon checked");
+
+ // Check Wednesday and Friday too.
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${wed}"]`),
+ {},
+ recurrenceWindow
+ );
+ Assert.ok(dayPicker.querySelector(`[label="${wed}"]`).checked, "wed checked");
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${fri}"]`),
+ {},
+ recurrenceWindow
+ );
+ Assert.ok(dayPicker.querySelector(`[label="${fri}"]`).checked, "fri checked");
+
+ let button = recurrenceDocument.querySelector("dialog").getButton("accept");
+ button.scrollIntoView();
+ // Close dialog.
+ EventUtils.synthesizeMouseAtCenter(button, {}, recurrenceWindow);
+}
+
+async function changeRecurrence(recurrenceWindow) {
+ let recurrenceDocument = recurrenceWindow.document;
+
+ // weekly
+ await menulistSelect(recurrenceDocument.getElementById("period-list"), "1");
+
+ let mon = cal.l10n.getDateFmtString("day.2.Mmm");
+ let tue = cal.l10n.getDateFmtString("day.3.Mmm");
+ let wed = cal.l10n.getDateFmtString("day.4.Mmm");
+ let fri = cal.l10n.getDateFmtString("day.6.Mmm");
+
+ let dayPicker = recurrenceDocument.getElementById("daypicker-weekday");
+
+ // Check old rule.
+ // Starting from Monday so it should be checked.
+ Assert.ok(dayPicker.querySelector(`[label="${mon}"]`).checked, "mon checked");
+ Assert.ok(dayPicker.querySelector(`[label="${wed}"]`).checked, "wed checked");
+ Assert.ok(dayPicker.querySelector(`[label="${fri}"]`).checked, "fri checked");
+
+ // Check Tuesday.
+ EventUtils.synthesizeMouseAtCenter(
+ dayPicker.querySelector(`[label="${tue}"]`),
+ {},
+ recurrenceWindow
+ );
+ Assert.ok(dayPicker.querySelector(`[label="${tue}"]`).checked, "tue checked");
+
+ let button = recurrenceDocument.querySelector("dialog").getButton("accept");
+ button.scrollIntoView();
+ // Close dialog.
+ EventUtils.synthesizeMouseAtCenter(button, {}, recurrenceWindow);
+}
diff --git a/comm/calendar/test/browser/recurrence/head.js b/comm/calendar/test/browser/recurrence/head.js
new file mode 100644
index 0000000000..efcee250b6
--- /dev/null
+++ b/comm/calendar/test/browser/recurrence/head.js
@@ -0,0 +1,26 @@
+/* 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/. */
+
+// The tests in this folder frequently take too long. Give them more time.
+requestLongerTimeout(2);
+
+var { CalendarTestUtils } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarTestUtils.jsm"
+);
+
+/* globals toggleOrientation */
+
+let isRotated =
+ document.getElementById("calendar_toggle_orientation_command").getAttribute("checked") == "true";
+let shouldBeRotated = Services.prefs.getBoolPref("calendar.test.rotateViews", false);
+
+if (isRotated != shouldBeRotated) {
+ toggleOrientation();
+}
+
+const calendarViewsInitialState = CalendarTestUtils.saveCalendarViewsState(window);
+
+registerCleanupFunction(async () => {
+ await CalendarTestUtils.restoreCalendarViewsState(window, calendarViewsInitialState);
+});