Bug 1851536 - Test the quota status bar panel and the folder properties dialog quota tab. r=mkmelin
This includes an incomplete implementation of RFC 2087 for the IMAP fakeserver, and resurrecting a long-dead function in nsImapIncomingServer which should make future tests involving IMAP a bit easier to write. Differential Revision: https://phabricator.services.mozilla.com/D194174 --HG-- extra : rebase_source : e70724b757af11fbb90b3881975eb7a3467cee5c extra : amend_source : 3cc8117ce9a674c6edd08bbb35f249ddc3ac465e
This commit is contained in:
Родитель
244dff4ca8
Коммит
b3bcc45f50
|
@ -674,7 +674,7 @@ function openActivityMgr() {
|
|||
function openFolderQuota() {
|
||||
document
|
||||
.getElementById("tabmail")
|
||||
.currentAbout3Pane?.folderPane.editFolder("QuotaTab");
|
||||
.currentAbout3Pane?.folderPane.editFolder(undefined, "QuotaTab");
|
||||
}
|
||||
|
||||
function openIMAccountMgr() {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
const {
|
||||
IMAP_GMAIL_extension,
|
||||
IMAP_RFC2087_extension,
|
||||
IMAP_RFC2197_extension,
|
||||
IMAP_RFC2342_extension,
|
||||
IMAP_RFC3348_extension,
|
||||
|
@ -108,3 +109,32 @@ export class GmailServer extends IMAPServer {
|
|||
this.testScope.registerCleanupFunction(() => this.close());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple IMAP server, with RFC2087 extension, for testing purposes.
|
||||
*/
|
||||
export class QuotaServer extends IMAPServer {
|
||||
open() {
|
||||
this.daemon = new ImapDaemon();
|
||||
this.server = new nsMailServer(daemon => {
|
||||
const handler = new IMAP_RFC3501_handler(daemon, {
|
||||
username: this.username,
|
||||
});
|
||||
mixinExtension(handler, IMAP_RFC2087_extension);
|
||||
return handler;
|
||||
}, this.daemon);
|
||||
this.server.start();
|
||||
|
||||
this.testScope.registerCleanupFunction(() => this.close());
|
||||
}
|
||||
|
||||
setQuota(folder, name, usage, limit) {
|
||||
const mailbox = this.daemon.getMailbox(folder.name);
|
||||
mailbox.quota = mailbox.quota ?? {};
|
||||
if (limit) {
|
||||
mailbox.quota[name] = { usage, limit };
|
||||
} else {
|
||||
delete mailbox.quota[name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ skip-if = headless
|
|||
[browser_paneFocus.js]
|
||||
[browser_paneSplitter.js]
|
||||
[browser_preferDisplayName.js]
|
||||
[browser_quota.js]
|
||||
[browser_searchBar.js]
|
||||
[browser_searchMessages.js]
|
||||
[browser_spacesToolbar.js]
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
/* 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/. */
|
||||
|
||||
const { QuotaServer } = ChromeUtils.importESModule(
|
||||
"resource://testing-common/IMAPServer.sys.mjs"
|
||||
);
|
||||
|
||||
let imapServer;
|
||||
|
||||
const tabmail = document.getElementById("tabmail");
|
||||
const about3Pane = tabmail.currentAbout3Pane;
|
||||
let imapRootFolder, imapFolder;
|
||||
|
||||
add_setup(async function () {
|
||||
// This test uses a single IMAP connection so that we can be sure it is idle
|
||||
// before using it.
|
||||
Services.prefs.setIntPref("mail.server.default.max_cached_connections", 1);
|
||||
imapServer = new QuotaServer(this);
|
||||
|
||||
const imapAccount = MailServices.accounts.createAccount();
|
||||
imapAccount.addIdentity(MailServices.accounts.createIdentity());
|
||||
imapAccount.incomingServer = MailServices.accounts.createIncomingServer(
|
||||
`${imapAccount.key}user`,
|
||||
"localhost",
|
||||
"imap"
|
||||
);
|
||||
imapAccount.incomingServer.port = imapServer.port;
|
||||
imapAccount.incomingServer.username = "user";
|
||||
imapAccount.incomingServer.password = "password";
|
||||
imapAccount.incomingServer.deleteModel = Ci.nsMsgImapDeleteModels.IMAPDelete;
|
||||
imapRootFolder = imapAccount.incomingServer.rootFolder;
|
||||
imapFolder = imapRootFolder
|
||||
.getFolderWithFlags(Ci.nsMsgFolderFlags.Inbox)
|
||||
.QueryInterface(Ci.nsIMsgImapMailFolder);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
MailServices.accounts.removeAccount(imapAccount, false);
|
||||
Services.prefs.clearUserPref("mail.server.default.max_cached_connections");
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function () {
|
||||
const quotaPanel = document.getElementById("quotaPanel");
|
||||
|
||||
// Load the folder with no quota.
|
||||
|
||||
about3Pane.displayFolder(imapFolder);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(quotaPanel), "panel should be hidden");
|
||||
|
||||
// Reload the folder with usage below mail.quota.mainwindow_threshold.show.
|
||||
|
||||
about3Pane.displayFolder(imapRootFolder);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(quotaPanel), "panel should be hidden");
|
||||
|
||||
await updateQuota(20, 123);
|
||||
about3Pane.displayFolder(imapFolder);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(quotaPanel), "panel should be hidden");
|
||||
|
||||
// Reload the folder with usage above mail.quota.mainwindow_threshold.show
|
||||
// but below mail.quota.mainwindow_threshold.warning.
|
||||
|
||||
about3Pane.displayFolder(imapRootFolder);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(quotaPanel), "panel should be hidden");
|
||||
|
||||
await updateQuota(98, 123);
|
||||
about3Pane.displayFolder(imapFolder);
|
||||
checkStatus(79, "98.0 KB", "123 KB");
|
||||
|
||||
// Reload the folder with usage above mail.quota.mainwindow_threshold.warning
|
||||
// but below mail.quota.mainwindow_threshold.critical.
|
||||
|
||||
about3Pane.displayFolder(imapRootFolder);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(quotaPanel), "panel should be hidden");
|
||||
|
||||
await updateQuota(105, 123);
|
||||
about3Pane.displayFolder(imapFolder);
|
||||
checkStatus(85, "105 KB", "123 KB", "alert-warning");
|
||||
|
||||
about3Pane.displayFolder(imapRootFolder);
|
||||
Assert.ok(BrowserTestUtils.is_hidden(quotaPanel), "panel should be hidden");
|
||||
|
||||
// Reload the folder with usage above mail.quota.mainwindow_threshold.critical.
|
||||
|
||||
await updateQuota(120, 123);
|
||||
about3Pane.displayFolder(imapFolder);
|
||||
checkStatus(97, "120 KB", "123 KB", "alert-critical");
|
||||
|
||||
// Click on the status bar panel to open the folder properties dialog.
|
||||
|
||||
const folderPropsPromise = BrowserTestUtils.promiseAlertDialog(
|
||||
undefined,
|
||||
"chrome://messenger/content/folderProps.xhtml",
|
||||
{
|
||||
async callback(win) {
|
||||
await SimpleTest.promiseFocus(win);
|
||||
|
||||
const doc = win.document;
|
||||
const tabBox = doc.getElementById("folderPropTabBox");
|
||||
const quotaStatus = doc.getElementById("folderQuotaStatus");
|
||||
const quotaDetails = doc.getElementById("quotaDetails");
|
||||
const cancelButton = doc.querySelector("dialog").getButton("cancel");
|
||||
|
||||
Assert.equal(
|
||||
tabBox.selectedPanel.id,
|
||||
"quotaPanel",
|
||||
"quota panel should be selected"
|
||||
);
|
||||
await TestUtils.waitForCondition(
|
||||
() => BrowserTestUtils.is_hidden(quotaStatus),
|
||||
"waiting for quota UI to update"
|
||||
);
|
||||
Assert.ok(
|
||||
BrowserTestUtils.is_visible(quotaDetails),
|
||||
"quota details should be visible"
|
||||
);
|
||||
Assert.equal(
|
||||
quotaDetails.childElementCount,
|
||||
1,
|
||||
"one quota should be displayed"
|
||||
);
|
||||
|
||||
const li = quotaDetails.firstElementChild;
|
||||
Assert.equal(li.querySelector("span").textContent, "STORAGE");
|
||||
Assert.equal(li.querySelector("progress").value, 120);
|
||||
Assert.equal(li.querySelector("progress").max, 123);
|
||||
Assert.deepEqual(
|
||||
document.l10n.getAttributes(li.querySelector("span:nth-child(3)")),
|
||||
{ id: "quota-percent-used", args: { percent: 97 } }
|
||||
);
|
||||
Assert.equal(
|
||||
li.querySelector("span:nth-child(4)").textContent,
|
||||
"120 KB / 123 KB"
|
||||
);
|
||||
|
||||
cancelButton.click();
|
||||
},
|
||||
}
|
||||
);
|
||||
EventUtils.synthesizeMouseAtCenter(quotaPanel, {}, window);
|
||||
await folderPropsPromise;
|
||||
});
|
||||
|
||||
async function updateQuota(usage, limit) {
|
||||
// Drain the event queue so that the folder just displayed starts to use the
|
||||
// IMAP connection.
|
||||
await TestUtils.waitForTick();
|
||||
await TestUtils.waitForCondition(() => {
|
||||
try {
|
||||
return imapFolder.server.numIdleConnections;
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
}
|
||||
return 0;
|
||||
}, "waiting for IMAP connection to become idle");
|
||||
imapServer.setQuota(imapFolder, "STORAGE", usage, limit);
|
||||
// Force the folder to be updated from the server.
|
||||
await new Promise(resolve =>
|
||||
imapFolder.updateFolderWithListener(window.msgWindow, {
|
||||
OnStartRunningUrl() {},
|
||||
OnStopRunningUrl() {
|
||||
resolve();
|
||||
},
|
||||
})
|
||||
);
|
||||
await TestUtils.waitForCondition(
|
||||
() => imapFolder.getQuota().length == 1,
|
||||
"waiting for the folder to have a quota"
|
||||
);
|
||||
}
|
||||
|
||||
function checkStatus(percent, usage, limit, className) {
|
||||
const quotaPanel = document.getElementById("quotaPanel");
|
||||
const quotaMeter = document.getElementById("quotaMeter");
|
||||
const quotaLabel = document.getElementById("quotaLabel");
|
||||
|
||||
Assert.ok(
|
||||
BrowserTestUtils.is_visible(quotaPanel),
|
||||
"status bar panel should be visible"
|
||||
);
|
||||
Assert.equal(
|
||||
quotaMeter.value,
|
||||
percent,
|
||||
"meter should have the correct value"
|
||||
);
|
||||
Assert.equal(quotaMeter.max, 100, "meter should have the correct maximum");
|
||||
Assert.deepEqual(
|
||||
document.l10n.getAttributes(quotaLabel),
|
||||
{
|
||||
id: "quota-panel-percent-used",
|
||||
args: { percent, usage, limit },
|
||||
},
|
||||
"label should have the correct text"
|
||||
);
|
||||
Assert.equal(
|
||||
quotaPanel.classList.contains("alert-warning"),
|
||||
className == "alert-warning",
|
||||
"panel should have the correct classes"
|
||||
);
|
||||
Assert.equal(
|
||||
quotaPanel.classList.contains("alert-critical"),
|
||||
className == "alert-critical",
|
||||
"panel should have the correct classes"
|
||||
);
|
||||
}
|
|
@ -73,6 +73,9 @@ interface nsIImapIncomingServer : nsISupports {
|
|||
/// Max age of messages we will autosync to, or keep in offline store.
|
||||
attribute long autoSyncMaxAgeDays;
|
||||
|
||||
/** Count of non-busy connections in cache. */
|
||||
readonly attribute long numIdleConnections;
|
||||
|
||||
/**
|
||||
* See IMAP RFC 6855
|
||||
*/
|
||||
|
|
|
@ -62,7 +62,6 @@ class nsImapIncomingServer : public nsMsgIncomingServer,
|
|||
NS_IMETHOD GetSearchScope(nsMsgSearchScopeValue* searchScope) override;
|
||||
NS_IMETHOD GetServerRequiresPasswordForBiff(
|
||||
bool* aServerRequiresPasswordForBiff) override;
|
||||
NS_IMETHOD GetNumIdleConnections(int32_t* aNumIdleConnections);
|
||||
NS_IMETHOD ForgetSessionPassword(bool modifyLogin) override;
|
||||
NS_IMETHOD GetMsgFolderFromURI(nsIMsgFolder* aFolderResource,
|
||||
const nsACString& aURI,
|
||||
|
|
|
@ -13,6 +13,7 @@ var EXPORTED_SYMBOLS = [
|
|||
"IMAP_GMAIL_extension",
|
||||
"IMAP_MOVE_extension",
|
||||
"IMAP_CUSTOM_extension",
|
||||
"IMAP_RFC2087_extension",
|
||||
"IMAP_RFC2197_extension",
|
||||
"IMAP_RFC2342_extension",
|
||||
"IMAP_RFC3348_extension",
|
||||
|
@ -2185,6 +2186,23 @@ var IMAP_CUSTOM_extension = {
|
|||
kCapabilities: ["X-CUSTOM1"],
|
||||
};
|
||||
|
||||
// RFC 2087: Quota (incomplete implementation)
|
||||
var IMAP_RFC2087_extension = {
|
||||
GETQUOTAROOT(args) {
|
||||
const mailbox = this._daemon.getMailbox(args[0]);
|
||||
const quota = mailbox.quota ?? {};
|
||||
const response = [`* QUOTAROOT INBOX ""`];
|
||||
for (const [name, { usage, limit }] of Object.entries(quota)) {
|
||||
response.push(`* QUOTA "" (${name} ${usage} ${limit})`);
|
||||
}
|
||||
response.push("OK Getquota completed");
|
||||
return response.join("\0");
|
||||
},
|
||||
kCapabilities: ["QUOTA"],
|
||||
_argFormat: { GETQUOTAROOT: ["mailbox"] },
|
||||
_enabledCommands: { 1: ["GETQUOTAROOT"], 2: ["GETQUOTAROOT"] },
|
||||
};
|
||||
|
||||
// RFC 2197: ID
|
||||
var IMAP_RFC2197_extension = {
|
||||
ID(args) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче