Bug 1759590 - Add tests for repeating and non-repeating events. r=aleca

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

--HG--
extra : amend_source : 90b8abd539e1bda8ff4e04fc97fecb11de9e1ffd
This commit is contained in:
Lasana Murray 2022-04-20 11:30:46 +02:00
Родитель 872ecc309d
Коммит 5fd7226493
8 изменённых файлов: 834 добавлений и 5 удалений

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

@ -255,7 +255,7 @@
<toolbarbutton is="toolbarbutton-menu-button" id="imipDeclineRecurrencesButton"
tooltiptext="&lightning.imipbar.btnDeclineRecurrences2.tooltiptext;"
label="&lightning.imipbar.btnDeclineRecurrences.label;"
oncommand="calImipBar.executeAction('DECLINED');"
oncommand="calImipBar.executeAction('DECLINED', 'AUTO');"
type="menu-button"
class="toolbarbutton-1 message-header-view-button imipDeclineRecurrencesButton"
hidden="true">
@ -264,7 +264,7 @@
tooltiptext="&lightning.imipbar.btnSendSeries.tooltiptext;"
label="&lightning.imipbar.btnSend.label;"
oncommand="calImipBar.executeAction('DECLINED'); event.stopPropagation();"/>
<menuitem id="imipDeclineRecurrencesButton_DeclineAllDontSend"
<menuitem id="imipDeclineRecurrencesButton_DeclineDontSend"
tooltiptext="&lightning.imipbar.btnDontSendSeries.tooltiptext;"
label="&lightning.imipbar.btnDontSend.label;"
oncommand="calImipBar.executeAction('DECLINED', 'NONE'); event.stopPropagation();"/>

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

