diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 17:32:43 +0000 |
commit | 6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch) | |
tree | a68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mail/components/extensions/test/browser/browser_ext_compose_sendMessage.js | |
parent | Initial commit. (diff) | |
download | thunderbird-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/mail/components/extensions/test/browser/browser_ext_compose_sendMessage.js')
-rw-r--r-- | comm/mail/components/extensions/test/browser/browser_ext_compose_sendMessage.js | 733 |
1 files changed, 733 insertions, 0 deletions
diff --git a/comm/mail/components/extensions/test/browser/browser_ext_compose_sendMessage.js b/comm/mail/components/extensions/test/browser/browser_ext_compose_sendMessage.js new file mode 100644 index 0000000000..4fd983e8e5 --- /dev/null +++ b/comm/mail/components/extensions/test/browser/browser_ext_compose_sendMessage.js @@ -0,0 +1,733 @@ +/* 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 { ExtensionSupport } = ChromeUtils.import( + "resource:///modules/ExtensionSupport.jsm" +); +var { localAccountUtils } = ChromeUtils.import( + "resource://testing-common/mailnews/LocalAccountUtils.jsm" +); +// Import the smtp server scripts +var { + nsMailServer, + gThreadManager, + fsDebugNone, + fsDebugAll, + fsDebugRecv, + fsDebugRecvSend, +} = ChromeUtils.import("resource://testing-common/mailnews/Maild.jsm"); +var { SmtpDaemon, SMTP_RFC2821_handler } = ChromeUtils.import( + "resource://testing-common/mailnews/Smtpd.jsm" +); +var { AuthPLAIN, AuthLOGIN, AuthCRAM } = ChromeUtils.import( + "resource://testing-common/mailnews/Auth.jsm" +); + +// Setup the daemon and server +function setupServerDaemon(handler) { + if (!handler) { + handler = function (d) { + return new SMTP_RFC2821_handler(d); + }; + } + var server = new nsMailServer(handler, new SmtpDaemon()); + return server; +} + +function getBasicSmtpServer(port = 1, hostname = "localhost") { + let server = localAccountUtils.create_outgoing_server( + port, + "user", + "password", + hostname + ); + + // Override the default greeting so we get something predictable + // in the ELHO message + Services.prefs.setCharPref("mail.smtpserver.default.hello_argument", "test"); + + return server; +} + +function getSmtpIdentity(senderName, smtpServer) { + // Set up the identity. + let identity = MailServices.accounts.createIdentity(); + identity.email = senderName; + identity.smtpServerKey = smtpServer.key; + + return identity; +} + +function tracksentMessages(aSubject, aTopic, aMsgID) { + // The aMsgID starts with < and ends with > which is not used by the API. + let headerMessageId = aMsgID.replace(/^<|>$/g, ""); + gSentMessages.push(headerMessageId); +} + +var gServer; +var gOutbox; +var gSentMessages = []; +let gPopAccount; +let gLocalAccount; + +add_setup(() => { + gServer = setupServerDaemon(); + gServer.start(); + + // Test needs a non-local default account to be able to send messages. + gPopAccount = createAccount("pop3"); + gLocalAccount = createAccount("local"); + MailServices.accounts.defaultAccount = gPopAccount; + + let identity = getSmtpIdentity( + "identity@foo.invalid", + getBasicSmtpServer(gServer.port) + ); + gPopAccount.addIdentity(identity); + gPopAccount.defaultIdentity = identity; + + // Test is using the Sent folder and Outbox folder of the local account. + let rootFolder = gLocalAccount.incomingServer.rootFolder; + rootFolder.createSubfolder("Sent", null); + MailServices.accounts.setSpecialFolders(); + gOutbox = rootFolder.getChildNamed("Outbox"); + + Services.obs.addObserver(tracksentMessages, "mail:composeSendSucceeded"); + + registerCleanupFunction(() => { + gServer.stop(); + Services.obs.removeObserver(tracksentMessages, "mail:composeSendSucceeded"); + }); +}); + +add_task(async function test_no_permission() { + let files = { + "background.js": async () => { + let details = { + to: ["send@test.invalid"], + subject: "Test send", + }; + + // Open a compose window with a message. + let tab = await browser.compose.beginNew(details); + + // Send now. It should fail due to the missing compose.send permission. + await browser.test.assertThrows( + () => browser.compose.sendMessage(tab.id), + /browser.compose.sendMessage is not a function/, + "browser.compose.sendMessage() should reject, if the permission compose.send is not granted." + ); + + // Clean up. + let removedWindowPromise = window.waitForEvent("windows.onRemoved"); + browser.tabs.remove(tab.id); + await removedWindowPromise; + + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose"], + }, + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); + +add_task(async function test_fail() { + let files = { + "background.js": async () => { + let details = { + to: ["send@test.invalid"], + subject: "Test send", + }; + + // Open a compose window with a message. + let createdWindowPromise = window.waitForEvent("windows.onCreated"); + await browser.compose.beginNew(details); + let [createdWindow] = await createdWindowPromise; + browser.test.assertEq("messageCompose", createdWindow.type); + + await window.sendMessage("checkWindow", details); + + let [tab] = await browser.tabs.query({ windowId: createdWindow.id }); + + browser.compose.onBeforeSend.addListener(() => { + return { cancel: true }; + }); + + // Add onAfterSend listener + let collectedEventsMap = new Map(); + function onAfterSendListener(tab, info) { + collectedEventsMap.set(tab.id, info); + } + browser.compose.onAfterSend.addListener(onAfterSendListener); + + // Send now. It should fail due to the aborting onBeforeSend listener. + await browser.test.assertRejects( + browser.compose.sendMessage(tab.id), + /Send aborted by an onBeforeSend event/, + "browser.compose.sendMessage() should reject, if the message could not be send." + ); + + // Check onAfterSend listener + browser.compose.onAfterSend.removeListener(onAfterSendListener); + browser.test.assertEq( + 1, + collectedEventsMap.size, + "Should have received the correct number of onAfterSend events" + ); + browser.test.assertEq( + "Send aborted by an onBeforeSend event", + collectedEventsMap.get(tab.id).error, + "Should have received the correct error" + ); + + // Clean up. + let removedWindowPromise = window.waitForEvent("windows.onRemoved"); + browser.windows.remove(createdWindow.id); + await removedWindowPromise; + + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose", "compose.send"], + }, + }); + + extension.onMessage("checkWindow", async expected => { + await checkComposeHeaders(expected); + extension.sendMessage(); + }); + + extension.onMessage("getSentMessages", async () => { + extension.sendMessage(gSentMessages); + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); + +add_task(async function test_send() { + let files = { + "background.js": async () => { + let details = { + to: ["send@test.invalid"], + subject: "Test send", + }; + + // Open a compose window with a message. + let createdWindowPromise = window.waitForEvent("windows.onCreated"); + await browser.compose.beginNew(details); + let [createdWindow] = await createdWindowPromise; + browser.test.assertEq("messageCompose", createdWindow.type); + + await window.sendMessage("checkWindow", details); + + let [tab] = await browser.tabs.query({ windowId: createdWindow.id }); + + // Add onAfterSend listener + let collectedEventsMap = new Map(); + function onAfterSendListener(tab, info) { + collectedEventsMap.set(tab.id, info); + } + browser.compose.onAfterSend.addListener(onAfterSendListener); + + // Send now. + let removedWindowPromise = window.waitForEvent("windows.onRemoved"); + let rv = await browser.compose.sendMessage(tab.id); + let [sentMessages] = await window.sendMessage("getSentMessages"); + + browser.test.assertEq( + 1, + sentMessages.length, + "Number of total messages sent should be correct." + ); + browser.test.assertEq( + "sendNow", + rv.mode, + "The mode of the last message operation should be correct." + ); + browser.test.assertEq( + sentMessages[0], + rv.headerMessageId, + "The headerMessageId of last message sent should be correct." + ); + browser.test.assertEq( + sentMessages[0], + rv.messages[0].headerMessageId, + "The headerMessageId in the copy of last message sent should be correct." + ); + + // Window should have closed after send. + await removedWindowPromise; + + // Check onAfterSend listener + browser.compose.onAfterSend.removeListener(onAfterSendListener); + browser.test.assertEq( + 1, + collectedEventsMap.size, + "Should have received the correct number of onAfterSend events" + ); + browser.test.assertTrue( + collectedEventsMap.has(tab.id), + "The received event should belong to the correct tab." + ); + browser.test.assertEq( + "sendNow", + collectedEventsMap.get(tab.id).mode, + "The received event should have the correct mode." + ); + browser.test.assertEq( + rv.headerMessageId, + collectedEventsMap.get(tab.id).headerMessageId, + "The received event should have the correct headerMessageId." + ); + browser.test.assertEq( + rv.headerMessageId, + collectedEventsMap.get(tab.id).messages[0].headerMessageId, + "The message in the received event should have the correct headerMessageId." + ); + + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose", "compose.send"], + }, + }); + + extension.onMessage("checkWindow", async expected => { + await checkComposeHeaders(expected); + extension.sendMessage(); + }); + + extension.onMessage("getSentMessages", async () => { + extension.sendMessage(gSentMessages); + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); + +add_task(async function test_sendDefault() { + let files = { + "background.js": async () => { + let details = { + to: ["sendDefault@test.invalid"], + subject: "Test sendDefault", + }; + + // Open a compose window with a message. + let createdWindowPromise = window.waitForEvent("windows.onCreated"); + await browser.compose.beginNew(details); + let [createdWindow] = await createdWindowPromise; + browser.test.assertEq("messageCompose", createdWindow.type); + + await window.sendMessage("checkWindow", details); + + let [tab] = await browser.tabs.query({ windowId: createdWindow.id }); + + // Send via default mode, which should be sendNow. + let removedWindowPromise = window.waitForEvent("windows.onRemoved"); + let rv = await browser.compose.sendMessage(tab.id, { mode: "default" }); + let [sentMessages] = await window.sendMessage("getSentMessages"); + + browser.test.assertEq( + 2, + sentMessages.length, + "Number of total messages sent should be correct." + ); + browser.test.assertEq( + "sendNow", + rv.mode, + "The mode of the last message operation should be correct." + ); + browser.test.assertEq( + sentMessages[1], + rv.headerMessageId, + "The headerMessageId of last message sent should be correct." + ); + browser.test.assertEq( + sentMessages[1], + rv.messages[0].headerMessageId, + "The headerMessageId in the copy of last message sent should be correct." + ); + + // Window should have closed after send. + await removedWindowPromise; + + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose", "compose.send"], + }, + }); + + extension.onMessage("checkWindow", async expected => { + await checkComposeHeaders(expected); + extension.sendMessage(); + }); + + extension.onMessage("getSentMessages", async () => { + extension.sendMessage(gSentMessages); + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); + gServer.resetTest(); +}); + +add_task(async function test_sendNow() { + let files = { + "background.js": async () => { + let details = { + to: ["sendNow@test.invalid"], + subject: "Test sendNow", + }; + + // Open a compose window with a message. + let createdWindowPromise = window.waitForEvent("windows.onCreated"); + await browser.compose.beginNew(details); + let [createdWindow] = await createdWindowPromise; + browser.test.assertEq("messageCompose", createdWindow.type); + + await window.sendMessage("checkWindow", details); + + let [tab] = await browser.tabs.query({ windowId: createdWindow.id }); + + // Send via sendNow mode. + let removedWindowPromise = window.waitForEvent("windows.onRemoved"); + let rv = await browser.compose.sendMessage(tab.id, { mode: "sendNow" }); + let [sentMessages] = await window.sendMessage("getSentMessages"); + + browser.test.assertEq( + 3, + sentMessages.length, + "Number of total messages sent should be correct." + ); + browser.test.assertEq( + "sendNow", + rv.mode, + "The mode of the last message operation should be correct." + ); + browser.test.assertEq( + sentMessages[2], + rv.headerMessageId, + "The headerMessageId of last message sent should be correct." + ); + browser.test.assertEq( + sentMessages[2], + rv.messages[0].headerMessageId, + "The headerMessageId in the copy of last message sent should be correct." + ); + + // Window should have closed after send. + await removedWindowPromise; + + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose", "compose.send"], + }, + }); + + extension.onMessage("checkWindow", async expected => { + await checkComposeHeaders(expected); + extension.sendMessage(); + }); + + extension.onMessage("getSentMessages", async () => { + extension.sendMessage(gSentMessages); + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); + +add_task(async function test_sendLater() { + let files = { + "background.js": async () => { + let details = { + to: ["sendLater@test.invalid"], + subject: "Test sendLater", + }; + + let createdWindowPromise = window.waitForEvent("windows.onCreated"); + await browser.compose.beginNew(details); + let [createdWindow] = await createdWindowPromise; + browser.test.assertEq("messageCompose", createdWindow.type); + + await window.sendMessage("checkWindow", details); + + let [tab] = await browser.tabs.query({ windowId: createdWindow.id }); + + // Send Later. + + let rv = await browser.compose.sendMessage(tab.id, { mode: "sendLater" }); + let [outboxMessage] = await window.sendMessage( + "checkMessagesInOutbox", + details + ); + + browser.test.assertEq( + "sendLater", + rv.mode, + "The mode of the last message operation should be correct." + ); + browser.test.assertEq( + outboxMessage, + rv.messages[0].headerMessageId, + "The headerMessageId in the copy of last message sent should be correct." + ); + + await window.sendMessage("clearMessagesInOutbox"); + browser.test.notifyPass("finished"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose", "compose.send", "messagesRead", "accountsRead"], + }, + }); + + extension.onMessage("checkMessagesInOutbox", async expected => { + // Check if the sendLater request did put the message in the outbox. + let outboxMessages = [...gOutbox.messages]; + Assert.ok(outboxMessages.length == 1); + let sentMessage = outboxMessages.shift(); + Assert.equal(sentMessage.subject, expected.subject, "subject is correct"); + Assert.equal(sentMessage.recipients, expected.to, "recipient is correct"); + extension.sendMessage(sentMessage.messageId); + }); + + extension.onMessage("clearMessagesInOutbox", async () => { + let outboxMessages = [...gOutbox.messages]; + await new Promise(resolve => { + gOutbox.deleteMessages( + outboxMessages, + null, + true, + false, + { OnStopCopy: resolve }, + false + ); + }); + + Assert.equal(0, [...gOutbox.messages].length, "outbox should be empty"); + extension.sendMessage(); + }); + + extension.onMessage("checkWindow", async expected => { + await checkComposeHeaders(expected); + extension.sendMessage(); + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); + +add_task(async function test_onComposeStateChanged() { + let files = { + "background.js": async () => { + let numberOfEvents = 0; + browser.compose.onComposeStateChanged.addListener(async (tab, state) => { + numberOfEvents++; + browser.test.log(`State #${numberOfEvents}: ${JSON.stringify(state)}`); + switch (numberOfEvents) { + case 1: + // The fresh created composer has no recipient, send is disabled. + browser.test.assertEq(false, state.canSendNow); + browser.test.assertEq(false, state.canSendLater); + break; + + case 2: + // The composer updated its initial details data, send is enabled. + browser.test.assertEq(true, state.canSendNow); + browser.test.assertEq(true, state.canSendLater); + break; + + case 3: + // The recipient has been invalidated, send is disabled. + browser.test.assertEq(false, state.canSendNow); + browser.test.assertEq(false, state.canSendLater); + break; + + case 4: + // The recipient has been reverted, send is enabled. + browser.test.assertEq(true, state.canSendNow); + browser.test.assertEq(true, state.canSendLater); + + // Clean up. + + let removedWindowPromise = window.waitForEvent("windows.onRemoved"); + browser.windows.remove(createdWindow.id); + await removedWindowPromise; + + browser.test.notifyPass("finished"); + break; + } + }); + + // The call to beginNew should create two onComposeStateChanged events, + // one after the empty window has been created and one after the initial + // details have been set. + let createdWindowPromise = window.waitForEvent("windows.onCreated"); + let createdTab = await browser.compose.beginNew({ + to: ["test@test.invalid"], + subject: "Test part 1", + body: "Original body.", + }); + let [createdWindow] = await createdWindowPromise; + browser.test.assertEq("messageCompose", createdWindow.type); + + // Trigger an onComposeStateChanged event by invalidating the recipient. + await browser.compose.setComposeDetails(createdTab.id, { + to: ["test"], + subject: "Test part 2", + }); + + // Trigger an onComposeStateChanged event by reverting the recipient. + await browser.compose.setComposeDetails(createdTab.id, { + to: ["test@test.invalid"], + subject: "Test part 3", + }); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose"], + }, + }); + + await extension.startup(); + await extension.awaitFinish("finished"); + await extension.unload(); +}); + +// Test onAfterSend for MV3 +add_task(async function test_onAfterSend_MV3_event_pages() { + let files = { + "background.js": async () => { + // Whenever the extension starts or wakes up, hasFired is set to false. In + // case of a wake-up, the first fired event is the one that woke up the background. + let hasFired = false; + + browser.compose.onAfterSend.addListener(async (tab, sendInfo) => { + // Only send the first event after background wake-up, this should be + // the only one expected. + if (!hasFired) { + hasFired = true; + browser.test.sendMessage("onAfterSend received", sendInfo); + } + }); + + browser.test.sendMessage("background started"); + }, + "utils.js": await getUtilsJS(), + }; + let extension = ExtensionTestUtils.loadExtension({ + files, + manifest: { + manifest_version: 3, + background: { scripts: ["utils.js", "background.js"] }, + permissions: ["compose"], + browser_specific_settings: { + gecko: { id: "compose.onAfterSend@xpcshell.test" }, + }, + }, + }); + + function checkPersistentListeners({ primed }) { + // A persistent event is referenced by its moduleName as defined in + // ext-mails.json, not by its actual namespace. + const persistent_events = ["compose.onAfterSend"]; + + for (let event of persistent_events) { + let [moduleName, eventName] = event.split("."); + assertPersistentListeners(extension, moduleName, eventName, { + primed, + }); + } + } + + await extension.startup(); + await extension.awaitMessage("background started"); + // The listeners should be persistent, but not primed. + checkPersistentListeners({ primed: false }); + + // Trigger onAfterSend without terminating the background first. + + let firstComposeWindow = await openComposeWindow(gPopAccount); + await focusWindow(firstComposeWindow); + firstComposeWindow.SetComposeDetails({ to: "first@invalid.net" }); + firstComposeWindow.SetComposeDetails({ subject: "First message" }); + firstComposeWindow.SendMessage(); + let firstSaveInfo = await extension.awaitMessage("onAfterSend received"); + Assert.equal( + "sendNow", + firstSaveInfo.mode, + "Returned SaveInfo should be correct" + ); + + // Terminate background and re-trigger onAfterSend. + + await extension.terminateBackground({ disableResetIdleForTest: true }); + // The listeners should be primed. + checkPersistentListeners({ primed: true }); + let secondComposeWindow = await openComposeWindow(gPopAccount); + await focusWindow(secondComposeWindow); + secondComposeWindow.SetComposeDetails({ to: "second@invalid.net" }); + secondComposeWindow.SetComposeDetails({ subject: "Second message" }); + secondComposeWindow.SendMessage(); + let secondSaveInfo = await extension.awaitMessage("onAfterSend received"); + Assert.equal( + "sendNow", + secondSaveInfo.mode, + "Returned SaveInfo should be correct" + ); + + // The background should have been restarted. + await extension.awaitMessage("background started"); + // The listener should no longer be primed. + checkPersistentListeners({ primed: false }); + + await extension.unload(); +}); |