summaryrefslogtreecommitdiffstats
path: root/comm/mail/test/browser/quick-filter-bar/browser_filterLogic.js
blob: fb863ea154e4054d457642451ab9e159f3dd91f4 (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
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
/* 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/. */

/**
 * Verify that we are constructing the filters that we expect and that they
 * are hooked up to the right buttons.
 */

"use strict";

var {
  assert_messages_in_view,
  assert_messages_not_in_view,
  be_in_folder,
  create_folder,
  delete_messages,
  get_about_3pane,
  make_message_sets_in_folders,
  mc,
} = ChromeUtils.import(
  "resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
var {
  assert_quick_filter_bar_visible,
  assert_results_label_count,
  assert_text_constraints_checked,
  clear_constraints,
  set_filter_text,
  toggle_boolean_constraints,
  toggle_quick_filter_bar,
  toggle_tag_constraints,
  toggle_tag_mode,
  toggle_text_constraints,
  cleanup_qfb_button,
} = ChromeUtils.import(
  "resource://testing-common/mozmill/QuickFilterBarHelpers.jsm"
);

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

add_setup(async function () {
  // Quick filter bar is hidden by default, need to toggle it on. To toggle
  // quick filter bar, need to be inside folder
  const folder = await create_folder("QuickFilterBarFilterFilterLogicSetup");
  await be_in_folder(folder);
  await ensure_table_view();
  await toggle_quick_filter_bar();

  registerCleanupFunction(async function () {
    await ensure_cards_view();
    await cleanup_qfb_button();
    // Quick filter bar is hidden by default, need to toggle it off.
    await toggle_quick_filter_bar();
  });
});

add_task(async function test_filter_unread() {
  let folder = await create_folder("QuickFilterBarFilterUnread");
  let [unread, read] = await make_message_sets_in_folders(
    [folder],
    [{ count: 1 }, { count: 1 }]
  );
  read.setRead(true);

  await be_in_folder(folder);
  toggle_boolean_constraints("unread");
  assert_messages_in_view(unread);
  teardownTest();
});

add_task(async function test_filter_starred() {
  let folder = await create_folder("QuickFilterBarFilterStarred");
  let [, starred] = await make_message_sets_in_folders(
    [folder],
    [{ count: 1 }, { count: 1 }]
  );
  starred.setStarred(true);

  await be_in_folder(folder);
  toggle_boolean_constraints("starred");
  assert_messages_in_view(starred);
  teardownTest();
});

add_task(async function test_filter_simple_intersection_unread_and_starred() {
  let folder = await create_folder("QuickFilterBarFilterUnreadAndStarred");
  let [, readUnstarred, unreadStarred, readStarred] =
    await make_message_sets_in_folders(
      [folder],
      [{ count: 1 }, { count: 1 }, { count: 1 }, { count: 1 }]
    );
  readUnstarred.setRead(true);
  unreadStarred.setStarred(true);
  readStarred.setRead(true);
  readStarred.setStarred(true);

  await be_in_folder(folder);
  toggle_boolean_constraints("unread", "starred");

  assert_messages_in_view(unreadStarred);
  teardownTest();
});

add_task(async function test_filter_attachments() {
  let attachSetDef = {
    count: 1,
    attachments: [
      {
        filename: "foo.png",
        contentType: "image/png",
        encoding: "base64",
        charset: null,
        body: "YWJj\n",
        format: null,
      },
    ],
  };
  let noAttachSetDef = {
    count: 1,
  };

  let folder = await create_folder("QuickFilterBarFilterAttachments");
  let [, setAttach] = await make_message_sets_in_folders(
    [folder],
    [noAttachSetDef, attachSetDef]
  );

  await be_in_folder(folder);
  toggle_boolean_constraints("attachments");

  assert_messages_in_view(setAttach);
  teardownTest();
});

/**
 * Create a card for the given e-mail address, adding it to the first address
 * book we can find.
 */
function add_email_to_address_book(aEmailAddr) {
  let card = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance(
    Ci.nsIAbCard
  );
  card.primaryEmail = aEmailAddr;

  for (let addrbook of MailServices.ab.directories) {
    addrbook.addCard(card);
    return;
  }

  throw new Error("Unable to find any suitable address book.");
}

add_task(async function test_filter_in_address_book() {
  let bookSetDef = {
    from: ["Qbert Q Qbington", "q@q.invalid"],
    count: 1,
  };
  add_email_to_address_book(bookSetDef.from[1]);
  let folder = await create_folder("MesssageFilterBarInAddressBook");
  let [setBook] = await make_message_sets_in_folders(
    [folder],
    [bookSetDef, { count: 1 }]
  );
  await be_in_folder(folder);
  toggle_boolean_constraints("addrbook");
  assert_messages_in_view(setBook);
  teardownTest();
});

add_task(async function test_filter_tags() {
  let folder = await create_folder("QuickFilterBarTags");
  const tagA = "$label1",
    tagB = "$label2",
    tagC = "$label3";
  let [setNoTag, setTagA, setTagB, setTagAB, setTagC] =
    await make_message_sets_in_folders(
      [folder],
      [{ count: 1 }, { count: 1 }, { count: 1 }, { count: 1 }, { count: 1 }]
    );
  setTagA.addTag(tagA);
  setTagB.addTag(tagB);
  setTagAB.addTag(tagA);
  setTagAB.addTag(tagB);
  setTagC.addTag(tagC);

  await be_in_folder(folder);
  toggle_boolean_constraints("tags"); // must have a tag
  assert_messages_in_view([setTagA, setTagB, setTagAB, setTagC]);

  toggle_tag_constraints(tagA); // must have tag A
  assert_messages_in_view([setTagA, setTagAB]);

  toggle_tag_constraints(tagB);
  // mode is OR by default -> must have tag A or tag B
  assert_messages_in_view([setTagA, setTagB, setTagAB]);

  toggle_tag_mode();
  // mode is now AND -> must have tag A and tag B
  assert_messages_in_view([setTagAB]);

  toggle_tag_constraints(tagA); // must have tag B
  assert_messages_in_view([setTagB, setTagAB]);

  toggle_tag_constraints(tagB); // have have a tag
  assert_messages_in_view([setTagA, setTagB, setTagAB, setTagC]);

  toggle_boolean_constraints("tags"); // no constraints
  assert_messages_in_view([setNoTag, setTagA, setTagB, setTagAB, setTagC]);

  // If we have filtered to a specific tag and we disable the tag filter
  // entirely, make sure that when we turn it back on we are just back to "any
  // tag".
  toggle_boolean_constraints("tags");
  toggle_tag_constraints(tagC);
  assert_messages_in_view(setTagC);

  toggle_boolean_constraints("tags"); // no constraints
  toggle_boolean_constraints("tags"); // should be any tag (not tagC!)
  assert_messages_in_view([setTagA, setTagB, setTagAB, setTagC]);
  teardownTest();
});

add_task(async function test_filter_text_single_word_and_predicates() {
  let folder = await create_folder("QuickFilterBarTextSingleWord");
  let whoFoo = ["zabba", "foo@madeup.invalid"];
  let [, setSenderFoo, setRecipientsFoo, setSubjectFoo, setBodyFoo] =
    await make_message_sets_in_folders(
      [folder],
      [
        { count: 1 },
        { count: 1, from: whoFoo },
        { count: 1, to: [whoFoo] },
        { count: 1, subject: "foo" },
        { count: 1, body: { body: "foo" } },
      ]
    );
  await be_in_folder(folder);

  // by default, sender/recipients/subject are selected
  assert_text_constraints_checked("sender", "recipients", "subject");

  // con defaults, por favor
  set_filter_text("foo");
  assert_messages_in_view([setSenderFoo, setRecipientsFoo, setSubjectFoo]);
  // note: we sequence the changes in the list so there is always at least one
  //  dude selected.  selecting down to nothing has potential UI implications
  //  we don't want this test to get affected by.
  // sender only
  toggle_text_constraints("recipients", "subject");
  assert_messages_in_view(setSenderFoo);
  // recipients only
  toggle_text_constraints("recipients", "sender");
  assert_messages_in_view(setRecipientsFoo);
  // subject only
  toggle_text_constraints("subject", "recipients");
  assert_messages_in_view(setSubjectFoo);
  // body only
  toggle_text_constraints("body", "subject");
  assert_messages_in_view(setBodyFoo);
  // everybody
  toggle_text_constraints("sender", "recipients", "subject");
  assert_messages_in_view([
    setSenderFoo,
    setRecipientsFoo,
    setSubjectFoo,
    setBodyFoo,
  ]);

  // sanity check non-matching
  set_filter_text("notgonnamatchevercauseisayso");
  assert_messages_in_view([]);
  // disable body, still should get nothing
  toggle_text_constraints("body");
  assert_messages_in_view([]);

  // (we are leaving with the defaults once again active)
  assert_text_constraints_checked("sender", "recipients", "subject");
  teardownTest();
});

/**
 * Verify that the multi-word logic is actually splitting the words into
 *  different terms and that the terms can match in different predicates.
 *  This means that given "foo bar" we should be able to match "bar foo" in
 *  a subject and "foo" in the sender and "bar" in the recipient.  And that
 *  constitutes sufficient positive coverage, although we also want to make
 *  sure that just a single term match is insufficient.
 */
add_task(async function test_filter_text_multi_word() {
  let folder = await create_folder("QuickFilterBarTextMultiWord");

  let whoFoo = ["foo", "zabba@madeup.invalid"];
  let whoBar = ["zabba", "bar@madeup.invalid"];
  let [, setPeepMatch, setSubjReverse] = await make_message_sets_in_folders(
    [folder],
    [
      { count: 1 },
      { count: 1, from: whoFoo, to: [whoBar] },
      { count: 1, subject: "bar foo" },
      { count: 1, from: whoFoo },
    ]
  );
  await be_in_folder(folder);

  // (precondition)
  assert_text_constraints_checked("sender", "recipients", "subject");

  set_filter_text("foo bar");
  assert_messages_in_view([setPeepMatch, setSubjReverse]);
  teardownTest();
});

/**
 * Verify that the quickfilter bar has OR functionality using
 * | (Pipe character) - Bug 586131
 */
add_task(async function test_filter_or_operator() {
  let folder = await create_folder("QuickFilterBarOrOperator");

  let whoFoo = ["foo", "zabba@madeup.invalid"];
  let whoBar = ["zabba", "bar@madeup.invalid"];
  let whoTest = ["test", "test@madeup.invalid"];
  let [setInert, setSenderFoo, setToBar, , , setSubject3, setMail1] =
    await make_message_sets_in_folders(
      [folder],
      [
        { count: 1 },
        { count: 1, from: whoFoo },
        { count: 1, to: [whoBar] },
        { count: 1, subject: "foo bar" },
        { count: 1, subject: "bar test" },
        { count: 1, subject: "test" },
        { count: 1, to: [whoTest], subject: "logic" },
        { count: 1, from: whoFoo, to: [whoBar], subject: "test" },
      ]
    );
  await be_in_folder(folder);

  assert_text_constraints_checked("sender", "recipients", "subject");
  set_filter_text("foo | bar");
  assert_messages_not_in_view([setInert, setSubject3, setMail1]);

  set_filter_text("test | bar");
  assert_messages_not_in_view([setInert, setSenderFoo]);

  set_filter_text("foo | test");
  assert_messages_not_in_view([setInert, setToBar]);

  // consists of leading and trailing spaces and tab character.
  set_filter_text("test     |   foo bar");
  assert_messages_not_in_view([
    setInert,
    setSenderFoo,
    setToBar,
    setSubject3,
    setMail1,
  ]);

  set_filter_text("test | foo  bar |logic");
  assert_messages_not_in_view([setInert, setSenderFoo, setToBar, setSubject3]);
  teardownTest();
});

/**
 * Make sure that when dropping all constraints on toggle off or changing
 *  folders that we persist/propagate the state of the
 *  sender/recipients/subject/body toggle buttons.
 */
add_task(async function test_filter_text_constraints_propagate() {
  let whoFoo = ["foo", "zabba@madeup.invalid"];
  let whoBar = ["zabba", "bar@madeup.invalid"];

  let folderOne = await create_folder("QuickFilterBarTextPropagate1");
  let [setSubjFoo, setWhoFoo] = await make_message_sets_in_folders(
    [folderOne],
    [
      { count: 1, subject: "foo" },
      { count: 1, from: whoFoo },
    ]
  );
  let folderTwo = await create_folder("QuickFilterBarTextPropagate2");
  let [, setWhoBar] = await make_message_sets_in_folders(
    [folderTwo],
    [
      { count: 1, subject: "bar" },
      { count: 1, from: whoBar },
    ]
  );

  await be_in_folder(folderOne);
  set_filter_text("foo");
  // (precondition)
  assert_text_constraints_checked("sender", "recipients", "subject");
  assert_messages_in_view([setSubjFoo, setWhoFoo]);

  // -- drop subject, close bar to reset, make sure it sticks
  toggle_text_constraints("subject");
  assert_messages_in_view([setWhoFoo]);

  await toggle_quick_filter_bar();
  await toggle_quick_filter_bar();

  set_filter_text("foo");
  assert_messages_in_view([setWhoFoo]);
  assert_text_constraints_checked("sender", "recipients");

  // -- now change folders and make sure the settings stick
  await be_in_folder(folderTwo);
  set_filter_text("bar");
  assert_messages_in_view([setWhoBar]);
  assert_text_constraints_checked("sender", "recipients");
  teardownTest();
});

/**
 * Here is what the results label does:
 * - No filter active: results label is not visible.
 * - Filter active, messages: it says the number of messages.
 * - Filter active, no messages: it says there are no messages.
 *
 * Additional nuances:
 * - The count needs to update as the user deletes messages or what not.
 */
add_task(async function test_results_label() {
  let folder = await create_folder("QuickFilterBarResultsLabel");
  let [setImmortal, setMortal, setGoldfish] =
    await make_message_sets_in_folders(
      [folder],
      [{ count: 1 }, { count: 1 }, { count: 1 }]
    );

  await be_in_folder(folder);

  // no filter, the label should not be visible
  Assert.ok(
    BrowserTestUtils.is_hidden(
      get_about_3pane().document.getElementById("qfb-results-label")
    ),
    "results label should not be visible"
  );

  toggle_boolean_constraints("unread");
  assert_messages_in_view([setImmortal, setMortal, setGoldfish]);
  assert_results_label_count(3);

  await delete_messages(setGoldfish);
  assert_results_label_count(2);

  await delete_messages(setMortal);
  assert_results_label_count(1);

  await delete_messages(setImmortal);
  assert_results_label_count(0);
  teardownTest();
});

function teardownTest() {
  clear_constraints();
}