@@ -8,7 +8,12 @@ import {
88 getIsMetaMaskInfuraEndpointUrl ,
99 getIsQuicknodeEndpointUrl ,
1010 isPublicEndpointUrl ,
11+ initializeChainlistDomains ,
12+ isChainlistDomain ,
13+ isChainlistEndpointUrl ,
14+ resetChainlistDomainsCache ,
1115} from './network-utils' ;
16+ import * as storageHelpers from './storage-helpers' ;
1217
1318jest . mock ( '../constants/network' , ( ) => ( {
1419 FEATURED_RPCS : [
@@ -193,12 +198,233 @@ describe('isPublicEndpointUrl', () => {
193198 ) . toBe ( true ) ;
194199 } ) ;
195200
196- it ( 'returns false for unknown URLs' , ( ) => {
201+ it ( 'returns false for unknown URLs when chainlist is not initialized' , ( ) => {
202+ resetChainlistDomainsCache ( ) ;
197203 expect (
198204 isPublicEndpointUrl (
199205 'https://unknown.example.com' ,
200206 MOCK_METAMASK_INFURA_PROJECT_ID ,
201207 ) ,
202208 ) . toBe ( false ) ;
203209 } ) ;
210+
211+ it ( 'returns true for chainlist domains after initialization' , async ( ) => {
212+ resetChainlistDomainsCache ( ) ;
213+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
214+ cachedResponse : [
215+ {
216+ name : 'Test Chain' ,
217+ chainId : 1 ,
218+ rpc : [ 'https://chainlist-rpc.example.com/rpc' ] ,
219+ } ,
220+ ] ,
221+ } ) ;
222+
223+ await initializeChainlistDomains ( ) ;
224+
225+ expect (
226+ isPublicEndpointUrl (
227+ 'https://chainlist-rpc.example.com/v1/abc123' ,
228+ MOCK_METAMASK_INFURA_PROJECT_ID ,
229+ ) ,
230+ ) . toBe ( true ) ;
231+ } ) ;
232+ } ) ;
233+
234+ describe ( 'initializeChainlistDomains' , ( ) => {
235+ beforeEach ( ( ) => {
236+ resetChainlistDomainsCache ( ) ;
237+ jest . clearAllMocks ( ) ;
238+ } ) ;
239+
240+ it ( 'initializes domains from cached chains list' , async ( ) => {
241+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
242+ cachedResponse : [
243+ {
244+ name : 'Ethereum Mainnet' ,
245+ chainId : 1 ,
246+ rpc : [
247+ 'https://mainnet.infura.io/v3/key' ,
248+ 'https://eth-mainnet.alchemyapi.io/v2/key' ,
249+ ] ,
250+ } ,
251+ {
252+ name : 'Polygon' ,
253+ chainId : 137 ,
254+ rpc : [ 'https://polygon-rpc.com' ] ,
255+ } ,
256+ ] ,
257+ } ) ;
258+
259+ await initializeChainlistDomains ( ) ;
260+
261+ expect ( isChainlistDomain ( 'mainnet.infura.io' ) ) . toBe ( true ) ;
262+ expect ( isChainlistDomain ( 'eth-mainnet.alchemyapi.io' ) ) . toBe ( true ) ;
263+ expect ( isChainlistDomain ( 'polygon-rpc.com' ) ) . toBe ( true ) ;
264+ expect ( isChainlistDomain ( 'unknown.com' ) ) . toBe ( false ) ;
265+ } ) ;
266+
267+ it ( 'handles empty chains list' , async ( ) => {
268+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
269+ cachedResponse : [ ] ,
270+ } ) ;
271+
272+ await initializeChainlistDomains ( ) ;
273+
274+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
275+ } ) ;
276+
277+ it ( 'handles missing cache' , async ( ) => {
278+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( undefined ) ;
279+
280+ await initializeChainlistDomains ( ) ;
281+
282+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
283+ } ) ;
284+
285+ it ( 'handles chains without rpc field' , async ( ) => {
286+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
287+ cachedResponse : [
288+ {
289+ name : 'Chain Without RPC' ,
290+ chainId : 999 ,
291+ } ,
292+ ] ,
293+ } ) ;
294+
295+ await initializeChainlistDomains ( ) ;
296+
297+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
298+ } ) ;
299+
300+ it ( 'skips invalid URLs in rpc list' , async ( ) => {
301+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
302+ cachedResponse : [
303+ {
304+ name : 'Chain With Invalid RPC' ,
305+ chainId : 999 ,
306+ rpc : [ 'not-a-valid-url' , 'https://valid-rpc.com' ] ,
307+ } ,
308+ ] ,
309+ } ) ;
310+
311+ await initializeChainlistDomains ( ) ;
312+
313+ expect ( isChainlistDomain ( 'valid-rpc.com' ) ) . toBe ( true ) ;
314+ } ) ;
315+
316+ it ( 'only fetches from storage once on subsequent calls' , async ( ) => {
317+ const getStorageItemSpy = jest
318+ . spyOn ( storageHelpers , 'getStorageItem' )
319+ . mockResolvedValue ( {
320+ cachedResponse : [ ] ,
321+ } ) ;
322+
323+ await initializeChainlistDomains ( ) ;
324+ await initializeChainlistDomains ( ) ;
325+ await initializeChainlistDomains ( ) ;
326+
327+ // Storage should only be accessed once despite multiple calls
328+ expect ( getStorageItemSpy ) . toHaveBeenCalledTimes ( 1 ) ;
329+ } ) ;
330+ } ) ;
331+
332+ describe ( 'isChainlistDomain' , ( ) => {
333+ beforeEach ( ( ) => {
334+ resetChainlistDomainsCache ( ) ;
335+ } ) ;
336+
337+ it ( 'returns false when not initialized' , ( ) => {
338+ expect ( isChainlistDomain ( 'any-domain.com' ) ) . toBe ( false ) ;
339+ } ) ;
340+
341+ it ( 'returns false for empty domain' , async ( ) => {
342+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
343+ cachedResponse : [
344+ { name : 'Test' , chainId : 1 , rpc : [ 'https://test.com' ] } ,
345+ ] ,
346+ } ) ;
347+ await initializeChainlistDomains ( ) ;
348+
349+ expect ( isChainlistDomain ( '' ) ) . toBe ( false ) ;
350+ } ) ;
351+
352+ it ( 'is case insensitive' , async ( ) => {
353+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
354+ cachedResponse : [
355+ { name : 'Test' , chainId : 1 , rpc : [ 'https://Test-RPC.COM/path' ] } ,
356+ ] ,
357+ } ) ;
358+ await initializeChainlistDomains ( ) ;
359+
360+ expect ( isChainlistDomain ( 'test-rpc.com' ) ) . toBe ( true ) ;
361+ expect ( isChainlistDomain ( 'TEST-RPC.COM' ) ) . toBe ( true ) ;
362+ expect ( isChainlistDomain ( 'Test-RPC.com' ) ) . toBe ( true ) ;
363+ } ) ;
364+ } ) ;
365+
366+ describe ( 'isChainlistEndpointUrl' , ( ) => {
367+ beforeEach ( ( ) => {
368+ resetChainlistDomainsCache ( ) ;
369+ } ) ;
370+
371+ it ( 'returns false when not initialized' , ( ) => {
372+ expect ( isChainlistEndpointUrl ( 'https://any-domain.com/rpc' ) ) . toBe ( false ) ;
373+ } ) ;
374+
375+ it ( 'returns true for URLs with chainlist domains' , async ( ) => {
376+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
377+ cachedResponse : [
378+ { name : 'Test' , chainId : 1 , rpc : [ 'https://chainlist-domain.com' ] } ,
379+ ] ,
380+ } ) ;
381+ await initializeChainlistDomains ( ) ;
382+
383+ expect (
384+ isChainlistEndpointUrl ( 'https://chainlist-domain.com/v1/abc123' ) ,
385+ ) . toBe ( true ) ;
386+ expect ( isChainlistEndpointUrl ( 'https://chainlist-domain.com' ) ) . toBe ( true ) ;
387+ } ) ;
388+
389+ it ( 'returns false for URLs with unknown domains' , async ( ) => {
390+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
391+ cachedResponse : [
392+ { name : 'Test' , chainId : 1 , rpc : [ 'https://known-domain.com' ] } ,
393+ ] ,
394+ } ) ;
395+ await initializeChainlistDomains ( ) ;
396+
397+ expect ( isChainlistEndpointUrl ( 'https://unknown-domain.com/rpc' ) ) . toBe (
398+ false ,
399+ ) ;
400+ } ) ;
401+
402+ it ( 'returns false for invalid URLs' , ( ) => {
403+ expect ( isChainlistEndpointUrl ( 'not-a-url' ) ) . toBe ( false ) ;
404+ expect ( isChainlistEndpointUrl ( '' ) ) . toBe ( false ) ;
405+ } ) ;
406+ } ) ;
407+
408+ describe ( 'resetChainlistDomainsCache' , ( ) => {
409+ it ( 'clears the cache and allows reinitialization' , async ( ) => {
410+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
411+ cachedResponse : [
412+ { name : 'First' , chainId : 1 , rpc : [ 'https://first-domain.com' ] } ,
413+ ] ,
414+ } ) ;
415+ await initializeChainlistDomains ( ) ;
416+ expect ( isChainlistDomain ( 'first-domain.com' ) ) . toBe ( true ) ;
417+
418+ resetChainlistDomainsCache ( ) ;
419+ expect ( isChainlistDomain ( 'first-domain.com' ) ) . toBe ( false ) ;
420+
421+ jest . spyOn ( storageHelpers , 'getStorageItem' ) . mockResolvedValue ( {
422+ cachedResponse : [
423+ { name : 'Second' , chainId : 2 , rpc : [ 'https://second-domain.com' ] } ,
424+ ] ,
425+ } ) ;
426+ await initializeChainlistDomains ( ) ;
427+ expect ( isChainlistDomain ( 'second-domain.com' ) ) . toBe ( true ) ;
428+ expect ( isChainlistDomain ( 'first-domain.com' ) ) . toBe ( false ) ;
429+ } ) ;
204430} ) ;
0 commit comments