361 строка
14 KiB
JavaScript
361 строка
14 KiB
JavaScript
/* 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/. */
|
|
|
|
var { cal } = ChromeUtils.importESModule("resource:///modules/calendar/calUtils.sys.mjs");
|
|
const { CalAttendee } = ChromeUtils.importESModule("resource:///modules/CalAttendee.sys.mjs");
|
|
var { CalEvent } = ChromeUtils.importESModule("resource:///modules/CalEvent.sys.mjs");
|
|
var { CalItipMessageSender } = ChromeUtils.importESModule(
|
|
"resource:///modules/CalItipMessageSender.sys.mjs"
|
|
);
|
|
var { MailServices } = ChromeUtils.importESModule("resource:///modules/MailServices.sys.mjs");
|
|
|
|
var { CalendarTestUtils } = ChromeUtils.importESModule(
|
|
"resource://testing-common/calendar/CalendarTestUtils.sys.mjs"
|
|
);
|
|
|
|
const identityEmail = "user@example.com";
|
|
const eventOrganizerEmail = "eventorganizer@example.com";
|
|
|
|
/**
|
|
* Creates a calendar event mimicking an event to which we have received an
|
|
* invitation.
|
|
*
|
|
* @param {string} organizerEmail - The email address of the event organizer.
|
|
* @param {string} attendeeEmail - The email address of an attendee who has
|
|
* accepted the invitation.
|
|
* @returns {calIItemBase} - The new calendar event.
|
|
*/
|
|
function createIncomingEvent(organizerEmail, attendeeEmail) {
|
|
const organizerId = cal.email.prependMailTo(organizerEmail);
|
|
const attendeeId = cal.email.prependMailTo(attendeeEmail);
|
|
|
|
const icalString = CalendarTestUtils.dedent`
|
|
BEGIN:VEVENT
|
|
CREATED:20210105T000000Z
|
|
DTSTAMP:20210501T000000Z
|
|
UID:c1a6cfe7-7fbb-4bfb-a00d-861e07c649a5
|
|
SUMMARY:Test Invitation
|
|
DTSTART:20210105T000000Z
|
|
DTEND:20210105T100000Z
|
|
STATUS:CONFIRMED
|
|
SUMMARY:Test Event
|
|
ORGANIZER;CN=${organizerEmail}:${organizerId}
|
|
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;
|
|
RSVP=TRUE;CN=other@example.com;:mailto:other@example.com
|
|
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;
|
|
RSVP=TRUE;CN=${attendeeEmail};:${attendeeId}
|
|
X-MOZ-RECEIVED-SEQUENCE:0
|
|
X-MOZ-RECEIVED-DTSTAMP:20210501T000000Z
|
|
X-MOZ-GENERATION:0
|
|
END:VEVENT
|
|
`;
|
|
|
|
return new CalEvent(icalString);
|
|
}
|
|
|
|
let calendar;
|
|
|
|
/**
|
|
* Ensure the calendar manager is available, initialize the calendar and
|
|
* identity we use for testing.
|
|
*/
|
|
add_setup(async function () {
|
|
do_get_profile();
|
|
|
|
await new Promise(resolve => do_load_calmgr(resolve));
|
|
calendar = CalendarTestUtils.createCalendar("Test", "memory");
|
|
|
|
const identity = MailServices.accounts.createIdentity();
|
|
identity.email = identityEmail;
|
|
|
|
const account = MailServices.accounts.createAccount();
|
|
account.incomingServer = MailServices.accounts.createIncomingServer(
|
|
`${account.key}user`,
|
|
"localhost",
|
|
"none"
|
|
);
|
|
account.addIdentity(identity);
|
|
|
|
registerCleanupFunction(() => {
|
|
MailServices.accounts.removeIncomingServer(account.incomingServer, false);
|
|
MailServices.accounts.removeAccount(account);
|
|
});
|
|
|
|
calendar.setProperty("imip.identity.key", identity.key);
|
|
calendar.setProperty("organizerId", cal.email.prependMailTo(identityEmail));
|
|
});
|
|
|
|
add_task(async function testAddAttendeesToOwnEvent() {
|
|
const icalString = CalendarTestUtils.dedent`
|
|
BEGIN:VEVENT
|
|
CREATED:20210105T000000Z
|
|
DTSTAMP:20210501T000000Z
|
|
UID:c1a6cfe7-7fbb-4bfb-a00d-861e07c649a5
|
|
SUMMARY:Test Invitation
|
|
DTSTART:20210105T000000Z
|
|
DTEND:20210105T100000Z
|
|
STATUS:CONFIRMED
|
|
SUMMARY:Test Event
|
|
X-MOZ-SEND-INVITATIONS:TRUE
|
|
END:VEVENT
|
|
`;
|
|
|
|
const item = new CalEvent(icalString);
|
|
const savedItem = await calendar.addItem(item);
|
|
|
|
// Modify the event to include an attendee not in the original, as well as the
|
|
// organizer. As of the writing of this test, this is the expected behavior
|
|
// for adding an attendee to an event which previously had none.
|
|
const newAttendeeEmail = "foo@example.com";
|
|
const newAttendee = new CalAttendee();
|
|
newAttendee.id = newAttendeeEmail;
|
|
|
|
const organizer = new CalAttendee();
|
|
organizer.isOrganizer = true;
|
|
organizer.id = identityEmail;
|
|
|
|
const organizerAsAttendee = new CalAttendee();
|
|
organizerAsAttendee.id = identityEmail;
|
|
|
|
const targetItem = savedItem.clone();
|
|
targetItem.addAttendee(newAttendee);
|
|
targetItem.addAttendee(organizer);
|
|
targetItem.addAttendee(organizerAsAttendee);
|
|
const modifiedItem = await calendar.modifyItem(targetItem, savedItem);
|
|
|
|
// Test that a sender with an original item and for which the current user is
|
|
// both an attendee and the organizer will generate a REQUEST, but not send a
|
|
// message to the organizer.
|
|
const sender = new CalItipMessageSender(savedItem, null);
|
|
|
|
const result = sender.buildOutgoingMessages(Ci.calIOperationListener.MODIFY, modifiedItem);
|
|
Assert.equal(result, 1, "return value should indicate there are pending messages");
|
|
Assert.equal(sender.pendingMessageCount, 1, "there should be one pending message");
|
|
|
|
const [msg] = sender.pendingMessages;
|
|
Assert.equal(msg.method, "REQUEST", "message method should be 'REQUEST'");
|
|
Assert.equal(msg.recipients.length, 1, "message should have one recipient");
|
|
|
|
const [recipient] = msg.recipients;
|
|
Assert.equal(
|
|
recipient.id,
|
|
cal.email.prependMailTo(newAttendeeEmail),
|
|
"recipient should be the non-organizer attendee"
|
|
);
|
|
|
|
await calendar.deleteItem(modifiedItem);
|
|
|
|
// Now also cancel the event. No mail should be sent to self.
|
|
const targetItem2 = modifiedItem.clone();
|
|
|
|
targetItem2.setProperty("STATUS", "CANCELLED");
|
|
targetItem2.setProperty("SEQUENCE", "2");
|
|
const modifiedItem2 = await calendar.addItem(targetItem2);
|
|
const sender2 = new CalItipMessageSender(modifiedItem2, null);
|
|
|
|
const result2 = sender2.buildOutgoingMessages(Ci.calIOperationListener.MODIFY, modifiedItem2);
|
|
Assert.equal(result2, 1, "return value should indicate there are pending messages");
|
|
Assert.equal(sender2.pendingMessageCount, 1, "there should be one pending message");
|
|
|
|
const [msg2] = sender2.pendingMessages;
|
|
Assert.equal(msg2.method, "CANCEL", "deletion message method should be 'CANCEL'");
|
|
Assert.equal(msg2.recipients.length, 1, "deletion message should have one recipient");
|
|
|
|
const [recipient2] = msg2.recipients;
|
|
Assert.equal(
|
|
recipient2.id,
|
|
cal.email.prependMailTo(newAttendeeEmail),
|
|
"for deletion message, recipient should be the non-organizer attendee"
|
|
);
|
|
});
|
|
|
|
add_task(async function testAddAdditionalAttendee() {
|
|
const icalString = CalendarTestUtils.dedent`
|
|
BEGIN:VEVENT
|
|
CREATED:20210105T000000Z
|
|
DTSTAMP:20210501T000000Z
|
|
UID:c1a6cfe7-7fbb-4bfb-a00d-861e07c649a5
|
|
SUMMARY:Test Invitation
|
|
DTSTART:20210105T000000Z
|
|
DTEND:20210105T100000Z
|
|
STATUS:CONFIRMED
|
|
SUMMARY:Test Event
|
|
ORGANIZER;CN=${identityEmail}:${cal.email.prependMailTo(identityEmail)}
|
|
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;
|
|
RSVP=TRUE;CN=other@example.com;:mailto:other@example.com
|
|
ATTENDEE;CUTYPE=INDIVIDUAL;ROLE=REQ-PARTICIPANT;PARTSTAT=ACCEPTED;
|
|
RSVP=TRUE;CN=${identityEmail};:${cal.email.prependMailTo(identityEmail)}
|
|
X-MOZ-SEND-INVITATIONS:TRUE
|
|
END:VEVENT
|
|
`;
|
|
|
|
const item = new CalEvent(icalString);
|
|
const savedItem = await calendar.addItem(item);
|
|
|
|
// Modify the event to include an attendee not in the original.
|
|
const newAttendeeEmail = "bar@example.com";
|
|
const newAttendee = new CalAttendee();
|
|
newAttendee.id = newAttendeeEmail;
|
|
|
|
const organizer = new CalAttendee();
|
|
organizer.isOrganizer = true;
|
|
organizer.id = identityEmail;
|
|
|
|
const organizerAsAttendee = new CalAttendee();
|
|
organizerAsAttendee.id = identityEmail;
|
|
|
|
const targetItem = savedItem.clone();
|
|
targetItem.addAttendee(newAttendee);
|
|
const modifiedItem = await calendar.modifyItem(targetItem, savedItem);
|
|
|
|
// Test that adding an attendee won't cause messages to be sent to the
|
|
// existing attendees.
|
|
const sender = new CalItipMessageSender(savedItem, null);
|
|
|
|
const result = sender.buildOutgoingMessages(Ci.calIOperationListener.MODIFY, modifiedItem);
|
|
Assert.equal(result, 1, "return value should indicate there are pending messages");
|
|
Assert.equal(sender.pendingMessageCount, 1, "there should be one pending message");
|
|
|
|
const [msg] = sender.pendingMessages;
|
|
Assert.equal(msg.method, "REQUEST", "message method should be 'REQUEST'");
|
|
Assert.equal(msg.recipients.length, 1, "message should have one recipient");
|
|
|
|
const [recipient] = msg.recipients;
|
|
Assert.equal(
|
|
recipient.id,
|
|
cal.email.prependMailTo(newAttendeeEmail),
|
|
"recipient should be the new attendee"
|
|
);
|
|
|
|
await calendar.deleteItem(modifiedItem);
|
|
});
|
|
|
|
add_task(async function testInvitationReceived() {
|
|
const item = createIncomingEvent(eventOrganizerEmail, identityEmail);
|
|
const savedItem = await calendar.addItem(item);
|
|
|
|
const attendeeId = cal.email.prependMailTo(identityEmail);
|
|
|
|
// Test that a sender with no original item and for which the current user is
|
|
// an attendee but not the organizer (representing a new incoming invitation)
|
|
// generates a single pending REPLY message on ADD.
|
|
const currentUserAsAttendee = savedItem.getAttendeeById(attendeeId);
|
|
const sender = new CalItipMessageSender(null, currentUserAsAttendee);
|
|
|
|
const result = sender.buildOutgoingMessages(Ci.calIOperationListener.ADD, savedItem);
|
|
Assert.equal(result, 1, "return value should indicate there are pending messages");
|
|
Assert.equal(sender.pendingMessageCount, 1, "there should be one pending message");
|
|
|
|
const [msg] = sender.pendingMessages;
|
|
Assert.equal(msg.method, "REPLY", "message method should be 'REPLY'");
|
|
Assert.equal(msg.recipients.length, 1, "message should have one recipient");
|
|
|
|
const [recipient] = msg.recipients;
|
|
Assert.equal(
|
|
recipient.id,
|
|
cal.email.prependMailTo(eventOrganizerEmail),
|
|
"recipient should be the event organizer"
|
|
);
|
|
|
|
const attendeeList = msg.item.getAttendees();
|
|
Assert.equal(attendeeList.length, 1, "there should be one attendee listed in the message");
|
|
|
|
const [attendee] = attendeeList;
|
|
Assert.equal(attendee.id, attendeeId, "listed attendee should be the current user");
|
|
Assert.equal(
|
|
attendee.participationStatus,
|
|
"ACCEPTED",
|
|
"current user's participation status should be 'ACCEPTED'"
|
|
);
|
|
|
|
await calendar.deleteItem(savedItem);
|
|
});
|
|
|
|
add_task(async function testParticipationStatusUpdated() {
|
|
const item = createIncomingEvent(eventOrganizerEmail, identityEmail);
|
|
const savedItem = await calendar.addItem(item);
|
|
|
|
const attendeeId = cal.email.prependMailTo(identityEmail);
|
|
|
|
// Modify the event to update the user's participation status.
|
|
const targetItem = savedItem.clone();
|
|
const currentUserAsAttendee = targetItem.getAttendeeById(attendeeId);
|
|
currentUserAsAttendee.participationStatus = "TENTATIVE";
|
|
const modifiedItem = await calendar.modifyItem(targetItem, savedItem);
|
|
|
|
// Test that a sender for which the current user is an attendee but not the
|
|
// organizer will generate a pending REPLY message on MODIFY.
|
|
const sender = new CalItipMessageSender(savedItem, currentUserAsAttendee);
|
|
const result = sender.buildOutgoingMessages(Ci.calIOperationListener.MODIFY, modifiedItem);
|
|
|
|
Assert.equal(result, 1, "return value should indicate there are pending messages");
|
|
Assert.equal(sender.pendingMessageCount, 1, "there should be one pending message");
|
|
|
|
const [msg] = sender.pendingMessages;
|
|
Assert.equal(msg.method, "REPLY", "message method should be 'REPLY'");
|
|
Assert.equal(msg.recipients.length, 1, "message should have one recipient");
|
|
|
|
const [recipient] = msg.recipients;
|
|
Assert.equal(
|
|
recipient.id,
|
|
cal.email.prependMailTo(eventOrganizerEmail),
|
|
"recipient should be the event organizer"
|
|
);
|
|
|
|
const attendeeList = msg.item.getAttendees();
|
|
Assert.equal(attendeeList.length, 1, "there should be one attendee listed in the message");
|
|
|
|
const [attendee] = attendeeList;
|
|
Assert.equal(attendee.id, attendeeId, "listed attendee should be the current user");
|
|
Assert.equal(
|
|
attendee.participationStatus,
|
|
"TENTATIVE",
|
|
"current user's participation status should be 'TENTATIVE'"
|
|
);
|
|
|
|
await calendar.deleteItem(modifiedItem);
|
|
});
|
|
|
|
add_task(async function testEventDeleted() {
|
|
const item = createIncomingEvent(eventOrganizerEmail, identityEmail);
|
|
const savedItem = await calendar.addItem(item);
|
|
|
|
const attendeeId = cal.email.prependMailTo(identityEmail);
|
|
|
|
await calendar.deleteItem(savedItem);
|
|
const currentUserAsAttendee = savedItem.getAttendeeById(attendeeId);
|
|
|
|
// Test that a sender with no original item and for which the current user is
|
|
// an attendee but not the organizer (representing the user deleting an event
|
|
// from their calendar) generates a single REPLY message to the organizer on
|
|
// DELETE.
|
|
const sender = new CalItipMessageSender(null, currentUserAsAttendee);
|
|
const result = sender.buildOutgoingMessages(Ci.calIOperationListener.DELETE, savedItem);
|
|
|
|
Assert.equal(result, 1, "return value should indicate there are pending messages");
|
|
Assert.equal(sender.pendingMessageCount, 1, "there should be one pending message");
|
|
|
|
const [msg] = sender.pendingMessages;
|
|
Assert.equal(msg.method, "REPLY", "message method should be 'REPLY'");
|
|
Assert.equal(msg.recipients.length, 1, "message should have one recipient");
|
|
|
|
const [recipient] = msg.recipients;
|
|
Assert.equal(
|
|
recipient.id,
|
|
cal.email.prependMailTo(eventOrganizerEmail),
|
|
"recipient should be the event organizer"
|
|
);
|
|
|
|
const attendeeList = msg.item.getAttendees();
|
|
Assert.equal(attendeeList.length, 1, "there should be one attendee listed in the message");
|
|
|
|
const [attendee] = attendeeList;
|
|
Assert.equal(attendee.id, attendeeId, "listed attendee should be the current user");
|
|
Assert.equal(
|
|
attendee.participationStatus,
|
|
"DECLINED",
|
|
"current user's participation status should be 'DECLINED'"
|
|
);
|
|
});
|