summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/folder-display/browser_messageCommandsOnMsgstore.js
blob: 899a211de82fd54e2e76e6292d43793f2f8a268e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
/* 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/. */

/**
 * This tests some commands on messages via the UI. But we specifically check,
 * whether the commands have an effect in the message store on disk, i.e. the
 * markings on the messages are stored in the msgStore, not only in the database.
 * For now, it checks for bug 840418.
 */

"use strict";

var utils = ChromeUtils.import("resource://testing-common/mozmill/utils.jsm");
const {
  open_compose_with_forward,
  open_compose_with_reply,
  setup_msg_contents,
} = ChromeUtils.import("resource://testing-common/mozmill/ComposeHelpers.jsm");
const {
  be_in_folder,
  create_folder,
  empty_folder,
  get_special_folder,
  make_message_sets_in_folders,
  mc,
  press_delete,
  right_click_on_row,
  select_click_row,
} = ChromeUtils.import(
  "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
const {
  click_menus_in_sequence,
  plan_for_window_close,
  wait_for_window_close,
} = ChromeUtils.import("resource://testing-common/mozmill/WindowHelpers.jsm");

const { MailServices } = ChromeUtils.import(
  "resource:///modules/MailServices.jsm"
);

let gInbox;
let gOutbox;
let gAutoRead;

add_setup(async function () {
  gAutoRead = Services.prefs.getBoolPref("mailnews.mark_message_read.auto");
  Services.prefs.setBoolPref("mailnews.mark_message_read.auto", false);

  gOutbox = await get_special_folder(Ci.nsMsgFolderFlags.Queue);
  gInbox = await create_folder("MsgStoreChecks");
  await make_message_sets_in_folders([gInbox], [{ count: 6 }]);

  // We delete the first message so that we have to compact anything.
  await be_in_folder(gInbox);
  let curMessage = select_click_row(0);
  press_delete(mc);
  Assert.notEqual(curMessage, select_click_row(0));

  let urlListener = {
    compactDone: false,

    OnStartRunningUrl(aUrl) {},
    OnStopRunningUrl(aUrl, aExitCode) {
      Assert.equal(aExitCode, 0);
      Assert.ok(gInbox.msgDatabase.summaryValid);
      this.compactDone = true;
    },
  };

  // Compaction adds the X-Mozilla-Status rows into the messages
  // that we will need later on.
  Assert.ok(gInbox.msgStore.supportsCompaction);
  gInbox.compact(urlListener, null);

  utils.waitFor(
    function () {
      return urlListener.compactDone;
    },
    "Timeout waiting for compact to complete",
    10000,
    100
  );
});

/**
 * Checks that a message has particular status stored in the mbox file,
 * in the X-Mozilla-Status header.
 *
 * @param folder         The folder containing the message to check.
 * @param offset         Offset to the start of the message within mbox file.
 * @param expectedStatus The required status of the message.
 */
async function check_status(folder, offset, expectedStatus) {
  let mboxstring = await IOUtils.readUTF8(folder.filePath.path);

  // Ah-hoc header parsing. Only check the first 1KB because the X-Mozilla-*
  // headers should be near the start.
  let msg = mboxstring.slice(offset, offset + 1024);
  msg = msg.replace(/\r/g, ""); // Simplify by using LFs only.
  for (let line of msg.split("\n")) {
    if (line == "") {
      break; // end of header block.
    }
    if (line.startsWith("X-Mozilla-Status:")) {
      let hexValue = /:\s*([0-9a-f]+)/i.exec(line)[1];
      let gotStatus = parseInt(hexValue, 16);
      Assert.equal(
        gotStatus,
        expectedStatus,
        `Check X-Mozilla-Status (for msg at offset ${offset})`
      );
      return;
    }
  }
  // If we got this far, we didn't find the header.
  Assert.ok(
    false,
    `Find X-Mozilla-Status header (for msg at offset ${offset})`
  );
}

add_task(async function test_mark_messages_read() {
  be_in_folder(gOutbox); // TODO shouldn't have to swap folders
  // 5 messages in the folder
  await be_in_folder(gInbox);
  let curMessage = select_click_row(0);
  // Store the offset because it will be unavailable via the hdr
  // after the message is deleted.
  let offset = curMessage.messageOffset;
  await check_status(gInbox, offset, 0); // status = unread
  press_delete(mc);
  Assert.notEqual(curMessage, select_click_row(0));
  await check_status(
    gInbox,
    offset,
    Ci.nsMsgMessageFlags.Read + Ci.nsMsgMessageFlags.Expunged
  );

  // 4 messages in the folder.
  curMessage = select_click_row(0);
  await check_status(gInbox, curMessage.messageOffset, 0); // status = unread

  // Make sure we can mark all read with >0 messages unread.
  await right_click_on_row(0);
  let hiddenPromise = BrowserTestUtils.waitForEvent(
    getMailContext(),
    "popuphidden"
  );
  await click_menus_in_sequence(getMailContext(), [
    { id: "mailContext-mark" },
    { id: "mailContext-markAllRead" },
  ]);
  await hiddenPromise;
  await new Promise(resolve => requestAnimationFrame(resolve));

  // All the 4 messages should now be read.
  Assert.ok(curMessage.isRead, "Message should have been marked Read!");
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Read
  );
  curMessage = select_click_row(1);
  Assert.ok(curMessage.isRead, "Message should have been marked Read!");
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Read
  );
  curMessage = select_click_row(2);
  Assert.ok(curMessage.isRead, "Message should have been marked Read!");
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Read
  );
  curMessage = select_click_row(3);
  Assert.ok(curMessage.isRead, "Message should have been marked Read!");
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Read
  );

  // Let's have the last message unread.
  await right_click_on_row(3);
  hiddenPromise = BrowserTestUtils.waitForEvent(
    getMailContext(),
    "popuphidden"
  );
  await click_menus_in_sequence(getMailContext(), [
    { id: "mailContext-mark" },
    { id: "mailContext-markUnread" },
  ]);
  await hiddenPromise;
  await new Promise(resolve => requestAnimationFrame(resolve));

  Assert.ok(!curMessage.isRead, "Message should have not been marked Read!");
  await check_status(gInbox, curMessage.messageOffset, 0);
});

