Skip to content

Commit

Permalink
fix: add tests for WalletConnectContext
Browse files Browse the repository at this point in the history
  • Loading branch information
iamacook committed Oct 2, 2023
1 parent 8f1537a commit 5c08405
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 10 deletions.
220 changes: 220 additions & 0 deletions src/services/walletconnect/WalletConnectContext.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { hexZeroPad } from 'ethers/lib/utils'
import { useContext } from 'react'
import type { SafeInfo } from '@safe-global/safe-gateway-typescript-sdk'

import { fireEvent, render, waitFor } from '@/tests/test-utils'
import { WalletConnectContext, WalletConnectProvider } from './WalletConnectContext'
import WalletConnectWallet from './WalletConnectWallet'
import { safeInfoSlice } from '@/store/safeInfoSlice'
import { useAppDispatch } from '@/store'

jest.mock('./WalletConnectWallet')

const TestComponent = () => {
const { walletConnect, error } = useContext(WalletConnectContext)
return (
<>
{walletConnect && <p>WalletConnect initialized</p>}
{error && <p>{error.message}</p>}
</>
)
}

describe('WalletConnectProvider', () => {
it('sets the walletConnect state', async () => {
jest.spyOn(WalletConnectWallet, 'init').mockImplementation(() => Promise.resolve())
jest.spyOn(WalletConnectWallet, 'updateSessions').mockImplementation(() => Promise.resolve())

const { getByText } = render(
<WalletConnectProvider>
<TestComponent />
</WalletConnectProvider>,
{
initialReduxState: {
safeInfo: {
loading: false,
data: {
address: {
value: hexZeroPad('0x123', 20),
},
chainId: '5',
} as SafeInfo,
},
},
},
)

await waitFor(() => {
expect(getByText('WalletConnect initialized')).toBeInTheDocument()
})
})

it('sets the error state', async () => {
jest.spyOn(WalletConnectWallet, 'init').mockImplementation(() => Promise.reject(new Error('Test init failed')))
jest.spyOn(WalletConnectWallet, 'updateSessions').mockImplementation(() => Promise.resolve())

const { getByText } = render(
<WalletConnectProvider>
<TestComponent />
</WalletConnectProvider>,
{
initialReduxState: {
safeInfo: {
loading: false,
data: {
address: {
value: hexZeroPad('0x123', 20),
},
chainId: '5',
} as SafeInfo,
},
},
},
)

await waitFor(() => {
expect(getByText('Test init failed')).toBeInTheDocument()
})
})

describe('updateSessions', () => {
const getUpdateSafeInfoComponent = (safeInfo: SafeInfo) => {
// eslint-disable-next-line react/display-name
return () => {
const dispatch = useAppDispatch()
const updateSafeInfo = () => {
dispatch(
safeInfoSlice.actions.set({
loading: false,
data: safeInfo,
}),
)
}

return <button onClick={() => updateSafeInfo()}>update</button>
}
}

it('updates sessions when the chainId changes', async () => {
jest.spyOn(WalletConnectWallet, 'init').mockImplementation(() => Promise.resolve())
jest.spyOn(WalletConnectWallet, 'updateSessions').mockImplementation(() => Promise.resolve())

const ChainUpdater = getUpdateSafeInfoComponent({
address: { value: hexZeroPad('0x123', 20) },
chainId: '1',
} as SafeInfo)

const { getByText } = render(
<WalletConnectProvider>
<TestComponent />
<ChainUpdater />
</WalletConnectProvider>,
{
initialReduxState: {
safeInfo: {
loading: false,
data: {
address: {
value: hexZeroPad('0x123', 20),
},
chainId: '5',
} as SafeInfo,
},
},
},
)

await waitFor(() => {
expect(getByText('WalletConnect initialized')).toBeInTheDocument()
expect(WalletConnectWallet.updateSessions).toHaveBeenCalledWith('5', hexZeroPad('0x123', 20))
})

fireEvent.click(getByText('update'))

await waitFor(() => {
expect(WalletConnectWallet.updateSessions).toHaveBeenCalledWith('1', hexZeroPad('0x123', 20))
})
})

it('updates sessions when the safeAddress changes', async () => {
jest.spyOn(WalletConnectWallet, 'init').mockImplementation(() => Promise.resolve())
jest.spyOn(WalletConnectWallet, 'updateSessions').mockImplementation(() => Promise.resolve())

const AddressUpdater = getUpdateSafeInfoComponent({
address: { value: hexZeroPad('0x456', 20) },
chainId: '5',
} as SafeInfo)

const { getByText } = render(
<WalletConnectProvider>
<TestComponent />
<AddressUpdater />
</WalletConnectProvider>,
{
initialReduxState: {
safeInfo: {
loading: false,
data: {
address: {
value: hexZeroPad('0x123', 20),
},
chainId: '5',
} as SafeInfo,
},
},
},
)

await waitFor(() => {
expect(getByText('WalletConnect initialized')).toBeInTheDocument()
expect(WalletConnectWallet.updateSessions).toHaveBeenCalledWith('5', hexZeroPad('0x123', 20))
})

fireEvent.click(getByText('update'))

await waitFor(() => {
expect(WalletConnectWallet.updateSessions).toHaveBeenCalledWith('5', hexZeroPad('0x456', 20))
})
})

it('sets the error state', async () => {
jest.spyOn(WalletConnectWallet, 'init').mockImplementation(() => Promise.resolve())
jest
.spyOn(WalletConnectWallet, 'updateSessions')
.mockImplementation(() => Promise.reject(new Error('Test updateSessions failed')))

const { getByText } = render(
<WalletConnectProvider>
<TestComponent />
</WalletConnectProvider>,
{
initialReduxState: {
safeInfo: {
loading: false,
data: {
address: {
value: hexZeroPad('0x123', 20),
},
chainId: '5',
} as SafeInfo,
},
},
},
)

await waitFor(() => {
expect(getByText('Test updateSessions failed')).toBeInTheDocument()
})
})
})

describe('onRequest', () => {
it.todo('does not continue with the request if there is no matching topic')

it.todo('does not continue with the request if there is no matching chainId')

it.todo('passes the request onto the Safe Wallet Provider and sends the response to WalletConnect')

it.todo('sets the error state if there is an error requesting the response from the Safe Wallet Provider')
})
})
6 changes: 3 additions & 3 deletions src/services/walletconnect/WalletConnectContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import WalletConnectWallet from './WalletConnectWallet'
import { asError } from '../exceptions/utils'
import { stripEip155Prefix } from './utils'

