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/test/browser/cloudfile/browser_attachmentItem.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/test/browser/cloudfile/browser_attachmentItem.js')
-rw-r--r-- | comm/mail/test/browser/cloudfile/browser_attachmentItem.js | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/comm/mail/test/browser/cloudfile/browser_attachmentItem.js b/comm/mail/test/browser/cloudfile/browser_attachmentItem.js new file mode 100644 index 0000000000..18c3d92e41 --- /dev/null +++ b/comm/mail/test/browser/cloudfile/browser_attachmentItem.js @@ -0,0 +1,451 @@ +/* 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/. */ + +/** + * Tests Filelink attachment item behaviour. + */ + +"use strict"; + +var utils = ChromeUtils.import("resource://testing-common/mozmill/utils.jsm"); +var { gMockFilePicker, gMockFilePickReg, select_attachments } = + ChromeUtils.import("resource://testing-common/mozmill/AttachmentHelpers.jsm"); +var { getFile, gMockCloudfileManager, MockCloudfileAccount } = + ChromeUtils.import("resource://testing-common/mozmill/CloudfileHelpers.jsm"); +var { + add_cloud_attachments, + convert_selected_to_cloud_attachment, + close_compose_window, + open_compose_new_mail, +} = ChromeUtils.import("resource://testing-common/mozmill/ComposeHelpers.jsm"); +var { close_popup, mc } = ChromeUtils.import( + "resource://testing-common/mozmill/FolderDisplayHelpers.jsm" +); + +var { cloudFileAccounts } = ChromeUtils.import( + "resource:///modules/cloudFileAccounts.jsm" +); + +var kAttachmentItemContextID = "msgComposeAttachmentItemContext"; + +// Prepare the mock prompt. +var originalPromptService = Services.prompt; +var mockPromptService = { + alertCount: 0, + alert() { + this.alertCount++; + }, + QueryInterface: ChromeUtils.generateQI(["nsIPromptService"]), +}; + +add_setup(function () { + Services.prompt = mockPromptService; + gMockFilePickReg.register(); + gMockCloudfileManager.register(); +}); + +registerCleanupFunction(function () { + gMockCloudfileManager.unregister(); + gMockFilePickReg.unregister(); + Services.prompt = originalPromptService; +}); + +/** + * Test that when an upload has been started, we can cancel and restart + * the upload, and then cancel again. For this test, we repeat this + * 3 times. + */ +add_task(async function test_upload_cancel_repeat() { + const kFile = "./data/testFile1"; + + // Prepare the mock file picker to return our test file. + let file = new FileUtils.File(getTestFilePath(kFile)); + gMockFilePicker.returnFiles = [file]; + + let provider = new MockCloudfileAccount(); + provider.init("someKey"); + let cw = open_compose_new_mail(mc); + + // We've got a compose window open, and our mock Filelink provider + // ready. Let's attach a file... + cw.window.AttachFile(); + + // Now we override the uploadFile function of the MockCloudfileAccount + // so that we're perpetually uploading... + let promise; + let started; + provider.uploadFile = function (window, aFile) { + return new Promise((resolve, reject) => { + promise = { resolve, reject }; + started = true; + }); + }; + + const kAttempts = 3; + for (let i = 0; i < kAttempts; i++) { + promise = null; + started = false; + + let bucket = cw.window.document.getElementById("attachmentBucket"); + Assert.equal( + bucket.itemCount, + 1, + "Should find correct number of attachments before converting." + ); + + // Select the attachment, and choose to convert it to a Filelink + select_attachments(cw, 0)[0]; + cw.window.convertSelectedToCloudAttachment(provider); + utils.waitFor(() => started); + + await assert_can_cancel_upload(cw, provider, promise, file); + await new Promise(resolve => setTimeout(resolve)); + + // A cancelled conversion must not remove the attachment. + Assert.equal( + bucket.itemCount, + 1, + "Should find correct number of attachments after converting." + ); + } + + close_compose_window(cw); +}); + +/** + * Test that we can cancel a whole series of files being uploaded at once. + */ +add_task(async function test_upload_multiple_and_cancel() { + const kFiles = ["./data/testFile1", "./data/testFile2", "./data/testFile3"]; + + // Prepare the mock file picker to return our test file. + let files = collectFiles(kFiles); + gMockFilePicker.returnFiles = files; + + let provider = new MockCloudfileAccount(); + provider.init("someKey"); + let cw = open_compose_new_mail(); + + let promises = {}; + provider.uploadFile = function (window, aFile) { + return new Promise((resolve, reject) => { + promises[aFile.leafName] = { resolve, reject }; + }); + }; + + add_cloud_attachments(cw, provider, false); + + let bucket = cw.window.document.getElementById("attachmentBucket"); + Assert.equal( + bucket.itemCount, + kFiles.length, + "Should find correct number of attachments before uploading." + ); + + for (let i = files.length - 1; i >= 0; --i) { + await assert_can_cancel_upload( + cw, + provider, + promises[files[i].leafName], + files[i] + ); + } + + // The cancelled attachment uploads should have been removed. + Assert.equal( + bucket.itemCount, + 0, + "Should find correct number of attachments after uploading." + ); + + close_compose_window(cw); +}); + +/** + * Helper function that takes an upload in progress, and cancels it, + * ensuring that the nsIMsgCloudFileProvider.uploadCanceled status message + * is returned to the passed in listener. + * + * @param aController the compose window controller to use. + * @param aProvider a MockCloudfileAccount for which the uploads have already + * started. + * @param aListener the nsIRequestObserver passed to aProvider's uploadFile + * function. + * @param aTargetFile the nsIFile to cancel the upload for. + */ +async function assert_can_cancel_upload( + aController, + aProvider, + aPromise, + aTargetFile +) { + let cancelled = false; + + // Override the provider's cancelFileUpload function. We can do this because + // it's assumed that the provider is a MockCloudfileAccount. + aProvider.cancelFileUpload = function (window, aFileToCancel) { + if (aTargetFile.equals(aFileToCancel)) { + aPromise.reject( + Components.Exception( + "Upload cancelled.", + cloudFileAccounts.constants.uploadCancelled + ) + ); + cancelled = true; + } + }; + + // Retrieve the attachment bucket index for the target file... + let index = get_attachmentitem_index_for_file(aController, aTargetFile); + + // Select that attachmentitem in the bucket + select_attachments(aController, index)[0]; + + // Bring up the context menu, and click cancel. + let cmd = aController.window.document.getElementById("cmd_cancelUpload"); + aController.window.updateAttachmentItems(); + + Assert.ok(!cmd.hidden, "cmd_cancelUpload should be shown"); + Assert.ok(!cmd.disabled, "cmd_cancelUpload should be enabled"); + + let attachmentItem = + aController.window.document.getElementById("attachmentBucket").selectedItem; + let contextMenu = aController.window.document.getElementById( + "msgComposeAttachmentItemContext" + ); + + let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown"); + EventUtils.synthesizeMouseAtCenter( + attachmentItem, + { type: "contextmenu", button: 2 }, + attachmentItem.ownerGlobal + ); + await popupPromise; + + let cancelItem = aController.window.document.getElementById( + "composeAttachmentContext_cancelUploadItem" + ); + if (AppConstants.platform == "macosx") { + // We need to use click() since the synthesizeMouseAtCenter doesn't work for + // context menu items on macos. + cancelItem.click(); + } else { + EventUtils.synthesizeMouseAtCenter(cancelItem, {}, cancelItem.ownerGlobal); + await new Promise(resolve => setTimeout(resolve)); + } + + // Close the popup, and wait for the cancellation to be complete. + await close_popup( + aController, + aController.window.document.getElementById(kAttachmentItemContextID) + ); + utils.waitFor(() => cancelled); +} + +/** + * A helper function to find the attachment bucket index for a particular + * nsIFile. Returns null if no attachmentitem is found. + * + * @param aController the compose window controller to use. + * @param aFile the nsIFile to search for. + */ +function get_attachmentitem_index_for_file(aController, aFile) { + // Get the fileUrl from the file. + let fileUrl = aController.window.FileToAttachment(aFile).url; + + // Get the bucket, and go through each item looking for the matching + // attachmentitem. + let bucket = aController.window.document.getElementById("attachmentBucket"); + for (let i = 0; i < bucket.getRowCount(); ++i) { + let attachmentitem = bucket.getItemAtIndex(i); + if (attachmentitem.attachment.url == fileUrl) { + return i; + } + } + return null; +} + +/** + * Helper function to start uploads and check number and icon of attachments + * after successful or failed uploads. + * + * @param error - to be returned error by uploadFile in case of failure + * @param expectedAttachments - number of expected attachments at the end of the test + * @param expectedAlerts - number of expected alerts at the end of the test + */ +async function test_upload(cw, error, expectedAttachments, expectedAlerts = 0) { + const kFiles = ["./data/testFile1", "./data/testFile2", "./data/testFile3"]; + + // Prepare the mock file picker to return our test file. + let files = collectFiles(kFiles); + gMockFilePicker.returnFiles = files; + + let provider = new MockCloudfileAccount(); + provider.init("someKey"); + + // Override the uploadFile function of the MockCloudfileAccount. + let promises = []; + provider.uploadFile = function (window, aFile) { + return new Promise((resolve, reject) => { + promises.push({ + resolve, + reject, + upload: { + url: `https://example.org/${aFile.leafName}`, + size: aFile.fileSize, + path: aFile.path, + }, + }); + }); + }; + + add_cloud_attachments(cw, provider, false); + utils.waitFor(() => promises.length == kFiles.length); + + let bucket = cw.window.document.getElementById("attachmentBucket"); + Assert.equal( + bucket.itemCount, + kFiles.length, + "Should find correct number of attachments before uploading." + ); + + for (let item of bucket.itemChildren) { + is( + item.querySelector("img.attachmentcell-icon").src, + "chrome://global/skin/icons/loading.png", + "CloudFile icon should be the loading spinner." + ); + } + + for (let promise of promises) { + if (error) { + promise.reject(error); + } else { + promise.resolve(promise.upload); + } + } + await new Promise(resolve => setTimeout(resolve)); + + Assert.equal( + bucket.itemCount, + expectedAttachments, + "Should find correct number of attachments after uploading." + ); + // Check if the spinner is no longer shown, but the expected moz-icon. + for (let item of bucket.itemChildren) { + ok( + item + .querySelector("img.attachmentcell-icon") + .src.startsWith("moz-icon://testFile"), + "CloudFile icon should be correct." + ); + } + + // Check and reset the prompt mock service. + is( + expectedAlerts, + Services.prompt.alertCount, + "Number of expected alert prompts should be correct." + ); + Services.prompt.alertCount = 0; +} + +/** + * Check if attachment is removed if upload failed. + */ +add_task(async function test_error_upload() { + let cw = open_compose_new_mail(); + await test_upload( + cw, + Components.Exception( + "Upload error.", + cloudFileAccounts.constants.uploadErr + ), + 0, + 3 + ); + close_compose_window(cw); +}); + +/** + * Check if attachment is not removed if upload is successful. + */ +add_task(async function test_successful_upload() { + let cw = open_compose_new_mail(); + await test_upload(cw, null, 3, 0); + close_compose_window(cw); +}); + +/** + * Check if the original cloud attachment is kept, after converting it to another + * provider failed. + */ +add_task(async function test_error_conversion() { + let cw = open_compose_new_mail(); + let bucket = cw.window.document.getElementById("attachmentBucket"); + + // Upload 3 files to the standard provider. + await test_upload(cw, null, 3, 0); + + // Define another provider. + let providerB = new MockCloudfileAccount(); + providerB.init("someOtherKey"); + + let uploadPromise = null; + providerB.uploadFile = function (window, aFile) { + return new Promise((resolve, reject) => { + uploadPromise = { resolve, reject }; + }); + }; + + select_attachments(cw, 0); + convert_selected_to_cloud_attachment(cw, providerB, false); + + let uploadError = new Promise(resolve => { + bucket.addEventListener("attachment-move-failed", resolve, { + once: true, + }); + }); + + // Reject the upload, causing the conversion to fail. + uploadPromise.reject( + new Components.Exception( + "Upload error.", + cloudFileAccounts.constants.uploadErr + ) + ); + await uploadError; + + // Wait for the showLocalizedCloudFileAlert() to localize the error message. + await new Promise(resolve => setTimeout(resolve)); + + is( + Services.prompt.alertCount, + 1, + "Number of expected alert prompts should be correct." + ); + Services.prompt.alertCount = 0; + + // Check that we still have the 3 attachments we started with. + Assert.equal( + bucket.itemCount, + 3, + "Should find correct number of attachments." + ); + for (let i = 0; i < bucket.itemCount; i++) { + let item = bucket.itemChildren[i]; + Assert.equal( + item.attachment.sendViaCloud, + true, + "Attachment should be a cloud attachment." + ); + Assert.equal( + item.attachment.cloudFileAccountKey, + "someKey", + "Attachment should be hosted by the correct provider." + ); + } + + close_compose_window(cw); +}); |