@ -256,8 +256,7 @@ class CalItipEmailTransport {
// "@mozilla.org/messengercompose/composesendlistener;1"
// and/or "chrome://messenger/content/messengercompose/sendProgress.xhtml"
// i.e. bug 432662
let msgSend = Cc["@mozilla.org/messengercompose/send;1"].createInstance(Ci.nsIMsgSend);
msgSend.sendMessageFile(
this.getMsgSend().sendMessageFile(
identity,
account.key,
composeFields,
@ -366,6 +365,14 @@ class CalItipEmailTransport {
}
}
/**
* Provides a new nsIMsgSend instance to use when sending the message. This
* method can be overridden in child classes for testing or other purposes.
*/
getMsgSend() {
return Cc["@mozilla.org/messengercompose/send;1"].createInstance(Ci.nsIMsgSend);
}
/**
* Provides the identity and account to use when sending iTIP emails. By
* default prefers whatever the item's calendar is configured to use or the

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

@ -1,5 +1,5 @@
[default]
head = ../head.js
head = ../head.js head.js
prefs =
calendar.item.promptDelete=false
calendar.timezone.local=UTC
@ -16,4 +16,6 @@ support-files = data/**
[browser_icsAttachment.js]
skip-if = os == 'win'
[browser_identityPrompt.js]
[browser_imipBar.js]
[browser_imipBarEmail.js]
[browser_imipBarRepeat.js]

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

@ -0,0 +1,204 @@
/* 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 for receiving event invitations via the imip-bar.
*/
"use strict";
var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
var { CalItipDefaultEmailTransport } = ChromeUtils.import(
"resource:///modules/CalItipEmailTransport.jsm"
);
var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
var { open_message_from_file } = ChromeUtils.import(
"resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
var { CalendarTestUtils } = ChromeUtils.import(
"resource://testing-common/calendar/CalendarTestUtils.jsm"
);
let deleteMgr = Cc["@mozilla.org/calendar/deleted-items-manager;1"].getService(Ci.calIDeletedItems)
.wrappedJSObject;
let account;
let identity;
let calendar;
let transport;
let getImipTransport;
let markDeleted;
/**
* Initialize account, identity and calendar.
*/
add_setup(async function() {
account = MailServices.accounts.createAccount();
account.incomingServer = MailServices.accounts.createIncomingServer(
"receiver",
"example.com",
"imap"
);
identity = MailServices.accounts.createIdentity();
identity.email = "receiver@example.com";
account.addIdentity(identity);
await CalendarTestUtils.setCalendarView(window, "month");
window.goToDate(cal.createDateTime("20220316T191602Z"));
calendar = CalendarTestUtils.createCalendar("Test");
transport = new EmailTransport(account, identity);
getImipTransport = cal.itip.getImipTransport;
cal.itip.getImipTransport = () => transport;
markDeleted = deleteMgr.markDeleted;
deleteMgr.markDeleted = () => {};
registerCleanupFunction(() => {
MailServices.accounts.removeAccount(account, true);
cal.itip.getImipTransport = getImipTransport;
deleteMgr.markDeleted = markDeleted;
CalendarTestUtils.removeCalendar(calendar);
});
});
/**
* Tests accepting an invitation and sending a response.
*/
add_task(async function testAcceptWithResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/single-event.eml")));
clickAction(win, "imipAcceptButton");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
partStat: "ACCEPTED",
},
event
);
await calendar.deleteItem(event);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests tentatively accepting an invitation and sending a response.
*/
add_task(async function testTentativeWithResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/single-event.eml")));
clickAction(win, "imipTentativeButton");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
partStat: "TENTATIVE",
},
event
);
await calendar.deleteItem(event);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests declining an invitation and sending a response.
*/
add_task(async function testDeclineWithResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/single-event.eml")));
clickAction(win, "imipDeclineButton");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
partStat: "DECLINED",
},
event
);
await calendar.deleteItem(event);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests accepting an invitation without sending a response.
*/
add_task(async function testAcceptWithoutResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/single-event.eml")));
await clickMenuAction(win, "imipAcceptButton", "imipAcceptButton_AcceptDontSend");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
partStat: "ACCEPTED",
noReply: true,
},
event
);
await calendar.deleteItem(event);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests tentatively accepting an invitation without sending a response.
*/
add_task(async function testTentativeWithoutResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/single-event.eml")));
await clickMenuAction(win, "imipTentativeButton", "imipTentativeButton_TentativeDontSend");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
partStat: "TENTATIVE",
noReply: true,
},
event
);
await calendar.deleteItem(event);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests declining an invitation without sending a response.
*/
add_task(async function testDeclineWithoutResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/single-event.eml")));
await clickMenuAction(win, "imipDeclineButton", "imipDeclineButton_DeclineDontSend");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
partStat: "DECLINED",
noReply: true,
},
event
);
await calendar.deleteItem(event);
await BrowserTestUtils.closeWindow(win);
});

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

