summaryrefslogtreecommitdiffstats
path: root/comm/calendar/test/unit/providers
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/calendar/test/unit/providers
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/calendar/test/unit/providers')
-rw-r--r--comm/calendar/test/unit/providers/head.js152
-rw-r--r--comm/calendar/test/unit/providers/test_caldavCalendar_cached.js201
-rw-r--r--comm/calendar/test/unit/providers/test_caldavCalendar_uncached.js96
-rw-r--r--comm/calendar/test/unit/providers/test_icsCalendar_cached.js53
-rw-r--r--comm/calendar/test/unit/providers/test_icsCalendar_uncached.js46
-rw-r--r--comm/calendar/test/unit/providers/test_storageCalendar.js17
-rw-r--r--comm/calendar/test/unit/providers/xpcshell.ini11
7 files changed, 576 insertions, 0 deletions
diff --git a/comm/calendar/test/unit/providers/head.js b/comm/calendar/test/unit/providers/head.js
new file mode 100644
index 0000000000..3c995ab31a
--- /dev/null
+++ b/comm/calendar/test/unit/providers/head.js
@@ -0,0 +1,152 @@
+/* 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.import("resource:///modules/calendar/calUtils.jsm");
+var { CalendarTestUtils } = ChromeUtils.import(
+ "resource://testing-common/calendar/CalendarTestUtils.jsm"
+);
+var { CalEvent } = ChromeUtils.import("resource:///modules/CalEvent.jsm");
+var { PromiseUtils } = ChromeUtils.importESModule("resource://gre/modules/PromiseUtils.sys.mjs");
+
+var { updateAppInfo } = ChromeUtils.importESModule("resource://testing-common/AppInfo.sys.mjs");
+updateAppInfo();
+
+// The tests in this directory each do the same thing, with slight variations as needed for each
+// calendar provider. The core of the test lives in this file and the tests call it when ready.
+
+do_get_profile();
+add_setup(async () => {
+ await new Promise(resolve => cal.manager.startup({ onResult: resolve }));
+ await new Promise(resolve => cal.timezoneService.startup({ onResult: resolve }));
+ cal.manager.addCalendarObserver(calendarObserver);
+});
+
+let calendarObserver = {
+ QueryInterface: ChromeUtils.generateQI(["calIObserver"]),
+
+ /* calIObserver */
+
+ _batchCount: 0,
+ _batchRequired: true,
+ onStartBatch(calendar) {
+ info(`onStartBatch ${calendar?.id} ${++this._batchCount}`);
+ Assert.equal(calendar, this._expectedCalendar);
+ Assert.equal(this._batchCount, 1, "onStartBatch must not occur in a batch");
+ },
+ onEndBatch(calendar) {
+ info(`onEndBatch ${calendar?.id} ${this._batchCount--}`);
+ Assert.equal(calendar, this._expectedCalendar);
+ Assert.equal(this._batchCount, 0, "onEndBatch must occur in a batch");
+ },
+ onLoad(calendar) {
+ info(`onLoad ${calendar.id}`);
+ Assert.equal(this._batchCount, 0, "onLoad must not occur in a batch");
+ Assert.equal(calendar, this._expectedCalendar);
+ if (this._onLoadPromise) {
+ this._onLoadPromise.resolve();
+ }
+ },
+ onAddItem(item) {
+ info(`onAddItem ${item.calendar.id} ${item.id}`);
+ if (this._batchRequired) {
+ Assert.equal(this._batchCount, 1, "onAddItem must occur in a batch");
+ }
+ if (this._onAddItemPromise) {
+ this._onAddItemPromise.resolve();
+ }
+ },
+ onModifyItem(newItem, oldItem) {
+ info(`onModifyItem ${newItem.calendar.id} ${newItem.id}`);
+ if (this._batchRequired) {
+ Assert.equal(this._batchCount, 1, "onModifyItem must occur in a batch");
+ }
+ if (this._onModifyItemPromise) {
+ this._onModifyItemPromise.resolve();
+ }
+ },
+ onDeleteItem(deletedItem) {
+ info(`onDeleteItem ${deletedItem.calendar.id} ${deletedItem.id}`);
+ if (this._onDeleteItemPromise) {
+ this._onDeleteItemPromise.resolve();
+ }
+ },
+ onError(calendar, errNo, message) {},
+ onPropertyChanged(calendar, name, value, oldValue) {},
+ onPropertyDeleting(calendar, name) {},
+};
+
+/**
+ * Create and register a calendar.
+ *
+ * @param {string} type - The calendar provider to use.
+ * @param {string} url - URL of the server.
+ * @param {boolean} useCache - Should this calendar have offline storage?
+ * @returns {calICalendar}
+ */
+function createCalendar(type, url, useCache) {
+ let calendar = cal.manager.createCalendar(type, Services.io.newURI(url));
+ calendar.name = type + (useCache ? " with cache" : " without cache");
+ calendar.id = cal.getUUID();
+ calendar.setProperty("cache.enabled", useCache);
+
+ cal.manager.registerCalendar(calendar);
+ calendar = cal.manager.getCalendarById(calendar.id);
+ calendarObserver._expectedCalendar = calendar;
+
+ info(`Created calendar ${calendar.id}`);
+ return calendar;
+}
+
+/**
+ * Creates an event and adds it to the given calendar.
+ *
+ * @param {calICalendar} calendar
+ * @returns {calIEvent}
+ */
+async function runAddItem(calendar) {
+ let event = new CalEvent();
+ event.id = "6b7dd6f6-d6f0-4e93-a953-bb5473c4c47a";
+ event.title = "New event";
+ event.startDate = cal.createDateTime("20200303T205500Z");
+ event.endDate = cal.createDateTime("20200303T210200Z");
+
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onModifyItemPromise = PromiseUtils.defer();
+ await calendar.addItem(event);
+ await Promise.any([
+ calendarObserver._onAddItemPromise.promise,
+ calendarObserver._onModifyItemPromise.promise,
+ ]);
+
+ return event;
+}
+
+/**
+ * Modifies the event from runAddItem.
+ *
+ * @param {calICalendar} calendar
+ */
+async function runModifyItem(calendar) {
+ let event = await calendar.getItem("6b7dd6f6-d6f0-4e93-a953-bb5473c4c47a");
+
+ let clone = event.clone();
+ clone.title = "Modified event";
+
+ calendarObserver._onModifyItemPromise = PromiseUtils.defer();
+ await calendar.modifyItem(clone, event);
+ await calendarObserver._onModifyItemPromise.promise;
+}
+
+/**
+ * Deletes the event from runAddItem.
+ *
+ * @param {calICalendar} calendar
+ */
+async function runDeleteItem(calendar) {
+ let event = await calendar.getItem("6b7dd6f6-d6f0-4e93-a953-bb5473c4c47a");
+
+ calendarObserver._onDeleteItemPromise = PromiseUtils.defer();
+ await calendar.deleteItem(event);
+ await calendarObserver._onDeleteItemPromise.promise;
+}
diff --git a/comm/calendar/test/unit/providers/test_caldavCalendar_cached.js b/comm/calendar/test/unit/providers/test_caldavCalendar_cached.js
new file mode 100644
index 0000000000..7a61973644
--- /dev/null
+++ b/comm/calendar/test/unit/providers/test_caldavCalendar_cached.js
@@ -0,0 +1,201 @@
+/* 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 { CalDAVServer } = ChromeUtils.import("resource://testing-common/calendar/CalDAVServer.jsm");
+
+add_setup(async function () {
+ CalDAVServer.open();
+ await CalDAVServer.putItemInternal(
+ "5a9fa76c-93f3-4ad8-9f00-9e52aedd2821.ics",
+ CalendarTestUtils.dedent`
+ BEGIN:VCALENDAR
+ BEGIN:VEVENT
+ UID:5a9fa76c-93f3-4ad8-9f00-9e52aedd2821
+ SUMMARY:exists before time
+ DTSTART:20210401T120000Z
+ DTEND:20210401T130000Z
+ END:VEVENT
+ END:VCALENDAR
+ `
+ );
+});
+registerCleanupFunction(() => CalDAVServer.close());
+
+add_task(async function () {
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ let calendar = createCalendar("caldav", CalDAVServer.url, true);
+ await calendarObserver._onAddItemPromise.promise;
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"));
+
+ info("creating the item");
+ calendarObserver._batchRequired = true;
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runAddItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("modifying the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runModifyItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("deleting the item");
+ await runDeleteItem(calendar);
+
+ cal.manager.unregisterCalendar(calendar);
+});
+
+/**
+ * Tests calendars that return status 404 for "current-user-privilege-set" are
+ * not flagged read-only.
+ */
+add_task(async function testCalendarWithNoPrivSupport() {
+ CalDAVServer.privileges = null;
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+
+ let calendar = createCalendar("caldav", CalDAVServer.url, true);
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(!calendar.readOnly, "calendar was not marked read-only");
+
+ cal.manager.unregisterCalendar(calendar);
+});
+
+/**
+ * Tests modifyItem() does not hang when the server reports no actual
+ * modifications were made.
+ */
+add_task(async function testModifyItemWithNoChanges() {
+ let event = new CalEvent();
+ let calendar = createCalendar("caldav", CalDAVServer.url, false);
+ event.id = "6f6dd7b6-0fbd-39e4-359a-a74c4c3745bb";
+ event.title = "A New Event";
+ event.startDate = cal.createDateTime("20200303T205500Z");
+ event.endDate = cal.createDateTime("20200303T210200Z");
+ await calendar.addItem(event);
+
+ let clone = event.clone();
+ clone.title = "A Modified Event";
+
+ let putItemInternal = CalDAVServer.putItemInternal;
+ CalDAVServer.putItemInternal = () => {};
+
+ let modifiedEvent = await calendar.modifyItem(clone, event);
+ CalDAVServer.putItemInternal = putItemInternal;
+
+ Assert.ok(modifiedEvent, "an event was returned");
+ Assert.equal(modifiedEvent.title, event.title, "the un-modified event is returned");
+
+ await calendar.deleteItem(modifiedEvent);
+ cal.manager.unregisterCalendar(calendar);
+});
+
+/**
+ * Tests that an error response from the server when syncing doesn't delete
+ * items from the local calendar.
+ */
+add_task(async function testSyncError1() {
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ let calendar = createCalendar("caldav", CalDAVServer.url, true);
+ await calendarObserver._onAddItemPromise.promise;
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(
+ await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"),
+ "item should exist when first connected"
+ );
+
+ info("syncing with rate limit error");
+ CalDAVServer.throwRateLimitErrors = true;
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ calendar.refresh();
+ await calendarObserver._onLoadPromise.promise;
+ CalDAVServer.throwRateLimitErrors = false;
+ info("sync with rate limit error complete");
+
+ Assert.equal(
+ calendar.getProperty("currentStatus"),
+ Cr.NS_OK,
+ "calendar should not be in an error state"
+ );
+ Assert.equal(calendar.getProperty("disabled"), null, "calendar should not be disabled");
+ Assert.ok(
+ await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"),
+ "item should still exist after error response"
+ );
+
+ info("syncing without rate limit error");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ calendar.refresh();
+ await calendarObserver._onLoadPromise.promise;
+ info("sync without rate limit error complete");
+
+ Assert.ok(
+ await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"),
+ "item should still exist after successful sync"
+ );
+
+ cal.manager.unregisterCalendar(calendar);
+});
+
+/**
+ * Tests that multiple pages of item responses from the server when syncing
+ * doesn't result in items being deleted from the local calendar.
+ *
+ * The server has a page size of 3, although this test should pass regardless
+ * of the page size.
+ */
+add_task(async function testSyncError2() {
+ // Add some items to the server so multiple requests are required to get
+ // them all. There's already one item on the server.
+ for (let i = 0; i < 3; i++) {
+ await CalDAVServer.putItemInternal(
+ `fake-uid-${i}.ics`,
+ CalendarTestUtils.dedent`
+ BEGIN:VCALENDAR
+ BEGIN:VEVENT
+ UID:fake-uid-${i}
+ SUMMARY:event ${i}
+ DTSTART:20210401T120000Z
+ DTEND:20210401T130000Z
+ END:VEVENT
+ END:VCALENDAR
+ `
+ );
+ }
+
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ let calendar = createCalendar("caldav", CalDAVServer.url, true);
+ await calendarObserver._onAddItemPromise.promise;
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ let items = await calendar.getItemsAsArray(Ci.calICalendar.ITEM_FILTER_TYPE_ALL, 0, null, null);
+ Assert.equal(items.length, 4, "all items added to calendar when first connected");
+
+ info("forced syncing with multiple pages");
+ calendar.wrappedJSObject.mUncachedCalendar.wrappedJSObject.mWebdavSyncToken = null;
+ calendar.wrappedJSObject.mUncachedCalendar.wrappedJSObject.saveCalendarProperties();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ calendar.refresh();
+ await calendarObserver._onLoadPromise.promise;
+ info("forced sync with multiple pages complete");
+
+ items = await calendar.getItemsAsArray(Ci.calICalendar.ITEM_FILTER_TYPE_ALL, 0, null, null);
+ Assert.equal(items.length, 4, "all items still in calendar after forced refresh");
+
+ cal.manager.unregisterCalendar(calendar);
+
+ // Delete the added items.
+ for (let i = 0; i < 3; i++) {
+ CalDAVServer.deleteItemInternal(`fake-uid-${i}.ics`);
+ }
+});
diff --git a/comm/calendar/test/unit/providers/test_caldavCalendar_uncached.js b/comm/calendar/test/unit/providers/test_caldavCalendar_uncached.js
new file mode 100644
index 0000000000..025a1ba871
--- /dev/null
+++ b/comm/calendar/test/unit/providers/test_caldavCalendar_uncached.js
@@ -0,0 +1,96 @@
+/* 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 { CalDAVServer } = ChromeUtils.import("resource://testing-common/calendar/CalDAVServer.jsm");
+
+add_setup(async function () {
+ CalDAVServer.open();
+ await CalDAVServer.putItemInternal(
+ "5a9fa76c-93f3-4ad8-9f00-9e52aedd2821.ics",
+ CalendarTestUtils.dedent`
+ BEGIN:VCALENDAR
+ BEGIN:VEVENT
+ UID:5a9fa76c-93f3-4ad8-9f00-9e52aedd2821
+ SUMMARY:exists before time
+ DTSTART:20210401T120000Z
+ DTEND:20210401T130000Z
+ END:VEVENT
+ END:VCALENDAR
+ `
+ );
+});
+registerCleanupFunction(() => CalDAVServer.close());
+
+add_task(async function () {
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ let calendar = createCalendar("caldav", CalDAVServer.url, false);
+ await calendarObserver._onAddItemPromise.promise;
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"));
+
+ info("creating the item");
+ calendarObserver._batchRequired = true;
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runAddItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("modifying the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runModifyItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("deleting the item");
+ await runDeleteItem(calendar);
+
+ cal.manager.unregisterCalendar(calendar);
+});
+
+/**
+ * Tests calendars that return status 404 for "current-user-privilege-set" are
+ * not flagged read-only.
+ */
+add_task(async function testCalendarWithNoPrivSupport() {
+ CalDAVServer.privileges = null;
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+
+ let calendar = createCalendar("caldav", CalDAVServer.url, false);
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(!calendar.readOnly, "calendar was not marked read-only");
+
+ cal.manager.unregisterCalendar(calendar);
+});
+
+/**
+ * Tests modifyItem() does not hang when the server reports no actual
+ * modifications were made.
+ */
+add_task(async function testModifyItemWithNoChanges() {
+ let event = new CalEvent();
+ let calendar = createCalendar("caldav", CalDAVServer.url, false);
+ event.id = "6f6dd7b6-0fbd-39e4-359a-a74c4c3745bb";
+ event.title = "A New Event";
+ event.startDate = cal.createDateTime("20200303T205500Z");
+ event.endDate = cal.createDateTime("20200303T210200Z");
+ await calendar.addItem(event);
+
+ let clone = event.clone();
+ clone.title = "A Modified Event";
+
+ let putItemInternal = CalDAVServer.putItemInternal;
+ CalDAVServer.putItemInternal = () => {};
+
+ let modifiedEvent = await calendar.modifyItem(clone, event);
+ CalDAVServer.putItemInternal = putItemInternal;
+
+ Assert.ok(modifiedEvent, "an event was returned");
+ Assert.equal(modifiedEvent.title, event.title, "the un-modified event is returned");
+
+ await calendar.deleteItem(modifiedEvent);
+ cal.manager.unregisterCalendar(calendar);
+});
diff --git a/comm/calendar/test/unit/providers/test_icsCalendar_cached.js b/comm/calendar/test/unit/providers/test_icsCalendar_cached.js
new file mode 100644
index 0000000000..9bc1b127da
--- /dev/null
+++ b/comm/calendar/test/unit/providers/test_icsCalendar_cached.js
@@ -0,0 +1,53 @@
+/* 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 { ICSServer } = ChromeUtils.import("resource://testing-common/calendar/ICSServer.jsm");
+
+ICSServer.open();
+ICSServer.putICSInternal(
+ CalendarTestUtils.dedent`
+ BEGIN:VCALENDAR
+ BEGIN:VEVENT
+ UID:5a9fa76c-93f3-4ad8-9f00-9e52aedd2821
+ SUMMARY:exists before time
+ DTSTART:20210401T120000Z
+ DTEND:20210401T130000Z
+ END:VEVENT
+ END:VCALENDAR
+ `
+);
+registerCleanupFunction(() => ICSServer.close());
+
+add_task(async function () {
+ // TODO: item notifications from a cached ICS calendar occur outside of batches.
+ // This isn't fatal but it shouldn't happen. Side-effects include alarms firing
+ // twice - once from onAddItem then again at onLoad.
+ //
+ // Remove the next line when this is fixed.
+ calendarObserver._batchRequired = false;
+
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ let calendar = createCalendar("ics", ICSServer.url, true);
+ await calendarObserver._onAddItemPromise.promise;
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"));
+
+ info("creating the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runAddItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("modifying the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runModifyItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("deleting the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runDeleteItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+});
diff --git a/comm/calendar/test/unit/providers/test_icsCalendar_uncached.js b/comm/calendar/test/unit/providers/test_icsCalendar_uncached.js
new file mode 100644
index 0000000000..db5db6e99f
--- /dev/null
+++ b/comm/calendar/test/unit/providers/test_icsCalendar_uncached.js
@@ -0,0 +1,46 @@
+/* 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 { ICSServer } = ChromeUtils.import("resource://testing-common/calendar/ICSServer.jsm");
+
+ICSServer.open();
+ICSServer.putICSInternal(
+ CalendarTestUtils.dedent`
+ BEGIN:VCALENDAR
+ BEGIN:VEVENT
+ UID:5a9fa76c-93f3-4ad8-9f00-9e52aedd2821
+ SUMMARY:exists before time
+ DTSTART:20210401T120000Z
+ DTEND:20210401T130000Z
+ END:VEVENT
+ END:VCALENDAR
+ `
+);
+registerCleanupFunction(() => ICSServer.close());
+
+add_task(async function () {
+ calendarObserver._onAddItemPromise = PromiseUtils.defer();
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ let calendar = createCalendar("ics", ICSServer.url, false);
+ await calendarObserver._onAddItemPromise.promise;
+ await calendarObserver._onLoadPromise.promise;
+ info("calendar set-up complete");
+
+ Assert.ok(await calendar.getItem("5a9fa76c-93f3-4ad8-9f00-9e52aedd2821"));
+
+ info("creating the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runAddItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("modifying the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runModifyItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+
+ info("deleting the item");
+ calendarObserver._onLoadPromise = PromiseUtils.defer();
+ await runDeleteItem(calendar);
+ await calendarObserver._onLoadPromise.promise;
+});
diff --git a/comm/calendar/test/unit/providers/test_storageCalendar.js b/comm/calendar/test/unit/providers/test_storageCalendar.js
new file mode 100644
index 0000000000..778f9f9251
--- /dev/null
+++ b/comm/calendar/test/unit/providers/test_storageCalendar.js
@@ -0,0 +1,17 @@
+/* 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/. */
+
+add_task(async function () {
+ let calendar = createCalendar("storage", "moz-storage-calendar://");
+
+ info("creating the item");
+ calendarObserver._batchRequired = false;
+ await runAddItem(calendar);
+
+ info("modifying the item");
+ await runModifyItem(calendar);
+
+ info("deleting the item");
+ await runDeleteItem(calendar);
+});
diff --git a/comm/calendar/test/unit/providers/xpcshell.ini b/comm/calendar/test/unit/providers/xpcshell.ini
new file mode 100644
index 0000000000..272fef7c78
--- /dev/null
+++ b/comm/calendar/test/unit/providers/xpcshell.ini
@@ -0,0 +1,11 @@
+[default]
+head = head.js
+prefs =
+ calendar.timezone.local=UTC
+ calendar.timezone.useSystemTimezone=false
+
+[test_caldavCalendar_cached.js]
+[test_caldavCalendar_uncached.js]
+[test_icsCalendar_cached.js]
+[test_icsCalendar_uncached.js]
+[test_storageCalendar.js]