add_task(async function test_mark_messages_flagged() {
  // Mark a message with the star.
  let curMessage = select_click_row(1);
  await right_click_on_row(1);
  let hiddenPromise = BrowserTestUtils.waitForEvent(
    getMailContext(),
    "popuphidden"
  );
  await click_menus_in_sequence(getMailContext(), [
    { id: "mailContext-mark" },
    { id: "mailContext-markFlagged" },
  ]);
  await hiddenPromise;
  await new Promise(resolve => requestAnimationFrame(resolve));

  Assert.ok(curMessage.isFlagged, "Message should have been marked Flagged!");
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Read + Ci.nsMsgMessageFlags.Marked
  );
});

async function subtest_check_queued_message() {
  // Always check the last message in the Outbox for the correct flag.
  await be_in_folder(gOutbox);
  let lastMsg = [...gOutbox.messages].pop();
  await check_status(
    gOutbox,
    lastMsg.messageOffset,
    Ci.nsMsgMessageFlags.Queued
  );
}

/**
 * Create a reply or forward of a message and queue it for sending later.
 *
 * @param aMsgRow  Row index of message in Inbox that is to be replied/forwarded.
 * @param aReply   true = reply, false = forward.
 */
async function reply_forward_message(aMsgRow, aReply) {
  await be_in_folder(gInbox);
  select_click_row(aMsgRow);
  let cwc;
  if (aReply) {
    // Reply to the message.
    cwc = open_compose_with_reply();
  } else {
    // Forward the message.
    cwc = open_compose_with_forward();
    // Type in some recipient.
    setup_msg_contents(cwc, "somewhere@host.invalid", "", "");
  }

  // Send it later.
  plan_for_window_close(cwc);
  // Ctrl+Shift+Return = Send Later
  cwc.window.document.getElementById("messageEditor").focus();
  EventUtils.synthesizeKey(
    "VK_RETURN",
    {
      shiftKey: true,
      accelKey: true,
    },
    cwc.window
  );
  wait_for_window_close(cwc);

  await subtest_check_queued_message();

  // Now this is hacky. We can't get the message to be sent out of TB because there
  // is no fake SMTP server support yet.
  // But we know that upon real sending of the message, the code would/should call
  // .addMessageDispositionState(). So call it directly and check the expected
  // flags were set. This is risky as the real code could change and call
  // a different function and the purpose of this test would be lost.
  await be_in_folder(gInbox);
  let curMessage = select_click_row(aMsgRow);
  let disposition = aReply
    ? gInbox.nsMsgDispositionState_Replied
    : gInbox.nsMsgDispositionState_Forwarded;
  gInbox.addMessageDispositionState(curMessage, disposition);
}

add_task(async function test_mark_messages_replied() {
  await reply_forward_message(2, true);
  let curMessage = select_click_row(2);
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Replied + Ci.nsMsgMessageFlags.Read
  );
});

add_task(async function test_mark_messages_forwarded() {
  await be_in_folder(gInbox);
  // Forward a clean message.
  await reply_forward_message(3, false);
  let curMessage = select_click_row(3);
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Forwarded
  );

  // Forward a message that is read and already replied to.
  curMessage = select_click_row(2);
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Replied + Ci.nsMsgMessageFlags.Read
  );
  await reply_forward_message(2, false);
  await check_status(
    gInbox,
    curMessage.messageOffset,
    Ci.nsMsgMessageFlags.Forwarded +
      Ci.nsMsgMessageFlags.Replied +
      Ci.nsMsgMessageFlags.Read
  );
});

registerCleanupFunction(async function () {
  Services.prefs.setBoolPref("mailnews.mark_message_read.auto", gAutoRead);
  // Clear all the created messages.
  await be_in_folder(gInbox.parent);
  await empty_folder(gInbox);
  // await empty_folder(gOutbox); TODO
  gInbox.server.rootFolder.emptyTrash(null);
});