@ -0,0 +1,229 @@
/* 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 for receiving recurring event invitations via the imip-bar.
*/
"use strict";
var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
var { CalItipDefaultEmailTransport } = ChromeUtils.import(
"resource:///modules/CalItipEmailTransport.jsm"
);
var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
var { open_message_from_file } = ChromeUtils.import(
"resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
var { CalendarTestUtils } = ChromeUtils.import(
"resource://testing-common/calendar/CalendarTestUtils.jsm"
);
let deleteMgr = Cc["@mozilla.org/calendar/deleted-items-manager;1"].getService(Ci.calIDeletedItems)
.wrappedJSObject;
let account;
let identity;
let calendar;
let transport;
let getImipTransport = cal.itip.getImipTransport;
let markDeleted = deleteMgr.markDeleted;
let startRange = cal.createDateTime("19990101");
let endRange = cal.createDateTime("20400101");
/**
* Initialize account, identity and calendar.
*/
add_setup(async function() {
account = MailServices.accounts.createAccount();
account.incomingServer = MailServices.accounts.createIncomingServer(
"receiver",
"example.com",
"imap"
);
identity = MailServices.accounts.createIdentity();
identity.email = "receiver@example.com";
account.addIdentity(identity);
await CalendarTestUtils.setCalendarView(window, "month");
window.goToDate(cal.createDateTime("20220316T191602Z"));
calendar = CalendarTestUtils.createCalendar("Test");
transport = new EmailTransport(account, identity);
getImipTransport = cal.itip.getImipTransport;
cal.itip.getImipTransport = () => transport;
markDeleted = deleteMgr.markDeleted;
deleteMgr.markDeleted = () => {};
registerCleanupFunction(() => {
MailServices.accounts.removeAccount(account, true);
cal.itip.getImipTransport = getImipTransport;
deleteMgr.markDeleted = markDeleted;
CalendarTestUtils.removeCalendar(calendar);
});
});
/**
* Tests accepting an invitation to a recurring event and sending a response.
*/
add_task(async function testAcceptRecurringWithResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/repeat-event.eml")));
clickAction(win, "imipAcceptRecurrencesButton");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
isRecurring: true,
partStat: "ACCEPTED",
},
event
);
await calendar.deleteItem(event.parentItem);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests tentatively accepting an invitation to a recurring event and sending a
* response.
*/
add_task(async function testTentativeRecurringWithResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/repeat-event.eml")));
clickAction(win, "imipTentativeRecurrencesButton");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
isRecurring: true,
partStat: "TENTATIVE",
},
event
);
await calendar.deleteItem(event.parentItem);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests declining an invitation to a recurring event and sending a response.
*/
add_task(async function testDeclineRecurringWithResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/repeat-event.eml")));
clickAction(win, "imipDeclineRecurrencesButton");
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
isRecurring: true,
partStat: "DECLINED",
},
event
);
await calendar.deleteItem(event.parentItem);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests accepting an invitation to a recurring event without sending a response.
*/
add_task(async function testAcceptRecurringWithoutResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/repeat-event.eml")));
await clickMenuAction(
win,
"imipAcceptRecurrencesButton",
"imipAcceptRecurrencesButton_AcceptDontSend"
);
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
isRecurring: true,
partStat: "ACCEPTED",
noReply: true,
},
event
);
await calendar.deleteItem(event.parentItem);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests tentatively accepting an invitation to a recurring event without sending
* a response.
*/
add_task(async function testTentativeRecurringWithoutResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/repeat-event.eml")));
await clickMenuAction(
win,
"imipTentativeRecurrencesButton",
"imipTentativeRecurrencesButton_TentativeDontSend"
);
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
isRecurring: true,
partStat: "TENTATIVE",
noReply: true,
},
event
);
await calendar.deleteItem(event.parentItem);
await BrowserTestUtils.closeWindow(win);
});
/**
* Tests declining an invitation to a recurring event without sending a response.
*/
add_task(async function testDeclineRecurrencesWithoutResponse() {
transport.reset();
let win = await openImipMessage(new FileUtils.File(getTestFilePath("data/repeat-event.eml")));
await clickMenuAction(
win,
"imipDeclineRecurrencesButton",
"imipDeclineRecurrencesButton_DeclineDontSend"
);
let event = (await CalendarTestUtils.monthView.waitForItemAt(window, 3, 4, 1)).item;
await doImipBarActionTest(
{
calendar,
transport,
identity,
isRecurring: true,
partStat: "DECLINED",
noReply: true,
},
event
);
await calendar.deleteItem(event.parentItem);
await BrowserTestUtils.closeWindow(win);
});

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

