Revision control
Copy as Markdown
Other Tools
/* 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
/**
* Tests that a calendar keeps its place in the calendar list when it is
* re-created (toggling its offline support) or re-registered (a provider being
* enabled/disabled), instead of dropping to the bottom of the list
*/
var { CalReadableStreamFactory } = ChromeUtils.importESModule(
);
/**
* Returns the calendar ids in the order they appear in the calendar list.
*
* @returns {string[]} - Calendar ids, top to bottom.
*/
function listedIds() {
const list = document.getElementById("calendar-list");
return [...list.rows].map(row => row.getAttribute("calendar-id"));
}
/**
* Returns the persisted sort order from the calendar.list.sortOrder pref.
*
* @returns {string[]} - Calendar ids in the persisted order.
*/
function savedOrder() {
return Services.prefs.getStringPref("calendar.list.sortOrder", "").split(" ").filter(Boolean);
}
/**
* Creates and registers a memory calendar.
*
* @param {string} name - Name for the new calendar.
* @returns {calICalendar} - The registered calendar.
*/
function registerMemory(name) {
const calendar = cal.manager.createCalendar(
"memory",
Services.io.newURI("moz-memory-calendar://")
);
calendar.name = name;
calendar.id = cal.getUUID();
cal.manager.registerCalendar(calendar);
return cal.manager.getCalendarById(calendar.id);
}
/**
* Removes all registered calendars with one of the given names.
*
* @param {string[]} names - Names of the calendars to remove.
*/
function removeByName(names) {
for (const calendar of cal.manager.getCalendars()) {
if (names.includes(calendar.name)) {
cal.manager.removeCalendar(calendar);
}
}
}
/**
* Creates a minimal dynamic calendar provider class, so enabling/disabling it
* swaps the calendar for a dummy - the path a provider add-on uses.
*
* @param {string} providerType - Value for the calendars' type property.
* @returns {Function} - The provider's calendar implementation class.
*/
function makeProvider(providerType) {
/** A calendar of the dynamically registered provider. */
return class CalendarProvider extends cal.provider.BaseClass {
QueryInterface = ChromeUtils.generateQI(["calICalendar"]);
type = providerType;
constructor() {
super();
this.initProviderBase();
}
getItems() {
return CalReadableStreamFactory.createEmptyReadableStream();
}
};
}
add_setup(async function () {
await CalendarTestUtils.openCalendarTab(window);
});
/**
* Tests that toggling a calendar's offline support, which re-creates it under a
* new id, keeps it in place in the list.
*/
add_task(async function testCacheToggleKeepsListPosition() {
const a = registerMemory("SortA");
const b = registerMemory("SortB");
const c = registerMemory("SortC");
registerCleanupFunction(() => removeByName(["SortA", "SortB", "SortC"]));
await TestUtils.waitForCondition(() => listedIds().includes(c.id), "calendars listed");
const bIndex = listedIds().indexOf(b.id);
Assert.greater(bIndex, 0, "B is listed in the middle");
// Toggle offline support: B is re-created under a new id.
b.setProperty("cache.enabled", true);
const newB = await TestUtils.waitForCondition(
() => cal.manager.getCalendars().find(x => x.name == "SortB" && x.id != b.id),
"B was re-created under a new id"
);
await TestUtils.waitForCondition(
() => listedIds().includes(newB.id),
"the re-created B is listed"
);
const ids = listedIds();
Assert.equal(ids.indexOf(newB.id), bIndex, "the re-created calendar keeps its list position");
Assert.ok(!ids.includes(b.id), "the old id no longer lingers in the list");
Assert.equal(ids.indexOf(a.id), bIndex - 1, "A stays before it");
Assert.equal(ids.indexOf(c.id), bIndex + 1, "C stays after it");
Assert.equal(ids.filter(id => id == newB.id).length, 1, "the re-created calendar is listed once");
Assert.deepEqual(savedOrder(), ids, "the persisted sort order matches the list");
});
/**
* Tests that disabling and re-enabling a provider, which swaps its calendar for
* a dummy and back, keeps the calendar in place in the list.
*/
add_task(async function testProviderSwapKeepsListPosition() {
const CalendarProvider = makeProvider("blm");
cal.manager.registerCalendarProvider("blm", CalendarProvider);
registerCleanupFunction(() => {
if (cal.manager.hasCalendarProvider("blm")) {
cal.manager.unregisterCalendarProvider("blm");
}
removeByName(["SwapA", "SwapMid", "SwapC"]);
});
registerMemory("SwapA");
mid.name = "SwapMid";
mid.id = cal.getUUID();
cal.manager.registerCalendar(mid);
registerMemory("SwapC");
await TestUtils.waitForCondition(
() => listedIds().includes(mid.id),
"the provider calendar is listed"
);
const midIndex = listedIds().indexOf(mid.id);
Assert.greater(midIndex, 0, "the provider calendar is in the middle");
// Disable the provider: the calendar is swapped for a dummy (same id).
cal.manager.unregisterCalendarProvider("blm");
await TestUtils.waitForCondition(
() => listedIds().includes(mid.id),
"the swapped calendar is still listed after disabling"
);
Assert.equal(
listedIds().indexOf(mid.id),
midIndex,
"the calendar keeps its position when the provider is disabled"
);
Assert.equal(
listedIds().filter(id => id == mid.id).length,
1,
"the calendar is listed once after disabling"
);
Assert.deepEqual(savedOrder(), listedIds(), "the persisted sort order matches after disabling");
// Re-enable the provider.
cal.manager.registerCalendarProvider("blm", CalendarProvider);
await TestUtils.waitForCondition(
() => listedIds().includes(mid.id),
"the swapped calendar is still listed after enabling"
);
Assert.equal(
listedIds().indexOf(mid.id),
midIndex,
"the calendar keeps its position when the provider is enabled"
);
});
/**
* Tests that the calendars of a provider owning several of them keep their
* relative order, even when it differs from the registration order.
*/
add_task(async function testMultiCalendarProviderKeepsListOrder() {
const CalendarProvider = makeProvider("blm2");
cal.manager.registerCalendarProvider("blm2", CalendarProvider);
registerCleanupFunction(() => {
if (cal.manager.hasCalendarProvider("blm2")) {
cal.manager.unregisterCalendarProvider("blm2");
}
removeByName(["MultiA", "MultiB", "MultiC"]);
});
/**
* Creates and registers a calendar of the dynamic provider.
*
* @param {string} name - Name for the new calendar.
* @returns {calICalendar} - The registered calendar.
*/
function registerProviderCalendar(name) {
calendar.name = name;
calendar.id = cal.getUUID();
cal.manager.registerCalendar(calendar);
return cal.manager.getCalendarById(calendar.id);
}
// Registration (and cache) order is A, B, C.
const a = registerProviderCalendar("MultiA");
const b = registerProviderCalendar("MultiB");
const c = registerProviderCalendar("MultiC");
await TestUtils.waitForCondition(() => listedIds().includes(c.id), "provider calendars listed");
// Reorder the list to the reverse, C, B, A, so the list order no longer
// matches the registration order the manager iterates.
const list = document.getElementById("calendar-list");
/**
* Moves a row of the calendar list, like a user dragging it.
*
* @param {string} movingId - Id of the calendar to move.
* @param {string} beforeId - Id of the calendar to move it before.
*/
function moveBefore(movingId, beforeId) {
const moving = list.getElementsByAttribute("calendar-id", movingId)[0];
const before = list.getElementsByAttribute("calendar-id", beforeId)[0];
list.insertBefore(moving, before);
list.dispatchEvent(new CustomEvent("ordered", { detail: moving }));
}
moveBefore(c.id, a.id);
moveBefore(b.id, a.id);
const wantOrder = listedIds();
Assert.less(wantOrder.indexOf(c.id), wantOrder.indexOf(b.id), "reordered to C, B, A");
Assert.less(wantOrder.indexOf(b.id), wantOrder.indexOf(a.id), "reordered to C, B, A");
Assert.deepEqual(savedOrder(), wantOrder, "the reorder was persisted");
// Disable the provider: all three are swapped for dummies together.
cal.manager.unregisterCalendarProvider("blm2");
await TestUtils.waitForCondition(
() => !cal.manager.hasCalendarProvider("blm2"),
"the provider is disabled"
);
Assert.deepEqual(listedIds(), wantOrder, "the list order is kept when the provider is disabled");
Assert.deepEqual(savedOrder(), wantOrder, "the persisted order is kept when disabled");
// Re-enable the provider.
cal.manager.registerCalendarProvider("blm2", CalendarProvider);
await TestUtils.waitForCondition(
() => cal.manager.hasCalendarProvider("blm2"),
"the provider is enabled"
);
Assert.deepEqual(listedIds(), wantOrder, "the list order is kept when the provider is enabled");
});