/* 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 { EwsServer, RemoteFolder } = ChromeUtils.importESModule( "resource://testing-common/mailnews/EwsServer.sys.mjs" ); var { localAccountUtils } = ChromeUtils.importESModule( "resource://testing-common/mailnews/LocalAccountUtils.sys.mjs" ); var { MessageGenerator } = ChromeUtils.importESModule( "resource://testing-common/mailnews/MessageGenerator.sys.mjs" ); var { TestUtils } = ChromeUtils.importESModule( "resource://testing-common/TestUtils.sys.mjs" ); var { MailServices } = ChromeUtils.importESModule( "resource:///modules/MailServices.sys.mjs" ); var incomingServer; /** * @type {EwsServer} */ var ewsServer; /** * @type {nsIMsgWindow} */ var msgWindow; var rootFolder, inboxFolder, archiveFolder, trashFolder; const ewsIdPropertyName = "ewsId"; const generator = new MessageGenerator(); class DummyStatusFeedback { showStatusString(_status) {} startMeteors() {} stopMeteors() {} showProgress(_percent) {} setWrappedStatusFeedback(_feedback) {} } add_setup(async function () { [ewsServer, incomingServer] = setupBasicEwsTestServer({}); msgWindow = Cc["@mozilla.org/messenger/msgwindow;1"].createInstance( Ci.nsIMsgWindow ); msgWindow.statusFeedback = new DummyStatusFeedback(); rootFolder = incomingServer.rootFolder; await syncFolder(incomingServer, rootFolder); inboxFolder = rootFolder.getChildNamed("Inbox"); archiveFolder = rootFolder.getChildNamed("Archives"); trashFolder = rootFolder.getChildNamed("Deleted Items"); Assert.ok(!!inboxFolder, "Inbox folder should exist."); Assert.ok(!!archiveFolder, "Archive folder should exist."); Assert.ok(!!trashFolder, "Trash folder should exist."); }); function findTestMessages(folder, prefix) { const messages = [...folder.messages]; return messages.filter(m => m.getStringProperty(ewsIdPropertyName).startsWith(prefix) ); } async function setupCopyMoveTest(prefix, isMove) { const itemId = `${prefix}_item`; ewsServer.addNewItemOrMoveItemToFolder(itemId, "inbox"); await syncFolder(incomingServer, inboxFolder); const headers = findTestMessages(inboxFolder, prefix); Assert.equal(headers.length, 1, "Should have on test message."); const listener = new PromiseTestUtils.PromiseCopyListener(); archiveFolder.copyMessages( inboxFolder, headers, isMove, msgWindow, listener, false, true ); await listener.promise; } function checkFolders(prefix, inboxCount, archiveCount) { Assert.equal( findTestMessages(inboxFolder, prefix).length, inboxCount, `Should have ${inboxCount} messages with id prefix ${prefix} in inbox folder.` ); Assert.equal( findTestMessages(archiveFolder, prefix).length, archiveCount, `Should have ${archiveCount} messages with id prefix ${prefix} in archive folder.` ); } add_task(async function test_undo_move() { const testItemPrefix = "undo_move_test"; await setupCopyMoveTest(testItemPrefix, true); checkFolders(testItemPrefix, 0, 1); const currentUndoItem = msgWindow.transactionManager.peekUndoStack(); Assert.ok(!!currentUndoItem, "Should have a transaction on the undo stack."); Assert.equal( currentUndoItem.QueryInterface(Ci.nsIMsgTxn).txnType, Ci.nsIMessenger.eMoveMsg ); msgWindow.transactionManager.undoTransaction(); await TestUtils.waitForCondition(() => { return findTestMessages(inboxFolder, testItemPrefix).length > 0; }, "Waiting for undo operation to complete."); checkFolders(testItemPrefix, 1, 0); const currentRedoItem = msgWindow.transactionManager.peekRedoStack(); Assert.ok(!!currentRedoItem, "Should have a transaction on the redo stack."); Assert.equal( currentRedoItem.QueryInterface(Ci.nsIMsgTxn).txnType, Ci.nsIMessenger.eMoveMsg ); msgWindow.transactionManager.redoTransaction(); await TestUtils.waitForCondition(() => { return findTestMessages(archiveFolder, testItemPrefix).length > 0; }, "Waiting for redo operation to complete."); checkFolders(testItemPrefix, 0, 1); msgWindow.transactionManager.clear(); }); add_task(async function test_undo_copy() { const testItemPrefix = "undo_copy_test"; await setupCopyMoveTest(testItemPrefix, false); checkFolders(testItemPrefix, 1, 1); const currentUndoItem = msgWindow.transactionManager.peekUndoStack(); Assert.ok(!!currentUndoItem, "Should have a transaction on the undo stack."); Assert.equal( currentUndoItem.QueryInterface(Ci.nsIMsgTxn).txnType, Ci.nsIMessenger.eCopyMsg ); msgWindow.transactionManager.undoTransaction(); await TestUtils.waitForCondition(() => { return findTestMessages(archiveFolder, testItemPrefix).length == 0; }, "Waiting for undo operation to complete."); checkFolders(testItemPrefix, 1, 0); const currentRedoItem = msgWindow.transactionManager.peekRedoStack(); Assert.ok(!!currentRedoItem, "Should have a transaction on the redo stack."); Assert.equal( currentRedoItem.QueryInterface(Ci.nsIMsgTxn).txnType, Ci.nsIMessenger.eCopyMsg ); msgWindow.transactionManager.redoTransaction(); await TestUtils.waitForCondition(() => { return findTestMessages(archiveFolder, testItemPrefix).length > 0; }, "Waiting for redo operation to complete."); checkFolders(testItemPrefix, 1, 1); msgWindow.transactionManager.clear(); }); /** * This test tests undoing of soft delete operations, in which the item is moved * to the trash. There is no way to undo a hard delete in which the item is * permanently deleted. */ add_task(async function test_undo_delete() { const testItemPrefix = "undo_delete_test"; ewsServer.addNewItemOrMoveItemToFolder(testItemPrefix, "inbox"); await syncFolder(incomingServer, inboxFolder); const messagesToDelete = findTestMessages(inboxFolder, "undo_delete_test"); Assert.equal( messagesToDelete.length, 1, "Should have a delete test message in inbox." ); const listener = new PromiseTestUtils.PromiseCopyListener(); inboxFolder.deleteMessages( messagesToDelete, msgWindow, false, false, listener, true ); await listener.promise; const checkCounts = (inboxCount, trashCount) => { Assert.equal( findTestMessages(inboxFolder, testItemPrefix).length, inboxCount, `Inbox should have ${inboxCount} messages.` ); Assert.equal( findTestMessages(trashFolder, testItemPrefix).length, trashCount, `Trash should have ${trashCount} messages.` ); }; checkCounts(0, 1); const undoTransaction = msgWindow.transactionManager.peekUndoStack(); Assert.ok(!!undoTransaction, "Should have an undo transaction."); Assert.equal( undoTransaction.QueryInterface(Ci.nsIMsgTxn).txnType, Ci.nsIMessenger.eDeleteMsg ); msgWindow.transactionManager.undoTransaction(); await TestUtils.waitForCondition(() => { return findTestMessages(inboxFolder, testItemPrefix).length > 0; }, "Waiting for message to reappear in inbox."); checkCounts(1, 0); const redoTransaction = msgWindow.transactionManager.peekRedoStack(); Assert.ok(!!redoTransaction, "Should have a redo transaction."); Assert.equal( redoTransaction.QueryInterface(Ci.nsIMsgTxn).txnType, Ci.nsIMessenger.eDeleteMsg ); msgWindow.transactionManager.redoTransaction(); await TestUtils.waitForCondition(() => { return findTestMessages(inboxFolder, testItemPrefix).length == 0; }, "Waiting for message to disappear from inbox."); checkCounts(0, 1); msgWindow.transactionManager.clear(); });