test_AboutNewTabComponentRegistry.js (8563B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ 3 */ 4 5 "use strict"; 6 7 const { TestUtils } = ChromeUtils.importESModule( 8 "resource://testing-common/TestUtils.sys.mjs" 9 ); 10 11 const { AboutNewTabComponentRegistry } = ChromeUtils.importESModule( 12 "moz-src:///browser/components/newtab/AboutNewTabComponents.sys.mjs" 13 ); 14 15 const CATEGORY_NAME = "browser-newtab-external-component"; 16 17 const originalCategoryEntries = []; 18 19 add_setup(() => { 20 for (let { entry, value } of Services.catMan.enumerateCategory( 21 CATEGORY_NAME 22 )) { 23 originalCategoryEntries.push({ entry, value }); 24 } 25 Services.catMan.deleteCategory(CATEGORY_NAME); 26 27 registerCleanupFunction(() => { 28 Services.catMan.deleteCategory(CATEGORY_NAME); 29 30 for (let { entry, value } of originalCategoryEntries) { 31 Services.catMan.addCategoryEntry( 32 CATEGORY_NAME, 33 entry, 34 value, 35 false, 36 true 37 ); 38 } 39 }); 40 }); 41 42 /** 43 * Tests that the AboutNewTabComponentRegistry can be instantiated. 44 */ 45 add_task(async function test_registry_initializes() { 46 let registry = new AboutNewTabComponentRegistry(); 47 Assert.ok(registry, "Registry should instantiate"); 48 registry.destroy(); 49 }); 50 51 /** 52 * Tests that the registry loads component configurations from category entries 53 * and fires an UPDATED_EVENT when components are registered. 54 */ 55 add_task(async function test_registry_loads_valid_configuration() { 56 let updateEventFired = false; 57 let registry = new AboutNewTabComponentRegistry(); 58 59 registry.on(AboutNewTabComponentRegistry.UPDATED_EVENT, () => { 60 updateEventFired = true; 61 }); 62 63 const testModuleURI = "resource://testing-common/TestRegistrant1.sys.mjs"; 64 65 Services.catMan.addCategoryEntry( 66 CATEGORY_NAME, 67 testModuleURI, 68 "TestRegistrant1", 69 false, 70 true 71 ); 72 73 await TestUtils.waitForCondition( 74 () => updateEventFired, 75 "Should fire updated event" 76 ); 77 78 let components = Array.from(registry.values); 79 Assert.equal(components.length, 1, "Should have one component registered"); 80 Assert.equal(components[0].type, "SEARCH", "Component type should be SEARCH"); 81 82 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false); 83 registry.destroy(); 84 }); 85 86 /** 87 * Tests that the registry rejects duplicate component types and only 88 * registers the first component when multiple registrants provide 89 * components with the same type. 90 */ 91 add_task(async function test_registry_rejects_duplicate_types() { 92 let registry = new AboutNewTabComponentRegistry(); 93 94 const testModuleURI = 95 "resource://testing-common/TestRegistrantDuplicateTypes.sys.mjs"; 96 97 Services.catMan.addCategoryEntry( 98 CATEGORY_NAME, 99 testModuleURI, 100 "TestRegistrantDuplicateTypes", 101 false, 102 true 103 ); 104 105 await TestUtils.waitForTick(); 106 107 let components = Array.from(registry.values); 108 Assert.equal( 109 components.length, 110 1, 111 "Should only register one component when types conflict" 112 ); 113 114 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false); 115 registry.destroy(); 116 }); 117 118 /** 119 * Tests that the registry rejects component configurations that are missing 120 * required fields like the type property. 121 */ 122 add_task(async function test_registry_rejects_invalid_configurations() { 123 let registry = new AboutNewTabComponentRegistry(); 124 125 const testModuleURI = 126 "resource://testing-common/TestRegistrantInvalidConfigs.sys.mjs"; 127 128 Services.catMan.addCategoryEntry( 129 CATEGORY_NAME, 130 testModuleURI, 131 "TestRegistrantInvalidConfigs", 132 false, 133 true 134 ); 135 136 await TestUtils.waitForTick(); 137 138 let components = Array.from(registry.values); 139 Assert.equal( 140 components.length, 141 0, 142 "Should reject configurations without valid type" 143 ); 144 145 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false); 146 registry.destroy(); 147 }); 148 149 /** 150 * Tests that the registry properly handles category entry removal by 151 * unregistering components and firing an UPDATED_EVENT. 152 */ 153 add_task(async function test_registry_handles_category_removal() { 154 let updateCount = 0; 155 let registry = new AboutNewTabComponentRegistry(); 156 157 registry.on(AboutNewTabComponentRegistry.UPDATED_EVENT, () => { 158 updateCount++; 159 }); 160 161 const testModuleURI = "resource://testing-common/TestRegistrant1.sys.mjs"; 162 163 Services.catMan.addCategoryEntry( 164 CATEGORY_NAME, 165 testModuleURI, 166 "TestRegistrant1", 167 false, 168 true 169 ); 170 171 await TestUtils.waitForCondition(() => updateCount >= 1); 172 173 const initialUpdateCount = updateCount; 174 let components = Array.from(registry.values); 175 Assert.equal(components.length, 1, "Should have component registered"); 176 177 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false); 178 179 await TestUtils.waitForCondition(() => updateCount > initialUpdateCount); 180 181 components = Array.from(registry.values); 182 Assert.equal(components.length, 0, "Should have no components after removal"); 183 184 registry.destroy(); 185 }); 186 187 /** 188 * Tests that the registry responds to registrant updates by refreshing 189 * its component list and firing update events. 190 */ 191 add_task(async function test_registry_handles_registrant_updates() { 192 let registry = new AboutNewTabComponentRegistry(); 193 let updateCount = 0; 194 195 registry.on(AboutNewTabComponentRegistry.UPDATED_EVENT, () => { 196 updateCount++; 197 }); 198 199 const testModuleURI = "resource://testing-common/TestRegistrant1.sys.mjs"; 200 201 Services.catMan.addCategoryEntry( 202 CATEGORY_NAME, 203 testModuleURI, 204 "TestRegistrant1", 205 false, 206 true 207 ); 208 209 await TestUtils.waitForCondition(() => updateCount >= 1); 210 211 let components = Array.from(registry.values); 212 Assert.equal(components.length, 1, "Should have initial component"); 213 Assert.equal(components[0].type, "SEARCH", "Initial type should be SEARCH"); 214 215 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false); 216 registry.destroy(); 217 }); 218 219 /** 220 * Tests that calling destroy() on the registry properly cleans up all 221 * registered components and internal state. 222 */ 223 add_task(async function test_registry_cleanup_on_destroy() { 224 let registry = new AboutNewTabComponentRegistry(); 225 226 const testModuleURI = "resource://testing-common/TestRegistrant1.sys.mjs"; 227 228 Services.catMan.addCategoryEntry( 229 CATEGORY_NAME, 230 testModuleURI, 231 "TestRegistrant1", 232 false, 233 true 234 ); 235 236 await TestUtils.waitForTick(); 237 238 registry.destroy(); 239 240 let components = Array.from(registry.values); 241 Assert.equal(components.length, 0, "Should have no components after destroy"); 242 243 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModuleURI, false); 244 }); 245 246 /** 247 * Tests that the registry validates registrants are instances of 248 * BaseAboutNewTabComponentRegistrant and rejects invalid registrants. 249 */ 250 add_task(async function test_registrant_subclass_validation() { 251 let registry = new AboutNewTabComponentRegistry(); 252 253 const invalidModuleURI = "resource://testing-common/NotARegistrant.sys.mjs"; 254 255 Services.catMan.addCategoryEntry( 256 CATEGORY_NAME, 257 invalidModuleURI, 258 "NotARegistrant", 259 false, 260 true 261 ); 262 263 await TestUtils.waitForTick(); 264 265 let components = Array.from(registry.values); 266 Assert.equal( 267 components.length, 268 0, 269 "Should reject registrant that doesn't subclass BaseAboutNewTabComponentRegistrant" 270 ); 271 272 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, invalidModuleURI, false); 273 registry.destroy(); 274 }); 275 276 /** 277 * Tests that the registry can handle multiple registrants providing 278 * different component types simultaneously. 279 */ 280 add_task(async function test_multiple_registrants() { 281 let registry = new AboutNewTabComponentRegistry(); 282 283 const testModule1URI = "resource://testing-common/TestRegistrant1.sys.mjs"; 284 const testModule2URI = "resource://testing-common/TestRegistrant2.sys.mjs"; 285 286 Services.catMan.addCategoryEntry( 287 CATEGORY_NAME, 288 testModule1URI, 289 "TestRegistrant1", 290 false, 291 true 292 ); 293 294 Services.catMan.addCategoryEntry( 295 CATEGORY_NAME, 296 testModule2URI, 297 "TestRegistrant2", 298 false, 299 true 300 ); 301 302 await TestUtils.waitForTick(); 303 304 let components = Array.from(registry.values); 305 Assert.equal( 306 components.length, 307 2, 308 "Should register components from multiple registrants" 309 ); 310 311 let types = components.map(c => c.type).sort(); 312 Assert.deepEqual( 313 types, 314 ["OTHER", "SEARCH"], 315 "Should have both component types" 316 ); 317 318 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModule1URI, false); 319 Services.catMan.deleteCategoryEntry(CATEGORY_NAME, testModule2URI, false); 320 registry.destroy(); 321 });