/* 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/. */ "use strict"; /** * Tests for ensuring the undo/redo options are enabled properly when * manipulating events. */ XPCOMUtils.defineLazyModuleGetters(this, { CalEvent: "resource:///modules/CalEvent.jsm", CalTransactionManager: "resource:///modules/CalTransactionManager.jsm", }); const calendar = CalendarTestUtils.createCalendar("Undo Redo Test"); const calTransManager = CalTransactionManager.getInstance(); /** * Checks the value of the "disabled" property for items in either the "Edit" * menu bar or the app menu. Display of the relevant menu is triggered first so * the UI code can update the respective items. * * @param {XULElement} element - The menu item we want to check, if its id begins * with "menu" then we assume it is in the menu * bar, if "appmenu" then the app menu. */ async function isDisabled(element) { let targetMenu = document.getElementById("menu_EditPopup"); let shownPromise = BrowserTestUtils.waitForEvent(targetMenu, "popupshown"); EventUtils.synthesizeMouseAtCenter(document.getElementById("menu_Edit"), {}); await shownPromise; let hiddenPromise = BrowserTestUtils.waitForEvent(targetMenu, "popuphidden"); let status = element.disabled; targetMenu.hidePopup(); await hiddenPromise; return status; } async function clickItem(element) { let targetMenu = document.getElementById("menu_EditPopup"); let shownPromise = BrowserTestUtils.waitForEvent(targetMenu, "popupshown"); EventUtils.synthesizeMouseAtCenter(document.getElementById("menu_Edit"), {}); await shownPromise; targetMenu.activateItem(element); } /** * Removes CalTransaction items from the CalTransactionManager stacks so other * tests are unhindered. */ function clearTransactions() { calTransManager.undoStack = []; calTransManager.redoStack = []; } /** * Test the undo/redo functionality for event creation. * * @param {string} undoId - The id of the "undo" menu item. * @param {string} redoId - The id of the "redo" menu item. */ async function testAddUndoRedoEvent(undoId, redoId) { let undo = document.getElementById(undoId); let redo = document.getElementById(redoId); Assert.ok(await isDisabled(undo), `#${undoId} is disabled`); Assert.ok(await isDisabled(redo), `#${redoId} is disabled`); let newBtn = document.getElementById("sidePanelNewEvent"); let windowOpened = CalendarTestUtils.waitForEventDialog("edit"); EventUtils.synthesizeMouseAtCenter(newBtn, {}); let win = await windowOpened; let iframeWin = win.document.getElementById("calendar-item-panel-iframe").contentWindow; await CalendarTestUtils.items.setData(win, iframeWin, { title: "A New Event" }); await CalendarTestUtils.items.saveAndCloseItemDialog(win); let eventItem; await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem; }, "event not created in time"); Assert.ok(!(await isDisabled(undo)), `#${undoId} is enabled`); Assert.ok(await isDisabled(redo), `#${redoId} is disabled`); // Test undo. await clickItem(undo); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return !eventItem; }, "undo did not remove item in time"); Assert.ok(!eventItem, `#${undoId} reverses item creation`); // Test redo. await clickItem(redo); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem; }, `${redoId} did not re-create item in time`); Assert.ok(eventItem, `#${redoId} redos item creation`); await calendar.deleteItem(eventItem.item); clearTransactions(); } /** * Test the undo/redo functionality for event modification. * * @param {string} undoId - The id of the "undo" menu item. * @param {string} redoId - The id of the "redo" menu item. */ async function testModifyUndoRedoEvent(undoId, redoId) { let undo = document.getElementById(undoId); let redo = document.getElementById(redoId); Assert.ok(await isDisabled(undo), `#${undoId} is disabled`); Assert.ok(await isDisabled(redo), `#${redoId} is disabled`); let event = new CalEvent(); event.title = "Modifiable Event"; event.startDate = cal.dtz.now(); await calendar.addItem(event); window.goToDate(event.startDate); let eventItem; await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem; }, "event not created in time"); let { dialogWindow, iframeWindow } = await CalendarTestUtils.editItem(window, eventItem); await CalendarTestUtils.items.setData(dialogWindow, iframeWindow, { title: "Modified Event", }); await CalendarTestUtils.items.saveAndCloseItemDialog(dialogWindow); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem && eventItem.item.title == "Modified Event"; }, "event not modified in time"); Assert.ok(!(await isDisabled(undo)), `#${undoId} is enabled`); Assert.ok(await isDisabled(redo), `#${redoId} is disabled`); // Test undo. await clickItem(undo); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem && eventItem.item.title == "Modifiable Event"; }, `#${undoId} did not un-modify event in time`); Assert.equal(eventItem.item.title, "Modifiable Event", `#${undoId} reverses item modification`); // Test redo. await clickItem(redo); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem && eventItem.item.title == "Modified Event"; }, `${redoId} did not re-modify item in time`); Assert.equal(eventItem.item.title, "Modified Event", `#${redoId} redos item modification`); clearTransactions(); await calendar.deleteItem(eventItem.item); } /** * Test the undo/redo functionality for event deletion. * * @param {string} undoId - The id of the "undo" menu item. * @param {string} redoId - The id of the "redo" menu item. */ async function testDeleteUndoRedo(undoId, redoId) { let undo = document.getElementById(undoId); let redo = document.getElementById(redoId); Assert.ok(await isDisabled(undo), `#${undoId} is disabled`); Assert.ok(await isDisabled(redo), `#${redoId} is disabled`); let event = new CalEvent(); event.title = "Deletable Event"; event.startDate = cal.dtz.now(); await calendar.addItem(event); window.goToDate(event.startDate); let eventItem; await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem; }, "event not created in time"); EventUtils.synthesizeMouseAtCenter(eventItem, {}); EventUtils.synthesizeKey("VK_DELETE"); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return !eventItem; }, "event not deleted in time"); Assert.ok(!(await isDisabled(undo)), `#${undoId} is enabled`); Assert.ok(await isDisabled(redo), `#${redoId} is disabled`); // Test undo. await clickItem(undo); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return eventItem; }, `#${undoId} did not add event in time`); Assert.ok(eventItem, `#${undoId} reverses item deletion`); // Test redo. await clickItem(redo); await TestUtils.waitForCondition(() => { eventItem = document.querySelector("calendar-month-day-box-item"); return !eventItem; }, "redo did not delete item in time"); Assert.ok(!eventItem, `#${redoId} redos item deletion`); clearTransactions(); } /** * Ensure the menu bar is visible and navigate the calendar view to today. */ add_setup(async function () { registerCleanupFunction(() => { CalendarTestUtils.removeCalendar(calendar); }); clearTransactions(); document.getElementById("toolbar-menubar").setAttribute("autohide", null); await CalendarTestUtils.setCalendarView(window, "month"); window.goToDate(cal.dtz.now()); }); /** * Tests the menu bar's undo/redo after adding an event. */ add_task(async function testMenuBarAddEventUndoRedo() { return testAddUndoRedoEvent("menu_undo", "menu_redo"); }).__skipMe = AppConstants.platform == "macosx"; // Can't click menu bar on Mac. /** * Tests the menu bar's undo/redo after modifying an event. */ add_task(async function testMenuBarModifyEventUndoRedo() { return testModifyUndoRedoEvent("menu_undo", "menu_redo"); }).__skipMe = AppConstants.platform == "macosx"; // Can't click menu bar on Mac. /** * Tests the menu bar's undo/redo after deleting an event. */ add_task(async function testMenuBarDeleteEventUndoRedo() { return testDeleteUndoRedo("menu_undo", "menu_redo"); }).__skipMe = AppConstants.platform == "macosx"; // Can't click menu bar on Mac.