@ -0,0 +1,49 @@
MIME-Version: 1.0
Date: Mon, 28 Mar 2022 17:49:35 +0000
Subject: Invitation: Repeat Event @ Daily from 2pm to 3pm 3 times (AST) (receiver@example.com)
From: sender@example.com
To: receiver@example.com
Content-Type: multipart/mixed; boundary="00000000000080f3db05db4aef5b"
--00000000000080f3db05db4aef5b
Content-Type: multipart/alternative; boundary="00000000000080f3da05db4aef59"
--00000000000080f3da05db4aef59
Content-Type: text/calendar; charset="UTF-8"; method=REQUEST
Content-Transfer-Encoding: 7bit
BEGIN:VCALENDAR
METHOD:REQUEST
BEGIN:VEVENT
DTSTART:20220316T110000Z
DTEND:20220316T113000Z
RRULE:FREQ=DAILY;WKST=SU;COUNT=3;INTERVAL=1
DTSTAMP:20220316T191602Z
UID:02e79b96
ORGANIZER;CN=Sender;
EMAIL=sender@example.com:mailto:sender@example.com
ATTENDEE;CN=Sender;
EMAIL=sender@example.com;CUTYPE=INDIVIDUAL;
PARTSTAT=ACCEPTED;RSVP=FALSE:mailto:sender@example.com
ATTENDEE;CN=Receiver;EMAIL=receiver@example.com;CUTYPE=INDIVIDUAL;
PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:receiver@example.com
ATTENDEE;CN=Other;EMAIL=other@example.com;CUTYPE=INDIVIDUAL;
PARTSTAT=NEEDS-ACTION;RSVP=TRUE:mailto:other@example.com
CREATED:20220328T174934Z
LAST-MODIFIED:20220328T174934Z
LOCATION:Somewhere
SEQUENCE:0
STATUS:CONFIRMED
SUMMARY:Repeat Event
DESCRIPTION:An event invitation.
TRANSP:OPAQUE
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:-P1D
DESCRIPTION:This is an event reminder
END:VALARM
END:VEVENT
END:VCALENDAR
--00000000000080f3da05db4aef59--
--00000000000080f3db05db4aef5b--

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

@ -0,0 +1,78 @@
MIME-Version: 1.0
Content-Transfer-Encoding: binary
Content-Type: multipart/mixed; boundary="_----------=_1647458162153312762582"
Date: Wed, 16 Mar 2022 15:16:02 -0400
To: receiver@example.com
Subject: Invitation: Single Event @ Wed, Mar 16 2022 11:00 AST
From: Sender <sender@example.com>
This is a multi-part message in MIME format.
--_----------=_1647458162153312762582
MIME-Version: 1.0
Content-Transfer-Encoding: binary
Content-Type: multipart/alternative; boundary="_----------=_1647458162153312762583"
Date: Wed, 16 Mar 2022 15:16:02 -0400
This is a multi-part message in MIME format.
--_----------=_1647458162153312762583
MIME-Version: 1.0
Content-Disposition: inline
Content-Length: 227
Content-Transfer-Encoding: binary
Content-Type: text/plain; charset="utf-8"
Date: Wed, 16 Mar 2022 15:16:02 -0400
Single Event
When:
Wed, Mar 16 2022
11:00 - 12:00 AST
Where:
Somewhere
--_----------=_1647458162153312762583
MIME-Version: 1.0
Content-Disposition: inline
Content-Transfer-Encoding: quoted-printable
Content-Type: text/calendar; charset="utf-8"; method="REQUEST"
Date: Wed, 16 Mar 2022 15:16:02 -0400
BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:b0c0b197
SEQUENCE:0
DTSTAMP:20220316T191602Z
CREATED:20220316T191532Z
DTSTART:20220316T110000Z
DTEND:20220316T113000Z
DURATION:PT1H
PRIORITY:0
SUMMARY:Single Event
DESCRIPTION:An event invitation.
LOCATION:Somewhere
STATUS:CONFIRMED
TRANSP:OPAQUE
CLASS:PUBLIC
ORGANIZER;CN=3DSender;
EMAIL=3Dsender@example.com:mailto:sender@example.com
ATTENDEE;CN=3DSender;
EMAIL=3Dsender@example.com;CUTYPE=3DINDIVIDUAL;
PARTSTAT=3DACCEPTED;RSVP=3DFALSE:mailto:sender@example.com
ATTENDEE;CN=Receiver;EMAIL=3Dreceiver@example.com;CUTYPE=3DINDIVIDUAL;
PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailto:receiver@example.com
ATTENDEE;CN=Other;EMAIL=other@example.com;CUTYPE=3DINDIVIDUAL;
PARTSTAT=3DNEEDS-ACTION;RSVP=3DTRUE:mailto:other@example.com
BEGIN:VALARM
ACTION:DISPLAY
TRIGGER:-P1D
DESCRIPTION:This is an event reminder
END:VALARM
END:VEVENT
END:VCALENDAR
--_----------=_1647458162153312762583--

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

