-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added hook tests Added mock for XMTP
- Loading branch information
Alex Risch
authored and
Alex Risch
committed
May 30, 2024
1 parent
ea02aa2
commit ee012a2
Showing
10 changed files
with
547 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
function mockedClient() { | ||
return { | ||
conversations: { | ||
streamAllMessages: jest.fn(() => {}), | ||
cancelStreamAllMessages: jest.fn(() => {}), | ||
cancelStream: jest.fn(() => {}), | ||
}, | ||
exportKeyBundle: jest.fn(() => 'keybundle'), | ||
canMessage: jest.fn(() => true), | ||
}; | ||
} | ||
|
||
module.exports = { | ||
Client: { | ||
createFromKeyBundle: jest.fn().mockImplementation(mockedClient), | ||
createRandom: jest.fn().mockImplementation(mockedClient), | ||
}, | ||
StaticAttachmentCodec: jest.fn().mockImplementation(() => { | ||
return {}; | ||
}), | ||
RemoteAttachmentCodec: jest.fn().mockImplementation(() => { | ||
return {}; | ||
}), | ||
JSContentCodec: jest.fn().mockImplementation(() => { | ||
return {}; | ||
}), | ||
GroupChangeCodec: jest.fn().mockImplementation(() => { | ||
return {}; | ||
}), | ||
ReplyCodec: jest.fn().mockImplementation(() => ({})), | ||
ReactionCodec: jest.fn().mockImplementation(() => ({})), | ||
emitter: {removeAllListeners: jest.fn(() => {})}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import {renderHook} from '@testing-library/react-hooks'; | ||
import {useAddress} from './useAddress'; | ||
import {useClient} from './useClient'; | ||
|
||
jest.mock('./useClient'); | ||
|
||
describe('useAddress', () => { | ||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
test('should return address and loading state from useClient', () => { | ||
const mockClient = {address: '0x1234567890abcdef1234567890abcdef12345678'}; | ||
const mockLoading = false; | ||
|
||
(useClient as jest.Mock).mockReturnValue({ | ||
client: mockClient, | ||
loading: mockLoading, | ||
}); | ||
|
||
const {result} = renderHook(() => useAddress()); | ||
|
||
expect(result.current.address).toBe(mockClient.address); | ||
expect(result.current.loading).toBe(mockLoading); | ||
}); | ||
|
||
test('should return undefined address if client is not available', () => { | ||
const mockClient = null; | ||
const mockLoading = true; | ||
|
||
(useClient as jest.Mock).mockReturnValue({ | ||
client: mockClient, | ||
loading: mockLoading, | ||
}); | ||
|
||
const {result} = renderHook(() => useAddress()); | ||
|
||
expect(result.current.address).toBeUndefined(); | ||
expect(result.current.loading).toBe(mockLoading); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import {renderHook} from '@testing-library/react-hooks'; | ||
import React from 'react'; | ||
import {ClientContext} from '../context/ClientContext'; | ||
import {useClient} from './useClient'; | ||
|
||
describe('useClient', () => { | ||
test('should return the client context value', () => { | ||
const mockContext = { | ||
client: null, | ||
setClient: () => {}, | ||
loading: false, | ||
}; | ||
|
||
const wrapper = ({children}: {children: React.ReactNode}) => ( | ||
<ClientContext.Provider value={mockContext}> | ||
{children} | ||
</ClientContext.Provider> | ||
); | ||
|
||
const {result} = renderHook(() => useClient(), {wrapper}); | ||
|
||
expect(result.current).toBe(mockContext); | ||
}); | ||
|
||
test('should return null if no client context value is provided', () => { | ||
const wrapper = ({children}: {children: React.ReactNode}) => ( | ||
// @ts-ignore-next-line | ||
<ClientContext.Provider value={null}>{children}</ClientContext.Provider> | ||
); | ||
|
||
const {result} = renderHook(() => useClient(), {wrapper}); | ||
|
||
expect(result.current).toBeNull(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import {act, renderHook} from '@testing-library/react-hooks'; | ||
import {mmkvStorage} from '../services/mmkvStorage'; | ||
import {getEnsInfo} from '../utils/getEnsInfo'; | ||
import {useContactInfo} from './useContactInfo'; | ||
|
||
// Mock dependencies | ||
jest.mock('../services/mmkvStorage', () => ({ | ||
mmkvStorage: { | ||
getEnsName: jest.fn(), | ||
getEnsAvatar: jest.fn(), | ||
saveEnsName: jest.fn(), | ||
saveEnsAvatar: jest.fn(), | ||
clearEnsAvatar: jest.fn(), | ||
}, | ||
})); | ||
|
||
jest.mock('../utils/formatAddress', () => ({ | ||
formatAddress: jest.fn(address => `Formatted: ${address}`), | ||
})); | ||
|
||
jest.mock('../utils/getEnsInfo', () => ({ | ||
getEnsInfo: jest.fn(), | ||
})); | ||
|
||
describe('useContactInfo', () => { | ||
const address = '0x1234567890abcdef1234567890abcdef12345678'; | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
test('should return initial state when no address is provided', () => { | ||
const {result} = renderHook(() => useContactInfo()); | ||
|
||
expect(result.current).toEqual({ | ||
displayName: null, | ||
avatarUrl: null, | ||
loading: true, | ||
}); | ||
}); | ||
|
||
test('should return cached ENS name and avatar if available', () => { | ||
(mmkvStorage.getEnsName as jest.Mock).mockReturnValue('cachedName'); | ||
(mmkvStorage.getEnsAvatar as jest.Mock).mockReturnValue('cachedAvatarUrl'); | ||
(getEnsInfo as jest.Mock).mockRejectedValueOnce('Failed to fetch ENS info'); | ||
|
||
const {result} = renderHook(() => useContactInfo(address)); | ||
|
||
expect(result.current).toEqual({ | ||
displayName: 'cachedName', | ||
avatarUrl: 'cachedAvatarUrl', | ||
loading: true, | ||
}); | ||
}); | ||
|
||
test('should fetch ENS info and update state', async () => { | ||
(mmkvStorage.getEnsName as jest.Mock).mockReturnValue(null); | ||
(mmkvStorage.getEnsAvatar as jest.Mock).mockReturnValue(null); | ||
(getEnsInfo as jest.Mock).mockResolvedValue({ | ||
ens: 'ensName', | ||
avatarUrl: 'ensAvatarUrl', | ||
}); | ||
|
||
const {result, waitForNextUpdate} = renderHook(() => | ||
useContactInfo(address), | ||
); | ||
|
||
await act(async () => { | ||
await waitForNextUpdate(); | ||
}); | ||
|
||
expect(getEnsInfo).toHaveBeenCalledWith(address); | ||
expect(mmkvStorage.saveEnsName).toHaveBeenCalledWith(address, 'ensName'); | ||
expect(mmkvStorage.saveEnsAvatar).toHaveBeenCalledWith( | ||
address, | ||
'ensAvatarUrl', | ||
); | ||
expect(result.current).toEqual({ | ||
displayName: 'ensName', | ||
avatarUrl: 'ensAvatarUrl', | ||
loading: false, | ||
}); | ||
}); | ||
|
||
test('should handle error when fetching ENS info', async () => { | ||
(mmkvStorage.getEnsName as jest.Mock).mockReturnValue(null); | ||
(mmkvStorage.getEnsAvatar as jest.Mock).mockReturnValue(null); | ||
(getEnsInfo as jest.Mock).mockRejectedValue( | ||
new Error('Failed to fetch ENS info'), | ||
); | ||
|
||
const {result, waitForNextUpdate} = renderHook(() => | ||
useContactInfo(address), | ||
); | ||
|
||
await act(async () => { | ||
await waitForNextUpdate(); | ||
}); | ||
|
||
expect(getEnsInfo).toHaveBeenCalledWith(address); | ||
expect(result.current).toEqual({ | ||
displayName: `Formatted: ${address}`, | ||
avatarUrl: null, | ||
loading: false, | ||
}); | ||
}); | ||
|
||
test('should save displayName to mmkvStorage when displayName changes', async () => { | ||
(mmkvStorage.getEnsName as jest.Mock).mockReturnValue(null); | ||
(mmkvStorage.getEnsAvatar as jest.Mock).mockReturnValue(null); | ||
(getEnsInfo as jest.Mock).mockResolvedValue({ | ||
ens: 'ensName', | ||
avatarUrl: 'ensAvatarUrl', | ||
}); | ||
|
||
const {waitForNextUpdate} = renderHook(() => useContactInfo(address)); | ||
|
||
await act(async () => { | ||
await waitForNextUpdate(); | ||
}); | ||
|
||
expect(mmkvStorage.saveEnsName).toHaveBeenCalledWith(address, 'ensName'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import {act, renderHook} from '@testing-library/react-hooks'; | ||
import {useDebounce} from './useDebounce'; | ||
|
||
jest.useFakeTimers(); | ||
|
||
describe('useDebounce', () => { | ||
test('should return the initial value immediately', () => { | ||
const {result} = renderHook(() => useDebounce('initial', 500)); | ||
expect(result.current).toBe('initial'); | ||
}); | ||
|
||
test('should debounce the value after the delay', () => { | ||
const {result, rerender} = renderHook( | ||
({value, delay}) => useDebounce(value, delay), | ||
{ | ||
initialProps: {value: 'initial', delay: 500}, | ||
}, | ||
); | ||
|
||
rerender({value: 'new value', delay: 500}); | ||
|
||
// Initially, the value should not be updated | ||
expect(result.current).toBe('initial'); | ||
|
||
// Fast forward the timers by 500ms | ||
act(() => { | ||
jest.advanceTimersByTime(500); | ||
}); | ||
|
||
// Now, the debounced value should be updated | ||
expect(result.current).toBe('new value'); | ||
}); | ||
|
||
test('should update debounced value only after the specified delay', () => { | ||
const {result, rerender} = renderHook( | ||
({value, delay}) => useDebounce(value, delay), | ||
{ | ||
initialProps: {value: 'initial', delay: 500}, | ||
}, | ||
); | ||
|
||
rerender({value: 'new value', delay: 500}); | ||
|
||
// Fast forward the timers by less than the delay time | ||
act(() => { | ||
jest.advanceTimersByTime(300); | ||
}); | ||
|
||
// Value should not have updated yet | ||
expect(result.current).toBe('initial'); | ||
|
||
// Fast forward the remaining time | ||
act(() => { | ||
jest.advanceTimersByTime(200); | ||
}); | ||
|
||
// Now, the debounced value should be updated | ||
expect(result.current).toBe('new value'); | ||
}); | ||
|
||
test('should reset debounce timer if value changes within delay period', () => { | ||
const {result, rerender} = renderHook( | ||
({value, delay}) => useDebounce(value, delay), | ||
{ | ||
initialProps: {value: 'initial', delay: 500}, | ||
}, | ||
); | ||
|
||
rerender({value: 'new value', delay: 500}); | ||
|
||
// Fast forward the timers by 300ms | ||
act(() => { | ||
jest.advanceTimersByTime(300); | ||
}); | ||
|
||
// Value should not have updated yet | ||
expect(result.current).toBe('initial'); | ||
|
||
// Change the value again before the delay period completes | ||
rerender({value: 'another value', delay: 500}); | ||
|
||
// Fast forward the timers by 300ms | ||
act(() => { | ||
jest.advanceTimersByTime(300); | ||
}); | ||
|
||
// Value should still not have updated because the timer was reset | ||
expect(result.current).toBe('initial'); | ||
|
||
// Fast forward the remaining time | ||
act(() => { | ||
jest.advanceTimersByTime(200); | ||
}); | ||
|
||
// Now, the debounced value should be updated to the latest value | ||
expect(result.current).toBe('another value'); | ||
}); | ||
}); |
Oops, something went wrong.