const walletConnectSingleton = new WalletConnectWallet()
const walletConnectSingleton = WalletConnectWallet

export const WalletConnectContext = createContext<{
walletConnect: WalletConnectWallet | null
walletConnect: typeof WalletConnectWallet | null
error: Error | null
}>({
walletConnect: null,
Expand All @@ -21,7 +21,7 @@ export const WalletConnectProvider = ({ children }: { children: ReactNode }) =>
safe: { chainId },
safeAddress,
} = useSafeInfo()
const [walletConnect, setWalletConnect] = useState<WalletConnectWallet | null>(null)
const [walletConnect, setWalletConnect] = useState<typeof WalletConnectWallet | null>(null)
const [error, setError] = useState<Error | null>(null)
const safeWalletProvider = useSafeWalletProvider()

Expand Down
12 changes: 9 additions & 3 deletions src/services/walletconnect/WalletConnectWallet.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { hexZeroPad } from 'ethers/lib/utils'
import type { ProposalTypes, SessionTypes, SignClientTypes, Verify } from '@walletconnect/types'
import type { IWeb3Wallet, Web3WalletTypes } from '@walletconnect/web3wallet'

import WalletConnectWallet from './WalletConnectWallet'
import type WalletConnectWallet from './WalletConnectWallet'

jest.mock('@walletconnect/core', () => ({
Core: jest.fn(),
Expand Down Expand Up @@ -45,14 +45,20 @@ jest.mock('@walletconnect/web3wallet', () => {
})

describe('WalletConnectWallet', () => {
let wallet: WalletConnectWallet
let wallet: typeof WalletConnectWallet

beforeEach(async () => {
wallet = new WalletConnectWallet()
// Reset import
wallet = (await import('./WalletConnectWallet')).default

await wallet.init()
})

afterEach(() => {
// Reset instance to avoid side leaking mocks
jest.resetModules()
})

describe('connect', () => {
it('should call pair with the correct parameters', async () => {
const pairSpy = jest.spyOn(((wallet as any).web3Wallet as IWeb3Wallet).core.pairing, 'pair')
Expand Down
3 changes: 2 additions & 1 deletion src/services/walletconnect/WalletConnectWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,4 +251,5 @@ class WalletConnectWallet {
}
}

export default WalletConnectWallet
// Instance needs to be exported for testing purposes
export default new WalletConnectWallet()
6 changes: 3 additions & 3 deletions src/services/walletconnect/useWalletConnectSessions.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ describe('useWalletConnectSessions', () => {
getActiveSessions: jest.fn().mockReturnValue(sessions),
onSessionAdd: jest.fn(),
onSessionDelete: jest.fn(),
} as unknown as WalletConnectWallet
} as unknown as typeof WalletConnectWallet

const wrapper = ({ children }: any) => (
<WalletConnectContext.Provider value={{ walletConnect: mockWalletConnect, error: null }}>
Expand All @@ -46,7 +46,7 @@ describe('useWalletConnectSessions', () => {
getActiveSessions: jest.fn().mockReturnValue([]),
onSessionAdd: jest.fn(),
onSessionDelete: jest.fn(),
} as unknown as WalletConnectWallet
} as unknown as typeof WalletConnectWallet

const wrapper = ({ children }: any) => (
<WalletConnectContext.Provider value={{ walletConnect: mockWalletConnect, error: null }}>
Expand Down Expand Up @@ -94,7 +94,7 @@ describe('useWalletConnectSessions', () => {
getActiveSessions: jest.fn().mockReturnValue([]),
onSessionAdd: jest.fn(),
onSessionDelete: jest.fn(),
} as unknown as WalletConnectWallet
} as unknown as typeof WalletConnectWallet

const wrapper = ({ children }: any) => (
<WalletConnectContext.Provider value={{ walletConnect: mockWalletConnect, error: null }}>
Expand Down

0 comments on commit 5c08405

Please sign in to comment.