diff options
Diffstat (limited to '')
-rw-r--r-- | comm/calendar/test/browser/browser_todayPane.js | 820 |
1 files changed, 820 insertions, 0 deletions
diff --git a/comm/calendar/test/browser/browser_todayPane.js b/comm/calendar/test/browser/browser_todayPane.js new file mode 100644 index 0000000000..8ad9141815 --- /dev/null +++ b/comm/calendar/test/browser/browser_todayPane.js @@ -0,0 +1,820 @@ +/* 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/. */ + +/* globals TodayPane */ + +var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); +var { formatDate, formatTime } = ChromeUtils.import( + "resource://testing-common/calendar/ItemEditingHelpers.jsm" +); + +XPCOMUtils.defineLazyModuleGetters(this, { + CalDateTime: "resource:///modules/CalDateTime.jsm", + CalEvent: "resource:///modules/CalEvent.jsm", + CalRecurrenceInfo: "resource:///modules/CalRecurrenceInfo.jsm", +}); + +let calendar = CalendarTestUtils.createCalendar(); +Services.prefs.setIntPref("calendar.agenda.days", 7); +registerCleanupFunction(() => { + CalendarTestUtils.removeCalendar(calendar); + Services.prefs.clearUserPref("calendar.agenda.days"); +}); + +let today = cal.dtz.now(); +let startHour = today.hour; +today.hour = today.minute = today.second = 0; + +let todayPanePanel = document.getElementById("today-pane-panel"); +let todayPaneStatusButton = document.getElementById("calendar-status-todaypane-button"); + +// Go to mail tab. +selectFolderTab(); + +// Verify today pane open. +if (todayPanePanel.hasAttribute("collapsed")) { + EventUtils.synthesizeMouseAtCenter(todayPaneStatusButton, {}); +} +Assert.ok(!todayPanePanel.hasAttribute("collapsed"), "Today Pane is open"); + +// Verify today pane's date. +Assert.equal(document.getElementById("datevalue-label").value, today.day, "Today Pane shows today"); + +async function addEvent(title, relativeStart, relativeEnd, isAllDay) { + let event = new CalEvent(); + event.id = cal.getUUID(); + event.title = title; + event.startDate = today.clone(); + event.startDate.addDuration(cal.createDuration(relativeStart)); + event.startDate.isDate = isAllDay; + event.endDate = today.clone(); + event.endDate.addDuration(cal.createDuration(relativeEnd)); + event.endDate.isDate = isAllDay; + return calendar.addItem(event); +} + +function checkEvent(row, { dateHeader, time, title, relative, overlap, classes = [] }) { + let dateHeaderElement = row.querySelector(".agenda-date-header"); + if (dateHeader) { + Assert.ok(BrowserTestUtils.is_visible(dateHeaderElement), "date header is visible"); + if (dateHeader instanceof CalDateTime || dateHeader instanceof Ci.calIDateTime) { + dateHeader = cal.dtz.formatter.formatDateLongWithoutYear(dateHeader); + } + Assert.equal(dateHeaderElement.textContent, dateHeader, "date header has correct value"); + } else { + Assert.ok(BrowserTestUtils.is_hidden(dateHeaderElement), "date header is hidden"); + } + + let calendarElement = row.querySelector(".agenda-listitem-calendar"); + let timeElement = row.querySelector(".agenda-listitem-time"); + if (time) { + Assert.ok(BrowserTestUtils.is_visible(calendarElement), "calendar is visible"); + Assert.ok(BrowserTestUtils.is_visible(timeElement), "time is visible"); + if (time instanceof CalDateTime || time instanceof Ci.calIDateTime) { + time = cal.dtz.formatter.formatTime(time); + } + Assert.equal(timeElement.textContent, time, "time has correct value"); + } else if (time === "") { + Assert.ok(BrowserTestUtils.is_visible(calendarElement), "calendar is visible"); + Assert.ok(BrowserTestUtils.is_hidden(timeElement), "time is hidden"); + } else { + Assert.ok(BrowserTestUtils.is_hidden(calendarElement), "calendar is hidden"); + Assert.ok(BrowserTestUtils.is_hidden(timeElement), "time is hidden"); + } + + let titleElement = row.querySelector(".agenda-listitem-title"); + Assert.ok(BrowserTestUtils.is_visible(titleElement), "title is visible"); + Assert.equal(titleElement.textContent, title, "title has correct value"); + + let relativeElement = row.querySelector(".agenda-listitem-relative"); + if (Array.isArray(relative)) { + Assert.ok(BrowserTestUtils.is_visible(relativeElement), "relative time is visible"); + Assert.report( + !relative.includes(relativeElement.textContent), + relative, + relativeElement.textContent, + "relative time is correct", + "includes" + ); + } else if (relative !== undefined) { + Assert.ok(BrowserTestUtils.is_hidden(relativeElement), "relative time is hidden"); + } + + let overlapElement = row.querySelector(".agenda-listitem-overlap"); + if (overlap) { + Assert.ok(BrowserTestUtils.is_visible(overlapElement), "overlap is visible"); + Assert.equal( + overlapElement.src, + `chrome://messenger/skin/icons/new/event-${overlap}.svg`, + "overlap has correct image" + ); + Assert.equal( + overlapElement.dataset.l10nId, + `calendar-editable-item-multiday-event-icon-${overlap}`, + "overlap has correct alt text" + ); + } else { + Assert.ok(BrowserTestUtils.is_hidden(overlapElement), "overlap is hidden"); + } + + for (let className of classes) { + Assert.ok(row.classList.contains(className), `row has ${className} class`); + } +} + +function checkEvents(...expectedEvents) { + Assert.equal(TodayPane.agenda.rowCount, expectedEvents.length, "expected number of rows shown"); + for (let i = 0; i < expectedEvents.length; i++) { + Assert.ok(TodayPane.agenda.rows[i].getAttribute("is"), "agenda-listitem"); + checkEvent(TodayPane.agenda.rows[i], expectedEvents[i]); + } +} + +add_task(async function testBasicAllDay() { + let todaysEvent = await addEvent("Today's Event", "P0D", "P1D", true); + checkEvents({ dateHeader: "Today", title: "Today's Event" }); + + let tomorrowsEvent = await addEvent("Tomorrow's Event", "P1D", "P2D", true); + checkEvents( + { dateHeader: "Today", title: "Today's Event" }, + { dateHeader: "Tomorrow", title: "Tomorrow's Event" } + ); + + let events = []; + for (let i = 2; i < 7; i++) { + events.push(await addEvent(`Event ${i + 1}`, `P${i}D`, `P${i + 1}D`, true)); + checkEvents( + { dateHeader: "Today", title: "Today's Event" }, + { dateHeader: "Tomorrow", title: "Tomorrow's Event" }, + ...events.map(e => { + return { dateHeader: e.startDate, title: e.title }; + }) + ); + } + + await calendar.deleteItem(todaysEvent); + checkEvents( + { dateHeader: "Tomorrow", title: "Tomorrow's Event" }, + ...events.map(e => { + return { dateHeader: e.startDate, title: e.title }; + }) + ); + await calendar.deleteItem(tomorrowsEvent); + checkEvents( + ...events.map(e => { + return { dateHeader: e.startDate, title: e.title }; + }) + ); + + while (events.length) { + await calendar.deleteItem(events.shift()); + checkEvents( + ...events.map(e => { + return { dateHeader: e.startDate, title: e.title }; + }) + ); + } +}); + +add_task(async function testBasic() { + let time = today.clone(); + time.hour = 23; + + let todaysEvent = await addEvent("Today's Event", "P0DT23H", "P1D"); + checkEvents({ dateHeader: "Today", time, title: "Today's Event" }); + + let tomorrowsEvent = await addEvent("Tomorrow's Event", "P1DT23H", "P2D"); + checkEvents( + { dateHeader: "Today", time, title: "Today's Event" }, + { dateHeader: "Tomorrow", time, title: "Tomorrow's Event" } + ); + + let events = []; + for (let i = 2; i < 7; i++) { + events.push(await addEvent(`Event ${i + 1}`, `P${i}DT23H`, `P${i + 1}D`)); + checkEvents( + { dateHeader: "Today", time, title: "Today's Event" }, + { dateHeader: "Tomorrow", time, title: "Tomorrow's Event" }, + ...events.map(e => { + return { dateHeader: e.startDate, time, title: e.title }; + }) + ); + } + + await calendar.deleteItem(todaysEvent); + checkEvents( + { dateHeader: "Tomorrow", time, title: "Tomorrow's Event" }, + ...events.map(e => { + return { dateHeader: e.startDate, time, title: e.title }; + }) + ); + await calendar.deleteItem(tomorrowsEvent); + checkEvents( + ...events.map(e => { + return { dateHeader: e.startDate, time, title: e.title }; + }) + ); + + while (events.length) { + await calendar.deleteItem(events.shift()); + checkEvents( + ...events.map(e => { + return { dateHeader: e.startDate, time, title: e.title }; + }) + ); + } +}); + +/** + * Adds and removes events in a different order from which they occur. + * This checks that the events are inserted in the right place, and that the + * date header is shown/hidden appropriately. + */ +add_task(async function testSortOrder() { + let afternoonEvent = await addEvent("Afternoon Event", "P1DT13H", "P1DT17H"); + checkEvents({ + dateHeader: "Tomorrow", + time: afternoonEvent.startDate, + title: "Afternoon Event", + }); + + let morningEvent = await addEvent("Morning Event", "P1DT8H", "P1DT12H"); + checkEvents( + { dateHeader: "Tomorrow", time: morningEvent.startDate, title: "Morning Event" }, + { time: afternoonEvent.startDate, title: "Afternoon Event" } + ); + + let allDayEvent = await addEvent("All Day Event", "P1D", "P2D", true); + checkEvents( + { dateHeader: "Tomorrow", title: "All Day Event" }, + { time: morningEvent.startDate, title: "Morning Event" }, + { time: afternoonEvent.startDate, title: "Afternoon Event" } + ); + + let eveningEvent = await addEvent("Evening Event", "P1DT18H", "P1DT22H"); + checkEvents( + { dateHeader: "Tomorrow", title: "All Day Event" }, + { time: morningEvent.startDate, title: "Morning Event" }, + { time: afternoonEvent.startDate, title: "Afternoon Event" }, + { time: eveningEvent.startDate, title: "Evening Event" } + ); + + await calendar.deleteItem(afternoonEvent); + checkEvents( + { dateHeader: "Tomorrow", title: "All Day Event" }, + { time: morningEvent.startDate, title: "Morning Event" }, + { time: eveningEvent.startDate, title: "Evening Event" } + ); + + await calendar.deleteItem(morningEvent); + checkEvents( + { dateHeader: "Tomorrow", title: "All Day Event" }, + { time: eveningEvent.startDate, title: "Evening Event" } + ); + + await calendar.deleteItem(allDayEvent); + checkEvents({ + dateHeader: "Tomorrow", + time: eveningEvent.startDate, + title: "Evening Event", + }); + + await calendar.deleteItem(eveningEvent); + checkEvents(); +}); + +/** + * Check events that begin and end on different days inside the date range. + * All-day events are still sorted ahead of non-all-day events. + */ +add_task(async function testOverlapInside() { + let allDayEvent = await addEvent("All Day Event", "P0D", "P2D", true); + checkEvents( + { dateHeader: "Today", title: "All Day Event", overlap: "start" }, + { dateHeader: "Tomorrow", title: "All Day Event", overlap: "end" } + ); + + let timedEvent = await addEvent("Timed Event", "P1H", "P1D23H"); + checkEvents( + { dateHeader: "Today", title: "All Day Event", overlap: "start" }, + { time: timedEvent.startDate, title: "Timed Event", overlap: "start" }, + { dateHeader: "Tomorrow", title: "All Day Event", overlap: "end" }, + { time: timedEvent.endDate, title: "Timed Event", overlap: "end" } + ); + + await calendar.deleteItem(allDayEvent); + await calendar.deleteItem(timedEvent); +}); + +/** + * Check events that begin and end on different days and that end at midnight. + * The list item for the end of the event should be the last one on the day + * before the end midnight, and its time label should display "24:00". + */ +add_task(async function testOverlapEndAtMidnight() { + // Start with an event that begins outside the displayed dates. + + let timedEvent = await addEvent("Timed Event", "-P1D", "P1D"); + // Ends an hour before `timedEvent` to prove the ordering is correct. + let duringEvent = await addEvent("During Event", "P22H", "P23H"); + // Starts at the same time as `timedEvent` ends to prove the ordering is correct. + let nextEvent = await addEvent("Next Event", "P1D", "P2D", true); + + checkEvents( + { dateHeader: "Today", time: duringEvent.startDate, title: "During Event" }, + { + // Should show "24:00" as the time and end today. + time: cal.dtz.formatter.formatTime(timedEvent.endDate, true), + title: "Timed Event", + overlap: "end", + }, + { dateHeader: "Tomorrow", title: "Next Event" } + ); + + // Move the event fully into the displayed range. + + let timedClone = timedEvent.clone(); + timedClone.startDate.day += 2; + timedClone.endDate.day += 2; + await calendar.modifyItem(timedClone, timedEvent); + + let duringClone = duringEvent.clone(); + duringClone.startDate.day += 2; + duringClone.endDate.day += 2; + await calendar.modifyItem(duringClone, duringEvent); + + let nextClone = nextEvent.clone(); + nextClone.startDate.day += 2; + nextClone.endDate.day += 2; + await calendar.modifyItem(nextClone, nextEvent); + + let realEndDate = today.clone(); + realEndDate.day += 2; + checkEvents( + { + dateHeader: "Tomorrow", + time: timedClone.startDate, + title: "Timed Event", + overlap: "start", + }, + { dateHeader: realEndDate, time: duringClone.startDate, title: "During Event" }, + { + // Should show "24:00" as the time and end on the day after tomorrow. + time: cal.dtz.formatter.formatTime(timedClone.endDate, true), + title: "Timed Event", + overlap: "end", + }, + { dateHeader: nextClone.startDate, title: "Next Event" } + ); + + await calendar.deleteItem(timedClone); + await calendar.deleteItem(duringClone); + await calendar.deleteItem(nextClone); +}); + +/** + * Check events that begin and/or end outside the date range. Events that have + * already started are listed as "Today", but still sorted by start time. + * All-day events are still sorted ahead of non-all-day events. + */ +add_task(async function testOverlapOutside() { + let before = await addEvent("Starts Before", "-P1D", "P1D", true); + checkEvents({ dateHeader: "Today", title: "Starts Before", overlap: "end" }); + + let after = await addEvent("Ends After", "P0D", "P9D", true); + checkEvents( + { dateHeader: "Today", title: "Starts Before", overlap: "end" }, + { title: "Ends After", overlap: "start" } + ); + + let both = await addEvent("Beyond Start and End", "-P2D", "P9D", true); + checkEvents( + { dateHeader: "Today", title: "Beyond Start and End", overlap: "continue" }, + { title: "Starts Before", overlap: "end" }, + { title: "Ends After", overlap: "start" } + ); + + // Change `before` to begin earlier than `both`. They should swap places. + + let startClone = before.clone(); + startClone.startDate.day -= 2; + await calendar.modifyItem(startClone, before); + checkEvents( + { dateHeader: "Today", title: "Starts Before", overlap: "end" }, + { title: "Beyond Start and End", overlap: "continue" }, + { title: "Ends After", overlap: "start" } + ); + + let beforeWithTime = await addEvent("Starts Before with time", "-PT5H", "PT15H"); + checkEvents( + { dateHeader: "Today", title: "Starts Before", overlap: "end" }, + { title: "Beyond Start and End", overlap: "continue" }, + { title: "Ends After", overlap: "start" }, + // This is the end of the event so the end time is used. + { time: beforeWithTime.endDate, title: "Starts Before with time", overlap: "end" } + ); + + let afterWithTime = await addEvent("Ends After with time", "PT6H", "P8DT12H"); + checkEvents( + { dateHeader: "Today", title: "Starts Before", overlap: "end" }, + { title: "Beyond Start and End", overlap: "continue" }, + { title: "Ends After", overlap: "start" }, + { time: afterWithTime.startDate, title: "Ends After with time", overlap: "start" }, + // This is the end of the event so the end time is used. + { time: beforeWithTime.endDate, title: "Starts Before with time", overlap: "end" } + ); + + let bothWithTime = await addEvent("Beyond Start and End with time", "-P2DT10H", "P9DT1H"); + checkEvents( + { dateHeader: "Today", title: "Starts Before", overlap: "end" }, + { title: "Beyond Start and End", overlap: "continue" }, + { title: "Ends After", overlap: "start" }, + { time: "", title: "Beyond Start and End with time", overlap: "continue" }, + { time: afterWithTime.startDate, title: "Ends After with time", overlap: "start" }, + // This is the end of the event so the end time is used. + { time: beforeWithTime.endDate, title: "Starts Before with time", overlap: "end" } + ); + + await calendar.deleteItem(before); + await calendar.deleteItem(after); + await calendar.deleteItem(both); + await calendar.deleteItem(beforeWithTime); + await calendar.deleteItem(afterWithTime); + await calendar.deleteItem(bothWithTime); +}); + +/** + * Checks that events that happened earlier today are marked as in the past, + * and events happening now are marked as such. + * + * This test may fail if run within a minute either side of midnight. + * + * It would be nice to test that as time passes events are changed + * appropriately, but that means waiting around for minutes and probably won't + * be very reliable, so we don't do that. + */ +add_task(async function testActive() { + let now = cal.dtz.now(); + + let pastEvent = await addEvent("Past Event", "PT0M", "PT1M"); + let presentEvent = await addEvent("Present Event", `PT${now.hour}H`, `PT${now.hour + 1}H`); + let futureEvent = await addEvent("Future Event", "PT23H59M", "PT24H"); + checkEvents( + { dateHeader: "Today", time: pastEvent.startDate, title: "Past Event" }, + { time: presentEvent.startDate, title: "Present Event" }, + { time: futureEvent.startDate, title: "Future Event" } + ); + + let [pastRow, presentRow, futureRow] = TodayPane.agenda.rows; + Assert.ok(pastRow.classList.contains("agenda-listitem-past"), "past event is marked past"); + Assert.ok(!pastRow.classList.contains("agenda-listitem-now"), "past event is not marked now"); + Assert.ok( + !presentRow.classList.contains("agenda-listitem-past"), + "present event is not marked past" + ); + Assert.ok(presentRow.classList.contains("agenda-listitem-now"), "present event is marked now"); + Assert.ok( + !futureRow.classList.contains("agenda-listitem-past"), + "future event is not marked past" + ); + Assert.ok(!futureRow.classList.contains("agenda-listitem-now"), "future event is not marked now"); + + await calendar.deleteItem(pastEvent); + await calendar.deleteItem(presentEvent); + await calendar.deleteItem(futureEvent); +}); + +/** + * Checks events in different time zones are displayed correctly. + */ +add_task(async function testOtherTimeZones() { + // Johannesburg is UTC+2. + let johannesburg = cal.timezoneService.getTimezone("Africa/Johannesburg"); + // Panama is UTC-5. + let panama = cal.timezoneService.getTimezone("America/Panama"); + + // All-day events are displayed on the day of the event, the time zone is ignored. + + let allDayEvent = new CalEvent(); + allDayEvent.id = cal.getUUID(); + allDayEvent.title = "All-day event in Johannesburg"; + allDayEvent.startDate = cal.createDateTime(); + allDayEvent.startDate.resetTo(today.year, today.month, today.day + 1, 0, 0, 0, johannesburg); + allDayEvent.startDate.isDate = true; + allDayEvent.endDate = cal.createDateTime(); + allDayEvent.endDate.resetTo(today.year, today.month, today.day + 2, 0, 0, 0, johannesburg); + allDayEvent.endDate.isDate = true; + allDayEvent = await calendar.addItem(allDayEvent); + + checkEvents({ + dateHeader: "Tomorrow", + title: "All-day event in Johannesburg", + }); + + await calendar.deleteItem(allDayEvent); + + // The event time must be displayed in the local time zone, and the event must be sorted correctly. + + let beforeEvent = await addEvent("Before", "P1DT5H", "P1DT6H"); + let afterEvent = await addEvent("After", "P1DT7H", "P1DT8H"); + + let timedEvent = new CalEvent(); + timedEvent.id = cal.getUUID(); + timedEvent.title = "Morning in Johannesburg"; + timedEvent.startDate = cal.createDateTime(); + timedEvent.startDate.resetTo(today.year, today.month, today.day + 1, 8, 0, 0, johannesburg); + timedEvent.endDate = cal.createDateTime(); + timedEvent.endDate.resetTo(today.year, today.month, today.day + 1, 12, 0, 0, johannesburg); + timedEvent = await calendar.addItem(timedEvent); + + checkEvents( + { + dateHeader: "Tomorrow", + time: beforeEvent.startDate, + title: "Before", + }, + { + time: cal.dtz.formatter.formatTime(cal.createDateTime("20000101T060000Z")), // The date used here is irrelevant. + title: "Morning in Johannesburg", + }, + { + time: afterEvent.startDate, + title: "After", + } + ); + Assert.stringContains( + TodayPane.agenda.rows[1].querySelector(".agenda-listitem-time").getAttribute("datetime"), + "T08:00:00+02:00" + ); + + await calendar.deleteItem(beforeEvent); + await calendar.deleteItem(afterEvent); + await calendar.deleteItem(timedEvent); + + // Events that cross midnight in the local time zone (but not in the event time zone) + // must have a start row and an end row. + + let overnightEvent = new CalEvent(); + overnightEvent.id = cal.getUUID(); + overnightEvent.title = "Evening in Panama"; + overnightEvent.startDate = cal.createDateTime(); + overnightEvent.startDate.resetTo(today.year, today.month, today.day, 17, 0, 0, panama); + overnightEvent.endDate = cal.createDateTime(); + overnightEvent.endDate.resetTo(today.year, today.month, today.day, 23, 0, 0, panama); + overnightEvent = await calendar.addItem(overnightEvent); + + checkEvents( + { + dateHeader: "Today", + time: cal.dtz.formatter.formatTime(cal.createDateTime("20000101T220000Z")), // The date used here is irrelevant. + title: "Evening in Panama", + overlap: "start", + }, + { + dateHeader: "Tomorrow", + time: cal.dtz.formatter.formatTime(cal.createDateTime("20000101T040000Z")), // The date used here is irrelevant. + title: "Evening in Panama", + overlap: "end", + } + ); + Assert.stringContains( + TodayPane.agenda.rows[0].querySelector(".agenda-listitem-time").getAttribute("datetime"), + "T17:00:00-05:00" + ); + Assert.stringContains( + TodayPane.agenda.rows[1].querySelector(".agenda-listitem-time").getAttribute("datetime"), + "T23:00:00-05:00" + ); + + await calendar.deleteItem(overnightEvent); +}); + +/** + * Checks events in different time zones are displayed correctly. + */ +add_task(async function testRelativeTime() { + let formatter = new Intl.RelativeTimeFormat(undefined, { style: "short" }); + let now = cal.dtz.now(); + now.second = 0; + info(`The time is now ${now}`); + + let testData = [ + { + name: "two hours ago", + start: "-PT1H55M", + expected: { + classes: ["agenda-listitem-past"], + }, + minHour: 2, + }, + { + name: "one hour ago", + start: "-PT1H5M", + expected: { + classes: ["agenda-listitem-past"], + }, + minHour: 2, + }, + { + name: "23 minutes ago", + start: "-PT23M", + expected: { + classes: ["agenda-listitem-past"], + }, + minHour: 1, + }, + { + name: "now", + start: "-PT5M", + expected: { + relative: ["now"], + classes: ["agenda-listitem-now"], + }, + minHour: 1, + maxHour: 22, + }, + { + name: "19 minutes ahead", + start: "PT19M", + expected: { + relative: [formatter.format(19, "minute"), formatter.format(18, "minute")], + }, + maxHour: 22, + }, + { + name: "one hour ahead", + start: "PT1H25M", + expected: { + relative: [formatter.format(85, "minute"), formatter.format(84, "minute")], + }, + maxHour: 21, + }, + { + name: "one and half hours ahead", + start: "PT1H35M", + expected: { + relative: [formatter.format(2, "hour")], + }, + maxHour: 21, + }, + { + name: "two hours ahead", + start: "PT1H49M", + expected: { + relative: [formatter.format(2, "hour")], + }, + maxHour: 21, + }, + ]; + + let events = []; + let expectedEvents = []; + for (let { name, start, expected, minHour, maxHour } of testData) { + if (minHour && now.hour < minHour) { + info(`Skipping ${name} because it's too early.`); + continue; + } + if (maxHour && now.hour > maxHour) { + info(`Skipping ${name} because it's too late.`); + continue; + } + + let event = new CalEvent(); + event.id = cal.getUUID(); + event.title = name; + event.startDate = now.clone(); + event.startDate.addDuration(cal.createDuration(start)); + event.endDate = event.startDate.clone(); + event.endDate.addDuration(cal.createDuration("PT10M")); + events.push(await calendar.addItem(event)); + + expectedEvents.push({ ...expected, title: name, time: event.startDate }); + } + + expectedEvents[0].dateHeader = "Today"; + checkEvents(...expectedEvents); + + for (let event of events) { + await calendar.deleteItem(event); + } +}); + +/** + * Tests the today pane opens events in the summary dialog for both + * non-recurring and recurring events. + */ +add_task(async function testOpenEvent() { + let noRepeatEvent = new CalEvent(); + noRepeatEvent.id = "no repeat event"; + noRepeatEvent.title = "No Repeat Event"; + noRepeatEvent.startDate = today.clone(); + noRepeatEvent.startDate.hour = startHour; + noRepeatEvent.endDate = noRepeatEvent.startDate.clone(); + noRepeatEvent.endDate.hour++; + + let repeatEvent = new CalEvent(); + repeatEvent.id = "repeated event"; + repeatEvent.title = "Repeated Event"; + repeatEvent.startDate = today.clone(); + repeatEvent.startDate.hour = startHour; + repeatEvent.endDate = noRepeatEvent.startDate.clone(); + repeatEvent.endDate.hour++; + repeatEvent.recurrenceInfo = new CalRecurrenceInfo(repeatEvent); + repeatEvent.recurrenceInfo.appendRecurrenceItem( + cal.createRecurrenceRule("RRULE:FREQ=DAILY;COUNT=5") + ); + + for (let event of [noRepeatEvent, repeatEvent]) { + let addedEvent = await calendar.addItem(event); + + if (event == noRepeatEvent) { + Assert.equal(TodayPane.agenda.rowCount, 1); + } else { + Assert.equal(TodayPane.agenda.rowCount, 5); + } + Assert.equal( + TodayPane.agenda.rows[0].querySelector(".agenda-listitem-title").textContent, + event.title, + "event title is correct" + ); + + let dialogWindowPromise = CalendarTestUtils.waitForEventDialog(); + EventUtils.synthesizeMouseAtCenter(TodayPane.agenda.rows[0], { clickCount: 2 }); + + let dialogWindow = await dialogWindowPromise; + let docUri = dialogWindow.document.documentURI; + Assert.ok( + docUri === "chrome://calendar/content/calendar-summary-dialog.xhtml", + "event summary dialog shown" + ); + + await BrowserTestUtils.closeWindow(dialogWindow); + await calendar.deleteItem(addedEvent); + } +}); + +/** + * Tests that the "New Event" button begins creating an event on the date + * selected in the Today Pane. + */ +add_task(async function testNewEvent() { + async function checkEventDialogDate() { + let dialogWindowPromise = CalendarTestUtils.waitForEventDialog("edit"); + EventUtils.synthesizeMouseAtCenter(newEventButton, {}, window); + await dialogWindowPromise.then(async function (dialogWindow) { + let iframe = dialogWindow.document.querySelector("#calendar-item-panel-iframe"); + let iframeDocument = iframe.contentDocument; + + let startDate = iframeDocument.getElementById("event-starttime"); + Assert.equal( + startDate._datepicker._inputField.value, + formatDate(expectedDate), + "date should match the expected date" + ); + Assert.equal( + startDate._timepicker._inputField.value, + formatTime(expectedDate), + "time should be the next hour after now" + ); + + await BrowserTestUtils.closeWindow(dialogWindow); + }); + } + + let newEventButton = document.getElementById("todaypane-new-event-button"); + + // Check today with the "day" view. + + TodayPane.displayMiniSection("miniday"); + EventUtils.synthesizeMouseAtCenter(document.getElementById("today-button"), {}, window); + + let expectedDate = cal.dtz.now(); + expectedDate.hour++; + expectedDate.minute = 0; + + await checkEventDialogDate(); + + // Check tomorrow with the "day" view. + + EventUtils.synthesizeMouseAtCenter(document.getElementById("next-day-button"), {}, window); + expectedDate.day++; + + await checkEventDialogDate(); + + // Check today with the "month" view; + + TodayPane.displayMiniSection("minimonth"); + let minimonth = document.getElementById("today-minimonth"); + minimonth.value = new Date(); + expectedDate.day--; + + await checkEventDialogDate(); + + // Check a date in the past with the "month" view; + + minimonth.value = new Date(Date.UTC(2018, 8, 1)); + expectedDate.resetTo(2018, 8, 1, expectedDate.hour, 0, 0, cal.dtz.UTC); + + await checkEventDialogDate(); +}).__skipMe = new Date().getUTCHours() == 23; |