import { renderHook } from '@testing-library/react-hooks'; import type { EnvironmentWithOptionalUrl } from './use-config'; import { useConfig } from './use-config'; import { Networks, ErrorType } from '../types'; const mockConfig = { hosts: [ 'https://vega-host-1.com', 'https://vega-host-2.com', 'https://vega-host-3.com', 'https://vega-host-4.com', ], }; const mockEnvironment: EnvironmentWithOptionalUrl = { VEGA_ENV: Networks.TESTNET, VEGA_CONFIG_URL: 'https://vega.url/config.json', VEGA_NETWORKS: {}, ETHEREUM_PROVIDER_URL: 'https://ethereum.provider', ETHERSCAN_URL: 'https://etherscan.url', GIT_BRANCH: 'test', GIT_ORIGIN_URL: 'https://github.com/test/repo', GIT_COMMIT_HASH: 'abcde01234', GITHUB_FEEDBACK_URL: 'https://github.com/test/feedback', }; function setupFetch(configUrl: string) { return (url: RequestInfo) => { if (url === configUrl) { return Promise.resolve({ ok: true, json: () => Promise.resolve(mockConfig), } as Response); } return Promise.resolve({ ok: true, } as Response); }; } global.fetch = jest.fn(); const onError = jest.fn(); beforeAll(() => { jest.useFakeTimers(); }); beforeEach(() => { onError.mockClear(); window.localStorage.clear(); // @ts-ignore typescript doesn't recognise the mocked instance global.fetch.mockReset(); // @ts-ignore typescript doesn't recognise the mocked instance global.fetch.mockImplementation( setupFetch(mockEnvironment.VEGA_CONFIG_URL ?? '') ); }); afterAll(() => { jest.clearAllMocks(); }); describe('useConfig hook', () => { it("doesn't update when there is no VEGA_CONFIG_URL in the environment", async () => { const mockEnvWithoutUrl = { ...mockEnvironment, VEGA_CONFIG_URL: undefined, }; const { result } = renderHook(() => useConfig(mockEnvWithoutUrl, onError)); expect(result.current.config).toBe(undefined); }); it('fetches configuration from the provided url', async () => { const { result, waitForNextUpdate } = renderHook(() => useConfig(mockEnvironment, onError) ); await waitForNextUpdate(); expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL); expect(result.current.config).toEqual(mockConfig); }); it('caches the configuration', async () => { const { result: firstResult, waitForNextUpdate: waitForFirstUpdate } = renderHook(() => useConfig(mockEnvironment, onError)); await waitForFirstUpdate(); expect(fetch).toHaveBeenCalledTimes(1); expect(firstResult.current.config).toEqual(mockConfig); const { result: secondResult } = renderHook(() => useConfig(mockEnvironment, onError) ); expect(fetch).toHaveBeenCalledTimes(1); expect(secondResult.current.config).toEqual(mockConfig); }); it('executes the error callback when the config endpoint fails', async () => { // @ts-ignore typescript doesn't recognise the mocked instance global.fetch.mockImplementation(() => Promise.reject()); const { result, waitForNextUpdate } = renderHook(() => useConfig(mockEnvironment, onError) ); await waitForNextUpdate(); expect(result.current.config).toEqual({ hosts: [] }); expect(onError).toHaveBeenCalledWith(ErrorType.CONFIG_LOAD_ERROR); }); it('executes the error callback when the config validation fails', async () => { // @ts-ignore typescript doesn't recognise the mocked instance global.fetch.mockImplementation(() => Promise.resolve({ ok: true, json: () => Promise.resolve({ data: 'not-valid-config' }), }) ); const { result, waitForNextUpdate } = renderHook(() => useConfig(mockEnvironment, onError) ); await waitForNextUpdate(); expect(result.current.config).toBe(undefined); expect(onError).toHaveBeenCalledWith(ErrorType.CONFIG_VALIDATION_ERROR); }); });