Skip to content

Commit

Permalink
Add tests for useAuthenticate hook
Browse files Browse the repository at this point in the history
  • Loading branch information
tmjssz committed Aug 12, 2024
1 parent fcc5ff7 commit 073b4f1
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 4 deletions.
75 changes: 75 additions & 0 deletions src/hooks/useAuthenticate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { act } from 'react'
import { waitFor } from '@testing-library/react'
import { SafeClient } from '@safe-global/safe-kit'
import { useAuthenticate } from '@/hooks/useAuthenticate.js'
import { renderHookInMockedSafeProvider } from '@test/utils.js'
import { signerPrivateKeys } from '@test/fixtures.js'
import { SafeContextType } from '@/SafeProvider.js'

describe('useAuthenticate', () => {
const signerClientMock = { safeClient: 'signer' } as unknown as SafeClient
const setSignerMock = jest.fn(() => Promise.resolve())

const renderUseAuthenticate = async (context: Partial<SafeContextType> = {}) => {
const renderResult = renderHookInMockedSafeProvider(() => useAuthenticate(), {
signerClient: signerClientMock,
setSigner: setSignerMock,
...context
})

await waitFor(() =>
expect(renderResult.result.current).toEqual({
connect: expect.any(Function),
disconnect: expect.any(Function)
})
)

return renderResult
}

afterEach(() => {
jest.clearAllMocks()
})

describe('connect', () => {
it('should create a new signer client if being called with a valid private key', async () => {
const { result } = await renderUseAuthenticate()

await act(() => result.current.connect(signerPrivateKeys[1]))

expect(setSignerMock).toHaveBeenCalledTimes(1)
expect(setSignerMock).toHaveBeenCalledWith(signerPrivateKeys[1])
})

it('should throw if being called with an empty private key string', async () => {
const { result } = await renderUseAuthenticate()

expect(() => result.current.connect('')).rejects.toThrow(
'Failed to connect because signer is empty'
)

expect(setSignerMock).toHaveBeenCalledTimes(0)
})
})

describe('disconnect', () => {
it('should set signer to `undefined` if connected', async () => {
const { result } = await renderUseAuthenticate()

await act(() => result.current.disconnect())

expect(setSignerMock).toHaveBeenCalledTimes(1)
expect(setSignerMock).toHaveBeenCalledWith(undefined)
})

it('should throw if being called when signerClient is not defined', async () => {
const { result } = await renderUseAuthenticate({ signerClient: undefined })

expect(() => result.current.disconnect()).rejects.toThrow(
'Failed to disconnect because no signer is connected'
)

expect(setSignerMock).toHaveBeenCalledTimes(0)
})
})
})
46 changes: 42 additions & 4 deletions test/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createContext, createElement } from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { createElement } from 'react'
import { renderHook, RenderHookOptions } from '@testing-library/react'
import { SafeProvider, SafeProviderProps } from '@/SafeProvider.js'
import * as safeProvider from '@/SafeProvider.js'

/**
* Wrapper to render a hook in a SafeProvider.
Expand All @@ -12,15 +12,53 @@ import { SafeProvider, SafeProviderProps } from '@/SafeProvider.js'
*/
export function renderHookInSafeProvider<Result, Props>(
hook: (initialProps: Props) => Result,
providerProps: SafeProviderProps,
providerProps: safeProvider.SafeProviderProps,
options: RenderHookOptions<Props> = {}
) {
return renderHook<Result, Props>(hook, {
...options,
wrapper: ({ children }) => createElement(SafeProvider, providerProps, children)
wrapper: ({ children }) => createElement(safeProvider.SafeProvider, providerProps, children)
})
}

/**
* Wrapper to render a hook in a mocked SafeProvider.
* @param hook Hook to render.
* @param context Mocked SafeContext properties.
* @param options Additional options to pass to renderHook.
* @returns RenderHookResult of the hook rendered in a mocked SafeProvider.
*/
export function renderHookInMockedSafeProvider<Result, Props>(
hook: (initialProps: Props) => Result,
context: Partial<safeProvider.SafeContextType> = {},
options: RenderHookOptions<Props> = {}
) {
const contextValue = {
initialized: false,
config: undefined,
setConfig: () => {},
setSigner: () => Promise.resolve(),
publicClient: undefined,
signerClient: undefined,
...context
}

const SafeContext = createContext<safeProvider.SafeContextType>(contextValue)

const OriginalSafeContext = safeProvider.SafeContext
;(safeProvider as any).SafeContext = SafeContext

const renderResult = renderHook<Result, Props>(hook, {
...options,
wrapper: ({ children }) =>
createElement(SafeContext.Provider, { value: contextValue }, children)
})

;(safeProvider as any).SafeContext = OriginalSafeContext

return renderResult
}

/**
* Wrapper to render a hook in a QueryClientProvider.
* @param hook Hook to render.
Expand Down

0 comments on commit 073b4f1

Please sign in to comment.