Bug 1852746 - Fix listAttachments() being empty for encrypted messages. r=mkmelin

This is a regression from switching to the MimeTreeEmitter. The old
implementation always tried to decrpypt the message for attachment
extraction. The new implementation did not, which is fixed by this patch.

This also adds an extended test to actually check listAttachments() on
an encrypted message (including nested messages).

Note: getRaw() and getFull() have an option to work on the original or
on the decrypted message. This patch does not add the same option for
listAttachments(). We currently do not handle encrypted parts as
"attachments" (this is why listAttachments() did not return the
raw encrypted part before this patch) and there has not been a request
to access the raw enrcrypted part. We may come back to this at a later time.

The changes in `utils.js` are fixing an incomplete type check. Only
`boolean`, `number` and `string` variables where type checked,
but not `date` for example. This patch also adds some more verbose
output to make it easier to track errors.

Differential Revision: https://phabricator.services.mozilla.com/D212990

--HG--
extra : amend_source : 4bb7d6715e9337f42d2da1837aa67b31b9339a0b
This commit is contained in:
John Bieling 2024-06-10 22:25:03 +02:00
Родитель b42d23ba49
Коммит fa1c537868
6 изменённых файлов: 700 добавлений и 13 удалений

Просмотреть файл

@ -597,7 +597,7 @@ export class MsgHdrProcessor {
* @returns {Promise<MimeTreePart[]>}
*/
async getAttachmentParts(options) {
const rawMessage = await this.getOriginalMessage();
const rawMessage = await this.getDecryptedMessage();
const mimeTree = this.#parseMessage(
rawMessage,
{
@ -645,7 +645,7 @@ export class MsgHdrProcessor {
* @returns {Promise<MimeTreePart>}
*/
async getAttachmentPart(partName, options) {
const rawMessage = await this.getOriginalMessage();
const rawMessage = await this.getDecryptedMessage();
const includeRaw = options?.includeRaw ?? false;
const mimeTree = this.#parseMessage(
rawMessage,

Просмотреть файл

@ -201,7 +201,7 @@
"size": {
"type": "integer",
"optional": true,
"description": "The size of this part. The size of <em>message/*</em> parts is not the actual message size (on disc), but the total size of its decoded body parts, excluding headers."
"description": "The size of this part. The size of parts with content type <em>message/rfc822</em> is not the actual message size (on disc), but the total size of its decoded body parts, excluding headers."
}
}
},

Просмотреть файл

@ -27,31 +27,46 @@ function assertDeepEqual(
}
}
function assertDeepEqualNested(expected, actual, strict) {
function assertDeepEqualNested(expected, actual, strict, description) {
if (expected === null) {
browser.test.assertTrue(actual === null);
browser.test.assertTrue(actual === null, description);
return actual === null;
}
if (expected === undefined) {
browser.test.assertTrue(actual === undefined);
browser.test.assertTrue(actual === undefined, description);
return actual === undefined;
}
browser.test.assertEq(
typeof expected,
typeof actual,
`${description} (type check)`
);
if (["boolean", "number", "string"].includes(typeof expected)) {
browser.test.assertEq(typeof expected, typeof actual);
browser.test.assertEq(expected, actual);
browser.test.assertEq(expected, actual, `${description} (value check)`);
return typeof expected == typeof actual && expected == actual;
}
if (Array.isArray(expected)) {
browser.test.assertTrue(Array.isArray(actual));
browser.test.assertEq(expected.length, actual.length);
browser.test.assertTrue(Array.isArray(actual), `${description} (exist)`);
browser.test.assertEq(
expected.length,
actual.length,
`${description} (length check)`
);
let ok = 0;
let all = 0;
for (let i = 0; i < expected.length; i++) {
all++;
if (assertDeepEqualNested(expected[i], actual[i], strict)) {
if (
assertDeepEqualNested(
expected[i],
actual[i],
strict,
`Array entry #${i} is correct`
)
) {
ok++;
}
}
@ -66,14 +81,36 @@ function assertDeepEqualNested(expected, actual, strict) {
const lengthOk = strict
? expectedKeys.length == actualKeys.length
: expectedKeys.length <= actualKeys.length;
browser.test.assertTrue(lengthOk);
if (strict) {
browser.test.assertEq(
expectedKeys.length,
actualKeys.length,
`strict length check for ${description}, expected exactly: ${JSON.stringify(
expectedKeys
)}, actual: ${JSON.stringify(actualKeys)}`
);
} else {
browser.test.assertTrue(
lengthOk,
`lazy length check for ${description}, expected at least: ${JSON.stringify(
expectedKeys
)}, actual: ${JSON.stringify(actualKeys)}`
);
}
let ok = 0;
let all = 0;
for (const key of expectedKeys) {
all++;
browser.test.assertTrue(actualKeys.includes(key), `Key ${key} exists`);
if (assertDeepEqualNested(expected[key], actual[key], strict)) {
if (
assertDeepEqualNested(
expected[key],
actual[key],
strict,
`Key ${key} is correct`
)
) {
ok++;
}
}

Просмотреть файл

@ -0,0 +1,51 @@
Message-ID: <sample.eml@mime.sample>
Date: Fri, 20 May 2000 00:29:55 -0400
To: Alice Lovelace <alice@openpgp.example>
From: Batman <bruce@wayne-enterprises.com>
Subject: Encrypted, attached message with attachments
Mime-Version: 1.0
Content-Type: multipart/encrypted;
protocol="application/pgp-encrypted";
boundary="------------M20GNGI1rLZhtbYD7WFekIXJ"
This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
--------------M20GNGI1rLZhtbYD7WFekIXJ
Content-Type: application/pgp-encrypted
Content-Description: PGP/MIME version identification
Version: 1
--------------M20GNGI1rLZhtbYD7WFekIXJ
Content-Type: application/octet-stream; name="encrypted.asc"
Content-Description: OpenPGP encrypted message
Content-Disposition: inline; filename="encrypted.asc"
-----BEGIN PGP MESSAGE-----
hF4DR2b2udXyHrYSAQdAkHA/pErV3hMoJphrF/glWHFydVs+PeGud6VzdyG6rF4w
s0EzVZ9peVVd0+XewFpTTasMWEB9WcC1s/bVMTY2cz2TbHLufxp8XDUwyOwCoSvn
0ukBxBk5ZqNqYNGI/LZ/9zBltAWmrxTt00BKzNuigQyhWTMFZx1SMbTamro0+ej0
9U61CaCfLT31b+DsMyOj+FQ+0DPq4rEZ5Oek9atkaRQ1N9kbvaLmgJB+Ya+kJTnQ
jPZEPrtHAVkSZSciPJxwkluacZMrRKWExq1HHoSoLI5NG0gIkT7+lqGX3e2HOFm8
NuKvOyOk6Pza0v15vKf8NlXjwfgzR8ZuaNaMjO6q5ismCfm6qpLFG+YGRw7KydOQ
ZiQ2pAWhBQEaDJuDTGM9KT2onQjkFl4okIkMRU7dapjBAZpM01t3g1YDss6UHMil
8zRT1dlEdeoCIQiRLJykCHsSVAAujWLGLdMQt9oYekbiTC0U56t9tbQrO8P7ZB2F
JEHeNcKRAxGod7WSl8Nbxfor8mQ8zuxUMjEdhmrBzsKTcfteTYLeMbrcXslguxxD
LDrbM7MevUTjfPJGAF9B+OMOMnyw1g5El/tK+wEFE3zv03EBEtEHjl4RPDPIzfZ3
ozKVPkzEmLkZPHMN2yKJzT2nsKI8RSqpoTxfk3QlKSrVHTaZhQDSTcorzFXLDL8Y
vdXQoozFyTfPVd3PHBp/L2tfnYZDpwqraicPK4eX0yQ+eoN1W5qFUXHFHcZFFyPo
InJsYtEBABGWtRWcZY58BX34/Fi8mHyrAWV6gRnAKVO3ecElfvgHgNdoufJzHLEY
b/B+I1UfRxKX37vrlPtszHpEJLk6+EYPHcPA5kjmDi4tMm3NkJJI6FBnybulzNRC
bgHnVKlJgJLoiRbKC7fbuOtM523+krkWlKPZhEXs+qY9PjIzaEpmAIUE64gPnCiT
OcyWQjG3hX2lo9E9CyBlI0SvyYkdHnbKAWIeZNd/k7STK99actjPg2Wn0XloYpx4
/ldupx5oYAhcQW7lKHrczsEqXp4yi+f7KFEXVeerVxNuaSaBSpDIqTSQ82GVotB1
3iGJFzqcpykeYVJZtYylMxRE5gKyU4wBkzPO7Thsxo8DVFcRkFAslnknEk2kqNf6
fKiWh7k0cqYvldLTVhbNkn/Tk7XXWlFFIFlqpOB8cXqSgcUp09PxQHHQB8nlftJK
627u7FBhrH6J01YbuLpHP+aO8pykNrS+JgvOvYxa9aGDmWSz70lDmp27hPGPvpWF
UU74y4xTcBtPB1xM580zvDOZULDQHmTmNyGzcOlWvGXpCbcDzeYyJRKb2+arZ7CM
uQj5Kyari1l6fDttXtuVVvoaHpCk8h5+IqwVTWi2qqoK0C6VgMQnBYs2EnfjbU/I
ywL4O+JDUGkxFBjkSrwyQAUY3LoA5CqaxApOwCPbP/veHpLYOuMFIkw=
=fK7D
-----END PGP MESSAGE-----
--------------M20GNGI1rLZhtbYD7WFekIXJ--

Просмотреть файл

@ -0,0 +1,598 @@
/* 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";
var { FileUtils } = ChromeUtils.importESModule(
"resource://gre/modules/FileUtils.sys.mjs"
);
var { ExtensionTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/ExtensionXPCShellUtils.sys.mjs"
);
var { OpenPGPTestUtils } = ChromeUtils.importESModule(
"resource://testing-common/mail/OpenPGPTestUtils.sys.mjs"
);
const OPENPGP_TEST_DIR = do_get_file("../../../../test/browser/openpgp");
const OPENPGP_KEY_PATH = PathUtils.join(
OPENPGP_TEST_DIR.path,
"data",
"keys",
"alice@openpgp.example-0xf231550c4f47e38e-secret.asc"
);
add_setup(async () => {
await OpenPGPTestUtils.initOpenPGP();
const _account = createAccount();
const _identity = addIdentity(_account);
const _folder = await createSubfolder(
_account.incomingServer.rootFolder,
"test1"
);
const [id] = await OpenPGPTestUtils.importPrivateKey(
null,
new FileUtils.File(OPENPGP_KEY_PATH)
);
_identity.setUnicharAttribute("openpgp_key_id", id);
await createMessageFromFile(
_folder,
do_get_file("messages/encryptedNestedMessages.eml").path
);
});
add_task(async function test_messages_encrypted_attachment() {
const extension = ExtensionTestUtils.loadExtension({
files: {
"background.js": async () => {
const [folder] = await browser.folders.query({ name: "test1" });
const { accountId } = folder;
const { type } = await browser.accounts.get(accountId);
const { messages } = await browser.messages.list(folder.id);
browser.test.assertEq(1, messages.length);
browser.test.assertEq(1, messages[0].id);
// Get the raw decrypted content of the message (id: 1) via getRaw().
const raw1file = await browser.messages.getRaw(1, { decrypt: true });
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(raw1file instanceof File);
browser.test.assertEq("message-1.eml", raw1file.name);
browser.test.assertEq(type == "imap" ? 4024 : 3971, raw1file.size);
const raw1content = await raw1file.text();
browser.test.assertTrue(
!raw1content.includes("-----BEGIN PGP MESSAGE-----"),
"getRaw() of the outer message should be decrypted"
);
browser.test.assertTrue(
raw1content.includes("<sample.eml@mime.sample>"),
"getRaw() of the outer message should include the message ID of the outer message"
);
browser.test.assertTrue(
raw1content.includes("<sample-attached.eml@mime.sample>"),
"getRaw() of the outer message should include the message ID of the nested message"
);
browser.test.assertTrue(
raw1content.includes("<sample-nested-attached.eml@mime.sample>"),
"getRaw() of the outer message should include the message ID of the inner nested message"
);
// Get the full decrypted content of the message (id: 1) via getFull().
const full1 = await browser.messages.getFull(1, { decrypt: true });
window.assertDeepEqual(
{
contentType: "message/rfc822",
partName: "",
size: 2884,
decryptionStatus: "success",
headers: {
"message-id": ["<sample.eml@mime.sample>"],
date: ["Fri, 20 May 2000 00:29:55 -0400"],
to: ["Alice Lovelace <alice@openpgp.example>"],
from: ["Batman <bruce@wayne-enterprises.com>"],
subject: ["Encrypted, attached message with attachments"],
"mime-version": ["1.0"],
"content-type": ["message/rfc822"],
},
parts: [
{
contentType: "multipart/mixed",
headers: {
"content-type": [
'multipart/mixed; boundary="------------M20GNGI1rLZhtbYD7WFekIXJ"',
],
},
size: 2884,
partName: "1",
parts: [
{
contentType: "multipart/mixed",
headers: {
"content-type": [
'multipart/mixed; boundary="------------49CVLb1N6p6Spdka4qq7Naeg"',
],
},
size: 2884,
partName: "1.1",
parts: [
{
contentType: "text/html",
headers: {
"content-type": ["text/html; charset=UTF-8"],
"content-transfer-encoding": ["7bit"],
},
size: 248,
partName: "1.1.1",
body: '<html>\n <head>\n\n <meta http-equiv="content-type" content="text/html; charset=UTF-8">\n </head>\n <body>\n <p>This message has one normal attachment and one email attachment,\n which itself has 3 attachments.<br>\n </p>\n </body>\n</html>',
},
{
contentType: "message/rfc822",
headers: {
"content-type": [
'message/rfc822; charset=UTF-8; name="message1.eml"',
],
"content-disposition": [
'attachment; filename="message1.eml"',
],
"content-transfer-encoding": ["7bit"],
},
size: 2517,
partName: "1.1.2",
name: "message1.eml",
},
{
contentType: "image/png",
headers: {
"content-type": ["image/png;"],
"content-transfer-encoding": ["base64"],
"content-disposition": [
'attachment; filename="yellowPixel.png"',
],
},
size: 119,
partName: "1.1.3",
name: "yellowPixel.png",
},
],
},
],
},
],
},
full1,
"getFull() for the outer message should be correct"
);
// List the attachments from the decrypted outer message.
// Note: listAttachments() is always acting on the decrypted message.
const attachments1 = await browser.messages.listAttachments(1);
window.assertDeepEqual(
[
{
contentType: "message/rfc822",
name: "message1.eml",
size: 442,
partName: "1.1.2",
message: {
id: 2,
date: new Date("2000-05-17T23:32:47.000Z"),
author: "Superman <clark.kent@dailyplanet.com>",
recipients: ["Jimmy <jimmy.olsen@dailyplanet.com>"],
ccList: [],
bccList: [],
subject: "Test message 1",
read: false,
new: false,
headersOnly: false,
flagged: false,
junk: false,
junkScore: 0,
headerMessageId: "sample-attached.eml@mime.sample",
size: 442,
tags: [],
external: true,
},
},
{
contentType: "image/png",
name: "yellowPixel.png",
size: 119,
partName: "1.1.3",
},
],
attachments1,
"Attachments of the outer message should be correct",
{
strict: true,
}
);
// Get the actual attachments of the outer message as File objects.
// Note: getAttachmentFile() is always acting on the decrypted message.
const file112 = await browser.messages.getAttachmentFile(1, "1.1.2");
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(file112 instanceof File);
browser.test.assertEq("message1.eml", file112.name);
browser.test.assertEq(2517, file112.size);
const file112content = await file112.text();
browser.test.assertTrue(
!file112content.includes("<sample.eml@mime.sample>"),
"file112content should not include the message ID of the outer message"
);
browser.test.assertTrue(
file112content.includes("<sample-attached.eml@mime.sample>"),
"file112content should include the message ID of the nested message"
);
browser.test.assertTrue(
file112content.includes("<sample-nested-attached.eml@mime.sample>"),
"file112content should include the message ID of the inner nested message"
);
const file113 = await browser.messages.getAttachmentFile(1, "1.1.3");
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(file113 instanceof File);
browser.test.assertEq("yellowPixel.png", file113.name);
browser.test.assertEq(119, file113.size);
// Get the raw content of the nested message (id: 2) via getRaw().
const raw2file = await browser.messages.getRaw(2);
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(raw2file instanceof File);
browser.test.assertEq("message-2.eml", raw2file.name);
browser.test.assertEq(2517, raw2file.size);
const raw2content = await raw2file.text();
browser.test.assertEq(
raw2content,
file112content,
"getRaw() of the attached message and getAttachmentFile() of the attached message should be identical"
);
// Get the full content of the nested message via getFull().
const full2 = await browser.messages.getFull(2);
window.assertDeepEqual(
{
contentType: "message/rfc822",
partName: "",
size: 1180,
decryptionStatus: "none",
headers: {
"message-id": ["<sample-attached.eml@mime.sample>"],
from: ["Superman <clark.kent@dailyplanet.com>"],
to: ["Jimmy <jimmy.olsen@dailyplanet.com>"],
subject: ["Test message 1"],
date: ["Wed, 17 May 2000 19:32:47 -0400"],
"mime-version": ["1.0"],
"content-type": ["message/rfc822"],
},
parts: [
{
contentType: "multipart/mixed",
headers: {
"content-type": [
'multipart/mixed;\tboundary="----=_NextPart_000_0002_01BFC036.AE309650"',
],
},
size: 1180,
partName: "1",
parts: [
{
contentType: "text/plain",
headers: {
"content-type": ['text/plain;\tcharset="iso-8859-1"'],
"content-transfer-encoding": ["7bit"],
},
size: 35,
partName: "1.1",
body: "Message with multiple attachments.\n",
},
{
contentType: "image/png",
headers: {
"content-type": ['image/png;\tname="whitePixel.png"'],
"content-transfer-encoding": ["base64"],
"content-disposition": [
'attachment;\tfilename="whitePixel.png"',
],
},
size: 69,
partName: "1.2",
name: "whitePixel.png",
},
{
contentType: "image/png",
headers: {
"content-type": ['image/png;\tname="greenPixel.png"'],
"content-transfer-encoding": ["base64"],
"content-disposition": ["attachment"],
},
size: 119,
partName: "1.3",
name: "greenPixel.png",
},
{
contentType: "image/png",
headers: {
"content-type": ["image/png"],
"content-transfer-encoding": ["base64"],
"content-disposition": [
'attachment;\tfilename="redPixel.png"',
],
},
size: 119,
partName: "1.4",
name: "redPixel.png",
},
{
contentType: "message/rfc822",
headers: {
"content-type": [
'message/rfc822; charset=UTF-8; name="message2.eml"',
],
"content-disposition": [
'attachment; filename="message2.eml"',
],
"content-transfer-encoding": ["7bit"],
},
size: 838,
partName: "1.5",
name: "message2.eml",
},
],
},
],
},
full2,
"getFull() of the nested message should be correct"
);
// List the attachments from the nested message.
const attachments2 = await browser.messages.listAttachments(2);
window.assertDeepEqual(
[
{
contentType: "image/png",
name: "whitePixel.png",
size: 69,
partName: "1.2",
},
{
contentType: "image/png",
name: "greenPixel.png",
size: 119,
partName: "1.3",
},
{
contentType: "image/png",
name: "redPixel.png",
size: 119,
partName: "1.4",
},
{
contentType: "message/rfc822",
name: "message2.eml",
size: 100,
partName: "1.5",
message: {
id: 3,
date: new Date("2000-05-16T23:32:47.000Z"),
author: "Jimmy <jimmy.olsen@dailyplanet.com>",
recipients: ["Superman <clark.kent@dailyplanet.com>"],
ccList: [],
bccList: [],
subject: "Test message 2",
read: false,
new: false,
headersOnly: false,
flagged: false,
junk: false,
junkScore: 0,
headerMessageId: "sample-nested-attached.eml@mime.sample",
size: 100,
tags: [],
external: true,
},
},
],
attachments2,
"Attachments of the nested message should be correct",
{
strict: true,
}
);
// Get the actual attachments of the nested message as File objects.
const files = new Map();
const expectedAttachments2 = [
{
name: "whitePixel.png",
size: 69,
partName: "1.2",
},
{
name: "greenPixel.png",
size: 119,
partName: "1.3",
},
{
name: "redPixel.png",
size: 119,
partName: "1.4",
},
{
name: "message2.eml",
size: 838,
partName: "1.5",
},
];
for (const expectedAttachment2 of expectedAttachments2) {
const f = await browser.messages.getAttachmentFile(
2,
expectedAttachment2.partName
);
files.set(expectedAttachment2.partName, f);
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(f instanceof File);
browser.test.assertEq(
expectedAttachment2.name,
f.name,
`Name of part ${expectedAttachment2.partName} should be correct`
);
browser.test.assertEq(
expectedAttachment2.size,
f.size,
`Size of part ${expectedAttachment2.partName} should be correct`
);
}
// Check content of the inner nested message (part 1.5, id: 3).
const file15content = await files.get("1.5").text();
browser.test.assertTrue(
!file15content.includes("<sample.eml@mime.sample>"),
"file15content should not include the message ID of the outer message"
);
browser.test.assertTrue(
!file15content.includes("<sample-attached.eml@mime.sample>"),
"file15content should not include the message ID of the nested message"
);
browser.test.assertTrue(
file15content.includes("<sample-nested-attached.eml@mime.sample>"),
"file15content should include the message ID of the inner nested message"
);
// Get the raw content of the inner nested message (part 1.5, id: 3) via
// getRaw().
const raw3file = await browser.messages.getRaw(3);
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(raw3file instanceof File);
browser.test.assertEq("message-3.eml", raw3file.name);
browser.test.assertEq(838, raw3file.size);
const raw3content = await raw3file.text();
browser.test.assertEq(
raw3content,
file15content,
"getRaw() of the inner attached message and getAttachmentFile() of the inner attached message should be identical"
);
// Get the full content of the inner nested message via getFull().
const full3 = await browser.messages.getFull(3);
window.assertDeepEqual(
{
contentType: "message/rfc822",
partName: "",
size: 100,
decryptionStatus: "none",
headers: {
"message-id": ["<sample-nested-attached.eml@mime.sample>"],
from: ["Jimmy <jimmy.olsen@dailyplanet.com>"],
to: ["Superman <clark.kent@dailyplanet.com>"],
subject: ["Test message 2"],
date: ["Wed, 16 May 2000 19:32:47 -0400"],
"mime-version": ["1.0"],
"content-type": ["message/rfc822"],
},
parts: [
{
contentType: "multipart/mixed",
headers: {
"content-type": [
'multipart/mixed; boundary="----=_NextPart_000_0003_01BFC036.AE309650"',
],
},
size: 100,
partName: "1",
parts: [
{
contentType: "text/plain",
headers: {
"content-type": ['text/plain; charset="iso-8859-1"'],
"content-transfer-encoding": ["7bit"],
},
size: 31,
partName: "1.1",
body: "This message has an attachment\n",
},
{
contentType: "image/png",
headers: {
"content-type": ['image/png; name="whitePixel.png"'],
"content-transfer-encoding": ["base64"],
"content-disposition": [
'attachment; filename="whitePixel.png"',
],
},
size: 69,
partName: "1.2",
name: "whitePixel.png",
},
],
},
],
},
full3,
"getFull() of the inner nested message should be correct"
);
// List the attachments from the inner nested message.
const attachments3 = await browser.messages.listAttachments(3);
window.assertDeepEqual(
[
{
contentType: "image/png",
name: "whitePixel.png",
size: 69,
partName: "1.2",
},
],
attachments3,
"Attachments of the inner nested message should be correct",
{
strict: true,
}
);
// Get the actual attachments of the inner nested message as File objects.
const expectedAttachments3 = [
{
name: "whitePixel.png",
size: 69,
partName: "1.2",
},
];
for (const expectedAttachment3 of expectedAttachments3) {
const f = await browser.messages.getAttachmentFile(
3,
expectedAttachment3.partName
);
files.set(expectedAttachment3.partName, f);
// eslint-disable-next-line mozilla/use-isInstance
browser.test.assertTrue(f instanceof File);
browser.test.assertEq(
expectedAttachment3.name,
f.name,
`Name of part ${expectedAttachment3.partName} should be correct`
);
browser.test.assertEq(
expectedAttachment3.size,
f.size,
`Size of part ${expectedAttachment3.partName} should be correct`
);
}
browser.test.notifyPass("finished");
},
"utils.js": await getUtilsJS(),
},
manifest: {
manifest_version: 3,
background: { scripts: ["utils.js", "background.js"] },
permissions: ["accountsRead", "messagesRead"],
},
});
await extension.startup();
await extension.awaitFinish("finished");
await extension.unload();
});

Просмотреть файл

@ -9,6 +9,7 @@ tags = addrbook
[test_ext_messages_attachments.js] # IMAP disabled (doesn't work with test server).
support-files = messages/**
[test_ext_messages_bug_1737532.js]
[test_ext_messages_encrypted_attachment.js]
[test_ext_messages_get.js] # PGP test disabled for NNTP.
support-files = messages/**
[test_ext_messages_id.js] # Disabled for NNTP (message move not supported).