Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
*/
"use strict";
const { TestUtils } = ChromeUtils.importESModule(
);
const { AboutNewTabComponentRegistry } = ChromeUtils.importESModule(
"moz-src:///browser/components/newtab/AboutNewTabComponents.sys.mjs"
);
const CATEGORY_NAME = "browser-newtab-external-component";
const originalCategoryEntries = [];
add_setup(() => {
for (let { entry, value } of Services.catMan.enumerateCategory(
CATEGORY_NAME
)) {
originalCategoryEntries.push({ entry, value });
}
Services.catMan.deleteCategory(CATEGORY_NAME);
registerCleanupFunction(() => {
Services.catMan.deleteCategory(CATEGORY_NAME);
for (let { entry, value } of originalCategoryEntries) {
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
entry,
value,
false,
true
);
}
});
});
/**
* Tests that the AboutNewTabComponentRegistry can be instantiated.
*/
add_task(async function test_registry_initializes() {
let registry = new AboutNewTabComponentRegistry();
Assert.ok(registry, "Registry should instantiate");
registry.destroy();
});
/**
* Tests that the registry loads component configurations from category entries
* and fires an UPDATED_EVENT when components are registered.
*/
add_task(async function test_registry_loads_valid_configuration() {
let updateEventFired = false;
let registry = new AboutNewTabComponentRegistry();
registry.on(AboutNewTabComponentRegistry.UPDATED_EVENT, () => {
updateEventFired = true;
});
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModuleURI,
"TestRegistrant1",
false,
true
);
await TestUtils.waitForCondition(
() => updateEventFired,
"Should fire updated event"
);
let components = Array.from(registry.values);
Assert.equal(components.length, 1, "Should have one component registered");
Assert.equal(components[0].type, "SEARCH", "Component type should be SEARCH");
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false);
registry.destroy();
});
/**
* Tests that the registry rejects duplicate component types and only
* registers the first component when multiple registrants provide
* components with the same type.
*/
add_task(async function test_registry_rejects_duplicate_types() {
let registry = new AboutNewTabComponentRegistry();
const testModuleURI =
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModuleURI,
"TestRegistrantDuplicateTypes",
false,
true
);
await TestUtils.waitForTick();
let components = Array.from(registry.values);
Assert.equal(
components.length,
1,
"Should only register one component when types conflict"
);
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false);
registry.destroy();
});
/**
* Tests that the registry rejects component configurations that are missing
* required fields like the type property.
*/
add_task(async function test_registry_rejects_invalid_configurations() {
let registry = new AboutNewTabComponentRegistry();
const testModuleURI =
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModuleURI,
"TestRegistrantInvalidConfigs",
false,
true
);
await TestUtils.waitForTick();
let components = Array.from(registry.values);
Assert.equal(
components.length,
0,
"Should reject configurations without valid type"
);
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false);
registry.destroy();
});
/**
* Tests that the registry properly handles category entry removal by
* unregistering components and firing an UPDATED_EVENT.
*/
add_task(async function test_registry_handles_category_removal() {
let updateCount = 0;
let registry = new AboutNewTabComponentRegistry();
registry.on(AboutNewTabComponentRegistry.UPDATED_EVENT, () => {
updateCount++;
});
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModuleURI,
"TestRegistrant1",
false,
true
);
await TestUtils.waitForCondition(() => updateCount >= 1);
const initialUpdateCount = updateCount;
let components = Array.from(registry.values);
Assert.equal(components.length, 1, "Should have component registered");
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false);
await TestUtils.waitForCondition(() => updateCount > initialUpdateCount);
components = Array.from(registry.values);
Assert.equal(components.length, 0, "Should have no components after removal");
registry.destroy();
});
/**
* Tests that the registry responds to registrant updates by refreshing
* its component list and firing update events.
*/
add_task(async function test_registry_handles_registrant_updates() {
let registry = new AboutNewTabComponentRegistry();
let updateCount = 0;
registry.on(AboutNewTabComponentRegistry.UPDATED_EVENT, () => {
updateCount++;
});
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModuleURI,
"TestRegistrant1",
false,
true
);
await TestUtils.waitForCondition(() => updateCount >= 1);
let components = Array.from(registry.values);
Assert.equal(components.length, 1, "Should have initial component");
Assert.equal(components[0].type, "SEARCH", "Initial type should be SEARCH");
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false);
registry.destroy();
});
/**
* Tests that calling destroy() on the registry properly cleans up all
* registered components and internal state.
*/
add_task(async function test_registry_cleanup_on_destroy() {
let registry = new AboutNewTabComponentRegistry();
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModuleURI,
"TestRegistrant1",
false,
true
);
await TestUtils.waitForTick();
registry.destroy();
let components = Array.from(registry.values);
Assert.equal(components.length, 0, "Should have no components after destroy");
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false);
});
/**
* Tests that the registry validates registrants are instances of
* BaseAboutNewTabComponentRegistrant and rejects invalid registrants.
*/
add_task(async function test_registrant_subclass_validation() {
let registry = new AboutNewTabComponentRegistry();
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
invalidModuleURI,
"NotARegistrant",
false,
true
);
await TestUtils.waitForTick();
let components = Array.from(registry.values);
Assert.equal(
components.length,
0,
"Should reject registrant that doesn't subclass BaseAboutNewTabComponentRegistrant"
);
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, invalidModuleURI, false);
registry.destroy();
});
/**
* Tests that the registry can handle multiple registrants providing
* different component types simultaneously.
*/
add_task(async function test_multiple_registrants() {
let registry = new AboutNewTabComponentRegistry();
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModule1URI,
"TestRegistrant1",
false,
true
);
Services.catMan.addCategoryEntry(
CATEGORY_NAME,
testModule2URI,
"TestRegistrant2",
false,
true
);
await TestUtils.waitForTick();
let components = Array.from(registry.values);
Assert.equal(
components.length,
2,
"Should register components from multiple registrants"
);
let types = components.map(c => c.type).sort();
Assert.deepEqual(
types,
["OTHER", "SEARCH"],
"Should have both component types"
);
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModule1URI, false);
Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModule2URI, false);
registry.destroy();
});