/* 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/. */ /** * Test dragging of events in the various calendar views. */ const { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm"); XPCOMUtils.defineLazyModuleGetters(this, { CalEvent: "resource:///modules/CalEvent.jsm", }); const calendar = CalendarTestUtils.createCalendar("Drag Test", "memory"); // Set a low number of hours to reduce pixel -> minute rounding errors. Services.prefs.setIntPref("calendar.view.visiblehours", 3); registerCleanupFunction(() => { CalendarTestUtils.removeCalendar(calendar); Services.prefs.clearUserPref("calendar.view.visiblehours"); // Reset the spaces toolbar to its default visible state. window.gSpacesToolbar.toggleToolbar(false); }); /** * Ensures that the window is maximised after switching dates. * * @param {calIDateTime} date - A date to navigate the view to. */ async function resetView(date, view) { window.goToDate(date); if (window.windowState != window.STATE_MAXIMIZED) { // The multi-day views adjust scrolling dynamically when they detect a // resize. Hook into the resize event and scroll after the adjustment. let resizePromise = BrowserTestUtils.waitForEvent(window, "resize"); window.maximize(); await resizePromise; } } /** * End the dragging of the event at the specified location. * * @param {number} day - The day to drop into. * @param {number} hour - The starting hour to drop to. * @param {number} topOffset - An offset to apply to the mouse position. */ function endDrag(day, hour, topOffset) { let view = window.currentView(); let hourElement; if (view.id == "day-view") { hourElement = CalendarTestUtils.dayView.getHourBoxAt(window, hour); } else { hourElement = CalendarTestUtils.weekView.getHourBoxAt(window, day, hour); } // We scroll to align the *end* of the hour element so we can avoid triggering // the auto-scroll when we synthesize mousemove below. // FIXME: Use and test auto scroll by holding mouseover at the view edges. CalendarTestUtils.scrollViewToTarget(hourElement, false); let hourRect = hourElement.getBoundingClientRect(); // We drop the event with some offset from the starting edge of the desired // hourElement. // NOTE: This may mean that the drop point may not be above the hourElement. // NOTE: We assume that the drop point is however still above the view. // Currently event "move" events get cancelled if the pointer leaves the view. let top = Math.round(hourRect.top + topOffset); let left = Math.round(hourRect.left + hourRect.width / 2); EventUtils.synthesizeMouseAtPoint(left, top, { type: "mousemove", shiftKey: true }, window); EventUtils.synthesizeMouseAtPoint(left, top, { type: "mouseup", shiftKey: true }, window); } /** * Simulates the dragging of an event box in a multi-day view to another * column, horizontally. * * @param {MozCalendarEventBox} eventBox - The event to start moving. * @param {number} day - The day to drop into. * @param {number} hour - The starting hour to drop to. */ function simulateDragToColumn(eventBox, day, hour) { // Scroll to align to the top of the view. CalendarTestUtils.scrollViewToTarget(eventBox, true); let sourceRect = eventBox.getBoundingClientRect(); // Start dragging from the center of the event box to avoid the gripbars. // NOTE: We assume that the eventBox's center is in view. let leftOffset = sourceRect.width / 2; // We round the mouse position to try and reduce rounding errors when // scrolling the view. let sourceTop = Math.round(sourceRect.top + sourceRect.height / 2); let sourceLeft = sourceRect.left + leftOffset; // Keep track of the exact offset. let topOffset = sourceTop - sourceRect.top; EventUtils.synthesizeMouseAtPoint( sourceLeft, sourceTop, // Hold shift to avoid snapping. { type: "mousedown", shiftKey: true }, window ); EventUtils.synthesizeMouseAtPoint( // We assume the location of the mouseout event does not matter, just as // long as the event box receives it. sourceLeft, sourceTop, { type: "mouseout", shiftKey: true }, window ); // End drag with the same offset from the starting edge. endDrag(day, hour, topOffset); } /** * Simulates the dragging of an event box via one of the gripbars. * * @param {MozCalendarEventBox} eventBox - The event to resize. * @param {"start"|"end"} - The side to grab. * @param {number} day - The day to move into. * @param {number} hour - The hour to move to. */ function simulateGripbarDrag(eventBox, side, day, hour) { // Scroll the edge of the box into view. CalendarTestUtils.scrollViewToTarget(eventBox, side == "start"); let gripbar = side == "start" ? eventBox.startGripbar : eventBox.endGripbar; let sourceRect = gripbar.getBoundingClientRect(); let sourceTop = sourceRect.top + sourceRect.height / 2; let sourceLeft = sourceRect.left + sourceRect.width / 2; // Hover to make the gripbar visible. EventUtils.synthesizeMouseAtPoint(sourceLeft, sourceTop, { type: "mouseover" }, window); EventUtils.synthesizeMouseAtPoint( sourceLeft, sourceTop, // Hold shift to avoid snapping. { type: "mousedown", shiftKey: true }, window ); // End the drag at the start of the hour. endDrag(day, hour, 0); } /** * Tests dragging an event item updates the event in the month view. */ add_task(async function testMonthViewDragEventItem() { let event = new CalEvent(); event.id = "1"; event.title = "Month View Event"; event.startDate = cal.createDateTime("20210316T000000Z"); event.endDate = cal.createDateTime("20210316T110000Z"); await CalendarTestUtils.setCalendarView(window, "month"); await calendar.addItem(event); await resetView(event.startDate); // Hide the spaces toolbar since it interferes with the calendar window.gSpacesToolbar.toggleToolbar(true); let eventItem = await CalendarTestUtils.monthView.waitForItemAt(window, 3, 3, 1); let dayBox = await CalendarTestUtils.monthView.getDayBox(window, 3, 2); let dragSession = Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService); dragSession.startDragSessionForTests(Ci.nsIDragService.DRAGDROP_ACTION_MOVE); let [result, dataTransfer] = EventUtils.synthesizeDragOver( eventItem, dayBox, undefined, undefined, eventItem.ownerGlobal, dayBox.ownerGlobal ); EventUtils.synthesizeDropAfterDragOver(result, dataTransfer, dayBox); dragSession.endDragSession(true); Assert.ok( !CalendarTestUtils.monthView.getItemAt(window, 3, 3, 1), "item removed from initial date" ); eventItem = await CalendarTestUtils.monthView.waitForItemAt(window, 3, 2, 1); Assert.ok(eventItem, "item moved to new date"); let { id, title, startDate, endDate } = eventItem.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210315T000000Z", "startDate is correct"); Assert.equal(endDate.icalString, "20210315T110000Z", "endDate is correct"); await calendar.deleteItem(eventItem.occurrence); }); /** * Tests dragging an event item updates the event in the multiweek view. */ add_task(async function testMultiWeekViewDragEventItem() { let event = new CalEvent(); event.id = "2"; event.title = "Multiweek View Event"; event.startDate = cal.createDateTime("20210316T000000Z"); event.endDate = cal.createDateTime("20210316T110000Z"); await CalendarTestUtils.setCalendarView(window, "multiweek"); await calendar.addItem(event); await resetView(event.startDate); let eventItem = await CalendarTestUtils.multiweekView.waitForItemAt(window, 1, 3, 1); let dayBox = await CalendarTestUtils.multiweekView.getDayBox(window, 1, 2); let dragSession = Cc["@mozilla.org/widget/dragservice;1"].getService(Ci.nsIDragService); dragSession.startDragSessionForTests(Ci.nsIDragService.DRAGDROP_ACTION_MOVE); let [result, dataTransfer] = EventUtils.synthesizeDragOver( eventItem, dayBox, undefined, undefined, eventItem.ownerGlobal, dayBox.ownerGlobal ); EventUtils.synthesizeDropAfterDragOver(result, dataTransfer, dayBox); dragSession.endDragSession(true); Assert.ok( !CalendarTestUtils.multiweekView.getItemAt(window, 1, 3, 1), "item removed from initial date" ); eventItem = await CalendarTestUtils.multiweekView.waitForItemAt(window, 1, 2, 1); Assert.ok(eventItem, "item moved to new date"); let { id, title, startDate, endDate } = eventItem.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210315T000000Z", "startDate is correct"); Assert.equal(endDate.icalString, "20210315T110000Z", "endDate is correct"); await calendar.deleteItem(eventItem.occurrence); }); /** * Tests dragging an event box to the previous day updates the event in the * week view. */ add_task(async function testWeekViewDragEventBoxToPreviousDay() { let event = new CalEvent(); event.id = "3"; event.title = "Week View Previous Day"; event.startDate = cal.createDateTime("20210316T020000Z"); event.endDate = cal.createDateTime("20210316T030000Z"); await CalendarTestUtils.setCalendarView(window, "week"); await calendar.addItem(event); await resetView(event.startDate); let eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 3, 1); simulateDragToColumn(eventBox, 2, 2); eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 2, 1); await TestUtils.waitForCondition( () => !CalendarTestUtils.weekView.getEventBoxAt(window, 3, 1), "Old position is empty" ); let { id, title, startDate, endDate } = eventBox.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210315T020000Z", "startDate is correct"); Assert.equal(endDate.icalString, "20210315T030000Z", "endDate is correct"); await calendar.deleteItem(eventBox.occurrence); }); /** * Tests dragging an event box to the following day updates the event in the * week view. */ add_task(async function testWeekViewDragEventBoxToFollowingDay() { let event = new CalEvent(); event.id = "4"; event.title = "Week View Following Day"; event.startDate = cal.createDateTime("20210316T020000Z"); event.endDate = cal.createDateTime("20210316T030000Z"); await CalendarTestUtils.setCalendarView(window, "week"); await calendar.addItem(event); await resetView(event.startDate); let eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 3, 1); simulateDragToColumn(eventBox, 4, 2); eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 4, 1); await TestUtils.waitForCondition( () => !CalendarTestUtils.weekView.getEventBoxAt(window, 3, 1), "Old position is empty" ); let { id, title, startDate, endDate } = eventBox.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210317T020000Z", "startDate is correct"); Assert.equal(endDate.icalString, "20210317T030000Z", "endDate is correct"); await calendar.deleteItem(eventBox.occurrence); }); /** * Tests dragging the top of an event box updates the start time in the week * view. */ add_task(async function testWeekViewDragEventBoxStartTime() { let event = new CalEvent(); event.id = "5"; event.title = "Week View Start"; event.startDate = cal.createDateTime("20210316T020000Z"); event.endDate = cal.createDateTime("20210316T030000Z"); await CalendarTestUtils.setCalendarView(window, "week"); await calendar.addItem(event); await resetView(event.startDate); let eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 3, 1); simulateGripbarDrag(eventBox, "start", 3, 1); eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 3, 1); let { id, title, startDate, endDate } = eventBox.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210316T010000Z", "startDate was changed"); Assert.equal(endDate.icalString, "20210316T030000Z", "endDate did not change"); await calendar.deleteItem(eventBox.occurrence); }); /** * Tests dragging the end of an event box changes the time in the week view. */ add_task(async function testWeekViewDragEventBoxEndTime() { let event = new CalEvent(); event.id = "6"; event.title = "Week View End"; event.startDate = cal.createDateTime("20210316T020000Z"); event.endDate = cal.createDateTime("20210316T030000Z"); await CalendarTestUtils.setCalendarView(window, "week"); await calendar.addItem(event); await resetView(event.startDate); let eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 3, 1); simulateGripbarDrag(eventBox, "end", 3, 6); eventBox = await CalendarTestUtils.weekView.waitForEventBoxAt(window, 3, 1); let { id, title, startDate, endDate } = eventBox.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210316T020000Z", "startDate did not change"); Assert.equal(endDate.icalString, "20210316T060000Z", "endDate was changed"); await calendar.deleteItem(eventBox.occurrence); }); /** * Tests dragging the top of an event box changes the start time in the day view. */ add_task(async function testDayViewDragEventBoxStartTime() { let event = new CalEvent(); event.id = "7"; event.title = "Day View Start"; event.startDate = cal.createDateTime("20210316T020000Z"); event.endDate = cal.createDateTime("20210316T030000Z"); await CalendarTestUtils.setCalendarView(window, "day"); await calendar.addItem(event); await resetView(event.startDate); let eventBox = await CalendarTestUtils.dayView.waitForEventBoxAt(window, 1); simulateGripbarDrag(eventBox, "start", 1, 1); eventBox = await CalendarTestUtils.dayView.waitForEventBoxAt(window, 1); let { id, title, startDate, endDate } = eventBox.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210316T010000Z", "startDate was changed"); Assert.equal(endDate.icalString, "20210316T030000Z", "endDate did not change"); await calendar.deleteItem(eventBox.occurrence); }); /** * Tests dragging the bottom of an event box changes the end time in the day * view. */ add_task(async function testDayViewDragEventBoxEndTime() { let event = new CalEvent(); event.id = "8"; event.title = "Day View End"; event.startDate = cal.createDateTime("20210316T020000Z"); event.endDate = cal.createDateTime("20210316T030000Z"); await CalendarTestUtils.setCalendarView(window, "day"); await calendar.addItem(event); await resetView(event.startDate); let eventBox = await CalendarTestUtils.dayView.waitForEventBoxAt(window, 1); simulateGripbarDrag(eventBox, "end", 1, 4); eventBox = await CalendarTestUtils.dayView.waitForEventBoxAt(window, 1); let { id, title, startDate, endDate } = eventBox.occurrence; Assert.equal(id, event.id, "id is correct"); Assert.equal(title, event.title, "title is correct"); Assert.equal(startDate.icalString, "20210316T020000Z", "startDate did not change"); Assert.equal(endDate.icalString, "20210316T040000Z", "endDate was changed"); await calendar.deleteItem(eventBox.occurrence); });