diff --git a/libs/wallet/src/connect-dialog/use-chain-id.spec.ts b/libs/wallet/src/connect-dialog/use-chain-id.spec.ts new file mode 100644 index 000000000..1e5263879 --- /dev/null +++ b/libs/wallet/src/connect-dialog/use-chain-id.spec.ts @@ -0,0 +1,72 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { useVegaWallet } from '../use-vega-wallet'; +import { useChainId } from './use-chain-id'; + +global.fetch = jest.fn(); +const mockFetch = global.fetch as jest.Mock; +mockFetch.mockImplementation((url: string) => { + return Promise.resolve({ ok: true }); +}); + +jest.mock('../use-vega-wallet', () => { + const original = jest.requireActual('../use-vega-wallet'); + return { + ...original, + useVegaWallet: jest.fn(), + }; +}); + +describe('useChainId', () => { + it('does not call fetch when statistics url could not be determined', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: '', + }); + renderHook(() => useChainId()); + await waitFor(() => { + expect(mockFetch).toHaveBeenCalledTimes(0); + }); + }); + + it('calls fetch with correct statistics url', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: 'http://localhost:1234/graphql', + }); + renderHook(() => useChainId()); + await waitFor(() => { + expect(mockFetch).toHaveBeenCalledWith( + 'http://localhost:1234/statistics' + ); + }); + }); + + it('does not return chain id when chain id is not present in response', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: 'http://localhost:1234/graphql', + }); + const { result } = renderHook(() => useChainId()); + await waitFor(() => { + expect(result.current).toBeUndefined(); + }); + }); + + it('returns chain id when chain id is present in response', async () => { + (useVegaWallet as jest.Mock).mockReturnValue({ + vegaUrl: 'http://localhost:1234/graphql', + }); + mockFetch.mockImplementation(() => { + return Promise.resolve({ + ok: true, + json: () => ({ + statistics: { + chainId: '1234', + }, + }), + }); + }); + const { result } = renderHook(() => useChainId()); + await waitFor(() => { + expect(result.current).not.toBeUndefined(); + expect(result.current).toEqual('1234'); + }); + }); +}); diff --git a/libs/wallet/src/connect-dialog/use-chain-id.ts b/libs/wallet/src/connect-dialog/use-chain-id.ts index d346bd2b9..559ed628d 100644 --- a/libs/wallet/src/connect-dialog/use-chain-id.ts +++ b/libs/wallet/src/connect-dialog/use-chain-id.ts @@ -3,17 +3,37 @@ import { useVegaWallet } from '../use-vega-wallet'; const cache: Record = {}; +/** + * Gets the statistics url for a given vega url. + * Example: + * https://graphql.example.com/graphql -> https://graphql.example.com/statistics + */ +const getNodeStatisticsUrl = (vegaUrl: string) => { + try { + const url = new URL(vegaUrl); + url.pathname = 'statistics'; + return url.toString(); + } catch (err) { + return undefined; + } +}; + export const useChainId = () => { const { vegaUrl } = useVegaWallet(); const [chainId, setChainId] = useState(cache[vegaUrl]); const [fetchAttempt, setFetchAttempt] = useState(1); + + const statisticsUrl = getNodeStatisticsUrl(vegaUrl); + useEffect(() => { + // abort when `/statistics` URL could not be determined + if (!statisticsUrl) return; let isCancelled = false; if (cache[vegaUrl]) { setChainId(cache[vegaUrl]); return; } - fetch(vegaUrl.replace('graphql', 'statistics')) + fetch(statisticsUrl) .then((response) => response.json()) .then((response) => { if (isCancelled) { @@ -32,6 +52,6 @@ export const useChainId = () => { return () => { isCancelled = true; }; - }, [fetchAttempt, vegaUrl]); + }, [fetchAttempt, statisticsUrl, vegaUrl]); return chainId; };