diff options
Diffstat (limited to '')
-rw-r--r-- | comm/calendar/test/unit/test_alarm.js | 674 |
1 files changed, 674 insertions, 0 deletions
diff --git a/comm/calendar/test/unit/test_alarm.js b/comm/calendar/test/unit/test_alarm.js new file mode 100644 index 0000000000..14b2ce76bd --- /dev/null +++ b/comm/calendar/test/unit/test_alarm.js @@ -0,0 +1,674 @@ +/* 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 { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs"); + +XPCOMUtils.defineLazyModuleGetters(this, { + CalAlarm: "resource:///modules/CalAlarm.jsm", + CalAttachment: "resource:///modules/CalAttachment.jsm", + CalAttendee: "resource:///modules/CalAttendee.jsm", + CalTodo: "resource:///modules/CalTodo.jsm", +}); + +function run_tests() { + test_initial_creation(); + + test_display_alarm(); + test_email_alarm(); + test_audio_alarm(); + test_custom_alarm(); + test_repeat(); + test_xprop(); + + test_dates(); + + test_clone(); + test_immutable(); + test_serialize(); + test_strings(); +} + +function run_test() { + do_calendar_startup(run_tests); +} + +function test_initial_creation() { + dump("Testing initial creation..."); + let alarm = new CalAlarm(); + + let passed; + try { + // eslint-disable-next-line no-unused-expressions + alarm.icalString; + passed = false; + } catch (e) { + passed = true; + } + if (!passed) { + do_throw("Fresh calIAlarm should not produce a valid icalString"); + } + dump("Done\n"); +} + +function test_display_alarm() { + dump("Testing DISPLAY alarms..."); + let alarm = new CalAlarm(); + // Set ACTION to DISPLAY, make sure this was not rejected + alarm.action = "DISPLAY"; + equal(alarm.action, "DISPLAY"); + + // Set a Description, REQUIRED for ACTION:DISPLAY + alarm.description = "test"; + equal(alarm.description, "test"); + + // SUMMARY is not valid for ACTION:DISPLAY + alarm.summary = "test"; + equal(alarm.summary, null); + + // No attendees allowed + let attendee = new CalAttendee(); + attendee.id = "mailto:horst"; + + throws(() => { + // DISPLAY alarm should not be able to save attendees + alarm.addAttendee(attendee); + }, /Alarm type AUDIO\/DISPLAY may not have attendees/); + + throws(() => { + // DISPLAY alarm should not be able to save attachment + alarm.addAttachment(new CalAttachment()); + }, /Alarm type DISPLAY may not have attachments/); + + dump("Done\n"); +} + +function test_email_alarm() { + dump("Testing EMAIL alarms..."); + let alarm = new CalAlarm(); + // Set ACTION to DISPLAY, make sure this was not rejected + alarm.action = "EMAIL"; + equal(alarm.action, "EMAIL"); + + // Set a Description, REQUIRED for ACTION:EMAIL + alarm.description = "description"; + equal(alarm.description, "description"); + + // Set a Summary, REQUIRED for ACTION:EMAIL + alarm.summary = "summary"; + equal(alarm.summary, "summary"); + + // Set an offset of some sort + alarm.related = Ci.calIAlarm.ALARM_RELATED_START; + alarm.offset = cal.createDuration(); + + // Check for at least one attendee + let attendee1 = new CalAttendee(); + attendee1.id = "mailto:horst"; + let attendee2 = new CalAttendee(); + attendee2.id = "mailto:gustav"; + + equal(alarm.getAttendees().length, 0); + alarm.addAttendee(attendee1); + equal(alarm.getAttendees().length, 1); + alarm.addAttendee(attendee2); + equal(alarm.getAttendees().length, 2); + alarm.addAttendee(attendee1); + let addedAttendees = alarm.getAttendees(); + equal(addedAttendees.length, 2); + equal(addedAttendees[0].wrappedJSObject, attendee2); + equal(addedAttendees[1].wrappedJSObject, attendee1); + + ok(!!alarm.icalComponent.serializeToICS().match(/mailto:horst/)); + ok(!!alarm.icalComponent.serializeToICS().match(/mailto:gustav/)); + + alarm.deleteAttendee(attendee1); + equal(alarm.getAttendees().length, 1); + + alarm.clearAttendees(); + equal(alarm.getAttendees().length, 0); + + // Make sure attendees are correctly folded/imported + alarm.icalString = dedent` + BEGIN:VALARM + ACTION:EMAIL + TRIGGER;VALUE=DURATION:-PT5M + ATTENDEE:mailto:test@example.com + ATTENDEE:mailto:test@example.com + ATTENDEE:mailto:test2@example.com + END:VALARM + `; + equal(alarm.icalString.match(/ATTENDEE:mailto:test@example.com/g).length, 1); + equal(alarm.icalString.match(/ATTENDEE:mailto:test2@example.com/g).length, 1); + + // TODO test attachments + dump("Done\n"); +} + +function test_audio_alarm() { + dump("Testing AUDIO alarms..."); + let alarm = new CalAlarm(); + alarm.related = Ci.calIAlarm.ALARM_RELATED_ABSOLUTE; + alarm.alarmDate = cal.createDateTime(); + // Set ACTION to AUDIO, make sure this was not rejected + alarm.action = "AUDIO"; + equal(alarm.action, "AUDIO"); + + // No Description for ACTION:AUDIO + alarm.description = "description"; + equal(alarm.description, null); + + // No Summary, for ACTION:AUDIO + alarm.summary = "summary"; + equal(alarm.summary, null); + + // No attendees allowed + let attendee = new CalAttendee(); + attendee.id = "mailto:horst"; + + try { + alarm.addAttendee(attendee); + do_throw("AUDIO alarm should not be able to save attendees"); + } catch (e) { + // TODO looks like this test is disabled. Why? + } + + // Test attachments + let sound = new CalAttachment(); + sound.uri = Services.io.newURI("file:///sound.wav"); + let sound2 = new CalAttachment(); + sound2.uri = Services.io.newURI("file:///sound2.wav"); + + // Adding an attachment should work + alarm.addAttachment(sound); + let addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 1); + equal(addedAttachments[0].wrappedJSObject, sound); + ok(alarm.icalString.includes("ATTACH:file:///sound.wav")); + + // Adding twice shouldn't change anything + alarm.addAttachment(sound); + addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 1); + equal(addedAttachments[0].wrappedJSObject, sound); + + try { + alarm.addAttachment(sound2); + do_throw("Adding a second attachment should fail for type AUDIO"); + } catch (e) { + // TODO looks like this test is disabled. Why? + } + + // Deleting should work + alarm.deleteAttachment(sound); + addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 0); + + // As well as clearing + alarm.addAttachment(sound); + alarm.clearAttachments(); + addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 0); + + // AUDIO alarms should only be allowing one attachment, and folding any with the same value + alarm.icalString = dedent` + BEGIN:VALARM + ACTION:AUDIO + TRIGGER;VALUE=DURATION:-PT5M + ATTACH:Basso + UID:28F8007B-FE56-442E-917C-1F4E48DD406A + X-APPLE-DEFAULT-ALARM:TRUE + ATTACH:Basso + END:VALARM + `; + equal(alarm.icalString.match(/ATTACH:Basso/g).length, 1); + + dump("Done\n"); +} + +function test_custom_alarm() { + dump("Testing X-SMS (custom) alarms..."); + let alarm = new CalAlarm(); + // Set ACTION to a custom value, make sure this was not rejected + alarm.action = "X-SMS"; + equal(alarm.action, "X-SMS"); + + // There is no restriction on DESCRIPTION for custom alarms + alarm.description = "description"; + equal(alarm.description, "description"); + + // There is no restriction on SUMMARY for custom alarms + alarm.summary = "summary"; + equal(alarm.summary, "summary"); + + // Test for attendees + let attendee1 = new CalAttendee(); + attendee1.id = "mailto:horst"; + let attendee2 = new CalAttendee(); + attendee2.id = "mailto:gustav"; + + equal(alarm.getAttendees().length, 0); + alarm.addAttendee(attendee1); + equal(alarm.getAttendees().length, 1); + alarm.addAttendee(attendee2); + equal(alarm.getAttendees().length, 2); + alarm.addAttendee(attendee1); + equal(alarm.getAttendees().length, 2); + + alarm.deleteAttendee(attendee1); + equal(alarm.getAttendees().length, 1); + + alarm.clearAttendees(); + equal(alarm.getAttendees().length, 0); + + // Test for attachments + let attach1 = new CalAttachment(); + attach1.uri = Services.io.newURI("file:///example.txt"); + let attach2 = new CalAttachment(); + attach2.uri = Services.io.newURI("file:///example2.txt"); + + alarm.addAttachment(attach1); + alarm.addAttachment(attach2); + + let addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 2); + equal(addedAttachments[0].wrappedJSObject, attach1); + equal(addedAttachments[1].wrappedJSObject, attach2); + + alarm.deleteAttachment(attach1); + addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 1); + + alarm.clearAttachments(); + addedAttachments = alarm.getAttachments(); + equal(addedAttachments.length, 0); +} + +// Check if any combination of REPEAT and DURATION work as expected. +function test_repeat() { + dump("Testing REPEAT and DURATION properties..."); + let alarm = new CalAlarm(); + + // Check initial value + equal(alarm.repeat, 0); + equal(alarm.repeatOffset, null); + equal(alarm.repeatDate, null); + + // Should not be able to get REPEAT when DURATION is not set + alarm.repeat = 1; + equal(alarm.repeat, 0); + + // Both REPEAT and DURATION should be accessible, when the two are set. + alarm.repeatOffset = cal.createDuration(); + notEqual(alarm.repeatOffset, null); + notEqual(alarm.repeat, 0); + + // Should not be able to get DURATION when REPEAT is not set + alarm.repeat = null; + equal(alarm.repeatOffset, null); + + // Should be able to unset alarm DURATION attribute. (REPEAT already tested above) + try { + alarm.repeatOffset = null; + } catch (e) { + do_throw("Could not set repeatOffset attribute to null" + e); + } + + // Check unset value + equal(alarm.repeat, 0); + equal(alarm.repeatOffset, null); + + // Check repeatDate + alarm = new CalAlarm(); + alarm.related = Ci.calIAlarm.ALARM_RELATED_ABSOLUTE; + alarm.alarmDate = cal.createDateTime(); + alarm.repeat = 1; + alarm.repeatOffset = cal.createDuration(); + alarm.repeatOffset.inSeconds = 3600; + + let date = alarm.alarmDate.clone(); + date.second += 3600; + equal(alarm.repeatDate.icalString, date.icalString); + + dump("Done\n"); +} + +function test_xprop() { + dump("Testing X-Props..."); + let alarm = new CalAlarm(); + alarm.setProperty("X-PROP", "X-VALUE"); + ok(alarm.hasProperty("X-PROP")); + equal(alarm.getProperty("X-PROP"), "X-VALUE"); + alarm.deleteProperty("X-PROP"); + ok(!alarm.hasProperty("X-PROP")); + equal(alarm.getProperty("X-PROP"), null); + + // also check X-MOZ-LASTACK prop + let date = cal.createDateTime(); + alarm.setProperty("X-MOZ-LASTACK", date.icalString); + alarm.action = "DISPLAY"; + alarm.description = "test"; + alarm.related = Ci.calIAlarm.ALARM_RELATED_START; + alarm.offset = cal.createDuration("-PT5M"); + ok(alarm.icalComponent.serializeToICS().includes(date.icalString)); + + alarm.deleteProperty("X-MOZ-LASTACK"); + ok(!alarm.icalComponent.serializeToICS().includes(date.icalString)); + dump("Done\n"); +} + +function test_dates() { + dump("Testing alarm dates..."); + let passed; + // Initial value + let alarm = new CalAlarm(); + equal(alarm.alarmDate, null); + equal(alarm.offset, null); + + // Set an offset and check it + alarm.related = Ci.calIAlarm.ALARM_RELATED_START; + let offset = cal.createDuration("-PT5M"); + alarm.offset = offset; + equal(alarm.alarmDate, null); + equal(alarm.offset, offset); + try { + alarm.alarmDate = cal.createDateTime(); + passed = false; + } catch (e) { + passed = true; + } + if (!passed) { + do_throw("Setting alarmDate when alarm is relative should not succeed"); + } + + // Set an absolute time and check it + alarm.related = Ci.calIAlarm.ALARM_RELATED_ABSOLUTE; + let alarmDate = createDate(2007, 0, 1, true, 2, 0, 0); + alarm.alarmDate = alarmDate; + equal(alarm.alarmDate.icalString, alarmDate.icalString); + equal(alarm.offset, null); + try { + alarm.offset = cal.createDuration(); + passed = false; + } catch (e) { + passed = true; + } + if (!passed) { + do_throw("Setting offset when alarm is absolute should not succeed"); + } + + dump("Done\n"); +} + +var propMap = { + related: Ci.calIAlarm.ALARM_RELATED_START, + repeat: 1, + action: "X-TEST", + description: "description", + summary: "summary", + offset: cal.createDuration("PT4M"), + repeatOffset: cal.createDuration("PT1M"), +}; +var clonePropMap = { + related: Ci.calIAlarm.ALARM_RELATED_END, + repeat: 2, + action: "X-CHANGED", + description: "description-changed", + summary: "summary-changed", + offset: cal.createDuration("PT5M"), + repeatOffset: cal.createDuration("PT2M"), +}; + +function test_immutable() { + dump("Testing immutable alarms..."); + let alarm = new CalAlarm(); + // Set up each attribute + for (let prop in propMap) { + alarm[prop] = propMap[prop]; + } + + // Set up some extra props + alarm.setProperty("X-FOO", "X-VAL"); + alarm.setProperty("X-DATEPROP", cal.createDateTime()); + alarm.addAttendee(new CalAttendee()); + + // Initial checks + ok(alarm.isMutable); + alarm.makeImmutable(); + ok(!alarm.isMutable); + alarm.makeImmutable(); + ok(!alarm.isMutable); + + // Check each attribute + for (let prop in propMap) { + try { + alarm[prop] = propMap[prop]; + } catch (e) { + equal(e.result, Cr.NS_ERROR_OBJECT_IS_IMMUTABLE); + continue; + } + do_throw("Attribute " + prop + " was writable while item was immutable"); + } + + // Functions + throws(() => { + alarm.setProperty("X-FOO", "changed"); + }, /Can not modify immutable data container/); + + throws(() => { + alarm.deleteProperty("X-FOO"); + }, /Can not modify immutable data container/); + + ok(!alarm.getProperty("X-DATEPROP").isMutable); + + dump("Done\n"); +} + +function test_clone() { + dump("Testing cloning alarms..."); + let alarm = new CalAlarm(); + // Set up each attribute + for (let prop in propMap) { + alarm[prop] = propMap[prop]; + } + + // Set up some extra props + alarm.setProperty("X-FOO", "X-VAL"); + alarm.setProperty("X-DATEPROP", cal.createDateTime()); + alarm.addAttendee(new CalAttendee()); + + // Make a copy + let newAlarm = alarm.clone(); + newAlarm.makeImmutable(); + newAlarm = newAlarm.clone(); + ok(newAlarm.isMutable); + + // Check if item is still the same + // TODO This is not quite optimal, maybe someone can find a better way to do + // the comparisons. + for (let prop in propMap) { + if (prop == "item") { + equal(alarm.item.icalString, newAlarm.item.icalString); + } else { + try { + alarm[prop].QueryInterface(Ci.nsISupports); + equal(alarm[prop].icalString, newAlarm[prop].icalString); + } catch { + equal(alarm[prop], newAlarm[prop]); + } + } + } + + // Check if changes on the cloned object do not affect the original object. + for (let prop in clonePropMap) { + newAlarm[prop] = clonePropMap[prop]; + dump("Checking " + prop + "..."); + notEqual(alarm[prop], newAlarm[prop]); + dump("OK!\n"); + break; + } + + // Check x props + alarm.setProperty("X-FOO", "BAR"); + equal(alarm.getProperty("X-FOO"), "BAR"); + let date = alarm.getProperty("X-DATEPROP"); + equal(date.isMutable, true); + + // Test xprop params + alarm.icalString = + "BEGIN:VALARM\n" + + "ACTION:DISPLAY\n" + + "TRIGGER:-PT15M\n" + + "X-FOO;X-PARAM=PARAMVAL:BAR\n" + + "DESCRIPTION:TEST\n" + + "END:VALARM"; + + newAlarm = alarm.clone(); + equal(alarm.icalString, newAlarm.icalString); + + dump("Done\n"); +} + +function test_serialize() { + // most checks done by other tests, these don't fit into categories + let alarm = new CalAlarm(); + + throws( + () => { + alarm.icalComponent = cal.icsService.createIcalComponent("BARF"); + }, + /0x80070057/, + "Invalid Argument" + ); + + function addProp(name, value) { + let prop = cal.icsService.createIcalProperty(name); + prop.value = value; + comp.addProperty(prop); + } + function addActionDisplay() { + addProp("ACTION", "DISPLAY"); + } + function addActionEmail() { + addProp("ACTION", "EMAIL"); + } + function addTrigger() { + addProp("TRIGGER", "-PT15M"); + } + function addDescr() { + addProp("DESCRIPTION", "TEST"); + } + function addDuration() { + addProp("DURATION", "-PT15M"); + } + function addRepeat() { + addProp("REPEAT", "1"); + } + function addAttendee() { + addProp("ATTENDEE", "mailto:horst"); + } + function addAttachment() { + addProp("ATTACH", "data:yeah"); + } + + // All is there, should not throw + let comp = cal.icsService.createIcalComponent("VALARM"); + addActionDisplay(); + addTrigger(); + addDescr(); + addDuration(); + addRepeat(); + alarm.icalComponent = comp; + alarm.toString(); + + // Attachments and attendees + comp = cal.icsService.createIcalComponent("VALARM"); + addActionEmail(); + addTrigger(); + addDescr(); + addAttendee(); + addAttachment(); + alarm.icalComponent = comp; + alarm.toString(); + + // Missing action + throws( + () => { + comp = cal.icsService.createIcalComponent("VALARM"); + addTrigger(); + addDescr(); + alarm.icalComponent = comp; + }, + /Illegal value/, + "Invalid Argument" + ); + + // Missing trigger + throws( + () => { + comp = cal.icsService.createIcalComponent("VALARM"); + addActionDisplay(); + addDescr(); + alarm.icalComponent = comp; + }, + /Illegal value/, + "Invalid Argument" + ); + + // Missing duration with repeat + throws( + () => { + comp = cal.icsService.createIcalComponent("VALARM"); + addActionDisplay(); + addTrigger(); + addDescr(); + addRepeat(); + alarm.icalComponent = comp; + }, + /Illegal value/, + "Invalid Argument" + ); + + // Missing repeat with duration + throws( + () => { + comp = cal.icsService.createIcalComponent("VALARM"); + addActionDisplay(); + addTrigger(); + addDescr(); + addDuration(); + alarm.icalComponent = comp; + }, + /Illegal value/, + "Invalid Argument" + ); +} + +function test_strings() { + // Serializing the string shouldn't throw, but we don't really care about + // the string itself. + let alarm = new CalAlarm(); + alarm.action = "DISPLAY"; + alarm.related = Ci.calIAlarm.ALARM_RELATED_ABSOLUTE; + alarm.alarmDate = cal.createDateTime(); + alarm.toString(); + + alarm.related = Ci.calIAlarm.ALARM_RELATED_START; + alarm.offset = cal.createDuration(); + alarm.toString(); + alarm.toString(new CalTodo()); + + alarm.related = Ci.calIAlarm.ALARM_RELATED_END; + alarm.offset = cal.createDuration(); + alarm.toString(); + alarm.toString(new CalTodo()); + + alarm.offset = cal.createDuration("P1D"); + alarm.toString(); + + alarm.offset = cal.createDuration("PT1H"); + alarm.toString(); + + alarm.offset = cal.createDuration("-PT1H"); + alarm.toString(); +} |