@ -0,0 +1,260 @@
/* 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/. */
/**
* Common functions for the imip-bar tests.
*
* Note that these tests are heavily tied to the properties of single-event.eml
* and repeat-event.eml.
*/
"use strict";
var { cal } = ChromeUtils.import("resource:///modules/calendar/calUtils.jsm");
var { CalItipDefaultEmailTransport } = ChromeUtils.import(
"resource:///modules/CalItipEmailTransport.jsm"
);
var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
var { MailServices } = ChromeUtils.import("resource:///modules/MailServices.jsm");
var { open_message_from_file } = ChromeUtils.import(
"resource://testing-common/mozmill/FolderDisplayHelpers.jsm"
);
var { CalendarTestUtils } = ChromeUtils.import(
"resource://testing-common/calendar/CalendarTestUtils.jsm"
);
class EmailTransport extends CalItipDefaultEmailTransport {
sentItems = [];
sentMsgs = [];
getMsgSend() {
let { sentMsgs } = this;
return {
sendMessageFile(
userIdentity,
accountKey,
composeFields,
messageFile,
deleteSendFileOnCompletion,
digest,
deliverMode,
msgToReplace,
listener,
statusFeedback,
smtpPassword
) {
sentMsgs.push({
userIdentity,
accountKey,
composeFields,
messageFile,
deleteSendFileOnCompletion,
digest,
deliverMode,
msgToReplace,
listener,
statusFeedback,
smtpPassword,
});
},
};
}
sendItems(recipients, itipItem, fromAttendee) {
this.sentItems.push({ recipients, itipItem, fromAttendee });
return super.sendItems(recipients, itipItem, fromAttendee);
}
reset() {
this.sentItems = [];
this.sentMsgs = [];
}
}
/**
* Opens an iMIP message file and waits for the imip-bar to appear.
*
* @param {nsIFile} file
* @return {Window}
*/
async function openImipMessage(file) {
let { window: win } = await open_message_from_file(file);
let imipBar = win.document.getElementById("imip-bar");
await TestUtils.waitForCondition(() => !imipBar.collapsed, "imip-bar shown");
return win;
}
/**
* Clicks on one of the imip-bar action buttons.
*
* @param {Window} win
* @param {string} id
*/
function clickAction(win, id) {
let action = win.document.getElementById(id);
Assert.ok(!action.hidden, `"#${id}" shown"`);
EventUtils.synthesizeMouseAtCenter(action, {}, win);
}
/**
* Clicks on one of the imip-bar actions from a dropdown menu.
*
* @param {Window} win The window the imip message is opened in.
* @param {string} buttonId The id of the <toolbarbutton> containing the menu.
* @param {string} actionId The id of the menu item to click.
*/
async function clickMenuAction(win, buttonId, actionId) {
let actionButton = win.document.getElementById(buttonId);
Assert.ok(!actionButton.hidden, `"${buttonId}" shown`);
let actionMenu = actionButton.querySelector("menupopup");
let menuShown = BrowserTestUtils.waitForEvent(actionMenu, "popupshown");
EventUtils.synthesizeMouseAtCenter(actionButton.querySelector("dropmarker"), {}, win);
await menuShown;
EventUtils.synthesizeMouseAtCenter(win.document.getElementById(actionId), {}, win);
}
const unpromotedProps = ["location", "description", "sequence", "x-moz-received-dtstamp"];
/**
* An object where the keys are paths and the values the values they lead to
* in an object we want to test for correctness.
* @typedef {Object} Comparable
*/
/**
* Compares the paths specified in the expected object against the provided
* actual object.
*
* @param {object} actual This is expected to be a calIEvent or calIAttendee but
* can also be an array of both etc.
* @param {Comparable} expected
*/
function compareProperties(actual, expected, prefix = "") {
Assert.equal(typeof actual, "object", `${prefix || "provided value"} is an object`);
for (let [key, value] of Object.entries(expected)) {
if (key.includes(".")) {
let keys = key.split(".");
let head = keys[0];
let tail = keys.slice(1).join(".");
compareProperties(actual[head], { [tail]: value }, [prefix, head].filter(k => k).join("."));
continue;
}
let path = [prefix, key].filter(k => k).join(".");
let actualValue = unpromotedProps.includes(key) ? actual.getProperty(key) : actual[key];
Assert.equal(actualValue, value, `property "${path}" is "${value}"`);
}
}
/**
* @typedef {Object} ImipBarActionTestConf
*
* @property {calICalendar} calendar The calendar used for the test.
* @property {calIItipTranport} transport The transport used for the test.
* @property {nsIIdentity} identity The identity expected to be used to
* send the reply.
* @property {string} partStat The participationStatus of the receiving user to
* expect.
* @property {boolean} noReply If true, do not expect an attempt to send a reply.
*/
/**
* Test the properties of an event created from the imip-bar and optionally, the
* attempt to send a reply.
*
* @param {ImipBarActionTestConf} conf
* @param {calIEvent|calIEvent[]} item
*/
async function doImipBarActionTest(conf, event) {
let { calendar, transport, identity, partStat, isRecurring, noReply } = conf;
let title = isRecurring ? "Repeat Event" : "Single Event";
let events = [event];
let startDates = ["20220316T110000Z"];
let endDates = ["20220316T113000Z"];
if (isRecurring) {
startDates = [...startDates, "20220317T110000Z", "20220318T110000Z"];
endDates = [...endDates, "20220317T113000Z", "20220318T113000Z"];
events = event.parentItem.recurrenceInfo.getOccurrences(
cal.createDateTime("19700101"),
cal.createDateTime("30000101"),
Infinity
);
Assert.equal(events.length, 3, "reccurring event has 3 occurrences");
}
info("Verifying relevant properties of each event occurrence");
for (let [index, occurrence] of events.entries()) {
compareProperties(occurrence, {
title,
"calendar.name": calendar.name,
"startDate.icalString": startDates[index],
"endDate.icalString": endDates[index],
description: "An event invitation.",
location: "Somewhere",
sequence: "0",
"x-moz-received-dtstamp": "20220316T191602Z",
"organizer.id": "mailto:sender@example.com",
status: "CONFIRMED",
});
// Alarms should be ignored.
Assert.equal(
occurrence.getAlarms().length,
0,
`${isRecurring ? "occurrence" : "event"} has no reminders`
);
info("Verifying attendee list and participation status");
let attendees = occurrence.getAttendees();
compareProperties(attendees, {
"0.id": "mailto:sender@example.com",
"0.participationStatus": "ACCEPTED",
"1.participationStatus": partStat,
"1.id": "mailto:receiver@example.com",
"2.id": "mailto:other@example.com",
"2.participationStatus": "NEEDS-ACTION",
});
}
if (noReply) {
Assert.equal(
transport.sentItems.length,
0,
"itip subsystem did not attempt to send a response"
);
Assert.equal(transport.sentMsgs.length, 0, "no call was made into the mail subsystem");
} else {
info("Verifying the attempt to send a response uses the correct data");
Assert.equal(transport.sentItems.length, 1, "itip subsystem attempted to send a response");
compareProperties(transport.sentItems[0], {
"recipients.0.id": "mailto:sender@example.com",
"itipItem.responseMethod": "REPLY",
"fromAttendee.id": "mailto:receiver@example.com",
"fromAttendee.participationStatus": partStat,
});
// The itipItem is used to generate the iTIP data in the message body.
info("Verifying the reply calItipItem attendee list");
let replyItem = transport.sentItems[0].itipItem.getItemList()[0];
let replyAttendees = replyItem.getAttendees();
Assert.equal(replyAttendees.length, 1, "reply has one attendee");
compareProperties(replyAttendees[0], {
id: "mailto:receiver@example.com",
participationStatus: partStat,
});
info("Verifying the call to the message subsystem");
Assert.equal(transport.sentMsgs.length, 1, "transport sent 1 message");
compareProperties(transport.sentMsgs[0], {
userIdentity: identity,
"composeFields.from": "receiver@example.com",
"composeFields.to": "Sender <sender@example.com>",
});
Assert.ok(transport.sentMsgs[0].messageFile.exists(), "message file was created");
}
}