diff --git a/typescript/sdk/src/metadata/blockExplorer.test.ts b/typescript/sdk/src/metadata/blockExplorer.test.ts index 53a358876b..3856668410 100644 --- a/typescript/sdk/src/metadata/blockExplorer.test.ts +++ b/typescript/sdk/src/metadata/blockExplorer.test.ts @@ -1,5 +1,7 @@ import { expect } from 'chai'; +import { ProtocolType } from '@hyperlane-xyz/utils'; + import { test1, testCosmosChain, @@ -8,6 +10,7 @@ import { import { getExplorerAddressUrl, + getExplorerApi, getExplorerApiUrl, getExplorerBaseUrl, getExplorerTxUrl, @@ -52,4 +55,120 @@ describe('Block explorer utils', () => { ); }); }); + + describe('Edge cases', () => { + const emptyChain = { + protocol: ProtocolType.Ethereum, + name: 'empty', + domainId: 1, + chainId: 1, + rpcUrls: [{ http: 'https://empty.test' }], + }; + + const chainWithoutApi = { + protocol: ProtocolType.Ethereum, + name: 'noapi', + chainId: 1, + domainId: 1, + rpcUrls: [{ http: 'https://noapi.test' }], + blockExplorers: [ + { + name: 'test', + url: 'https://test.com', + apiUrl: '', + }, + ], + }; + + it('handles chain without block explorers', () => { + expect(getExplorerBaseUrl(emptyChain)).to.be.null; + expect(getExplorerApi(emptyChain)).to.be.null; + expect(getExplorerTxUrl(emptyChain, '0x123')).to.be.null; + expect(getExplorerAddressUrl(emptyChain, '0x123')).to.be.null; + }); + + it('handles chain without api url', () => { + expect(getExplorerBaseUrl(chainWithoutApi)).to.equal('https://test.com/'); + expect(getExplorerApi(chainWithoutApi)).to.be.null; + }); + }); + + describe('Multiple block explorers', () => { + const multiExplorerChain = { + protocol: ProtocolType.Ethereum, + name: 'multi', + domainId: 1, + chainId: 1, + rpcUrls: [{ http: 'https://multi.test' }], + blockExplorers: [ + { + name: 'first', + url: 'https://first.com', + apiUrl: 'https://api.first.com', + apiKey: 'key1', + }, + { + name: 'second', + url: 'https://second.com', + apiUrl: 'https://api.second.com', + apiKey: 'key2', + }, + ], + }; + + it('uses correct explorer by index', () => { + expect(getExplorerBaseUrl(multiExplorerChain, 1)).to.equal( + 'https://second.com/', + ); + expect(getExplorerApiUrl(multiExplorerChain, 1)).to.equal( + 'https://api.second.com/?apikey=key2', + ); + }); + }); + + describe('Special chain names with different common paths', () => { + const nautilusChain = { + protocol: ProtocolType.Ethereum, + name: 'nautilus', + chainId: 1, + domainId: 1, + rpcUrls: [{ http: 'https://nautilus.test' }], + blockExplorers: [ + { + name: 'nautilus', + url: 'https://nautilus.com', + apiUrl: 'https://api.nautilus.com', + }, + ], + }; + + it('uses correct transaction path for special chains', () => { + expect(getExplorerTxUrl(nautilusChain, '0x123')).to.equal( + 'https://nautilus.com/transaction/0x123', + ); + }); + }); + + describe('URL handling', () => { + const chainWithTrailingSlash = { + protocol: ProtocolType.Ethereum, + name: 'test', + domainId: 1, + chainId: 1, + rpcUrls: [{ http: 'https://test.chain' }], + blockExplorers: [ + { + name: 'test', + url: 'https://test.com/', + apiUrl: 'https://api.test.com', + }, + ], + }; + + it('handles trailing slashes correctly', () => { + expect(getExplorerTxUrl(chainWithTrailingSlash, '0x123')).to.equal( + 'https://test.com/tx/0x123', + ); + }); + }); });