-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
119 additions
and
34 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,40 +1,119 @@ | ||
import '@testing-library/jest-dom'; | ||
import { render, screen, waitFor } from '@testing-library/react'; | ||
import { useAccount, useConnect, useDisconnect } from 'wagmi'; | ||
import { fireEvent, render, screen } from '@testing-library/react'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { ConnectWallet } from './ConnectWallet'; | ||
import { Wallet } from './Wallet'; | ||
import { WalletDropdown } from './WalletDropdown'; | ||
import { useWalletContext } from './WalletProvider'; | ||
|
||
vi.mock('wagmi', () => ({ | ||
useAccount: vi.fn(), | ||
useConnect: vi.fn(), | ||
useDisconnect: vi.fn(), | ||
vi.mock('./WalletProvider', () => ({ | ||
useWalletContext: vi.fn(), | ||
WalletProvider: ({ children }) => <>{children}</>, | ||
})); | ||
|
||
vi.mock('./ConnectWallet', () => ({ | ||
ConnectWallet: () => <div data-testid="connect-wallet">Connect Wallet</div>, | ||
})); | ||
|
||
vi.mock('./WalletDropdown', () => ({ | ||
WalletDropdown: () => ( | ||
<div data-testid="wallet-dropdown">Wallet Dropdown</div> | ||
), | ||
})); | ||
|
||
describe('Wallet Component', () => { | ||
let mockSetIsOpen: ReturnType<typeof vi.fn>; | ||
|
||
beforeEach(() => { | ||
vi.clearAllMocks(); | ||
(useAccount as vi.Mock).mockReturnValue({ status: 'disconnected' }); | ||
(useConnect as vi.Mock).mockReturnValue({ | ||
connectors: [{ name: 'injected' }], | ||
connect: vi.fn(), | ||
mockSetIsOpen = vi.fn(); | ||
(useWalletContext as ReturnType<typeof vi.fn>).mockReturnValue({ | ||
isOpen: false, | ||
setIsOpen: mockSetIsOpen, | ||
}); | ||
(useDisconnect as vi.Mock).mockReturnValue({ disconnect: vi.fn() }); | ||
}); | ||
|
||
it('should render the Wallet component with ConnectWallet', async () => { | ||
it('should render the Wallet component with ConnectWallet', () => { | ||
render( | ||
<Wallet> | ||
<ConnectWallet /> | ||
<WalletDropdown> | ||
<div /> | ||
</WalletDropdown> | ||
<WalletDropdown /> | ||
</Wallet>, | ||
); | ||
await waitFor(() => { | ||
expect( | ||
screen.getByTestId('ockConnectWallet_Container'), | ||
).toBeInTheDocument(); | ||
|
||
expect(screen.getByTestId('connect-wallet')).toBeDefined(); | ||
expect(screen.queryByTestId('wallet-dropdown')).toBeNull(); | ||
}); | ||
|
||
it('should close the wallet when clicking outside', () => { | ||
(useWalletContext as ReturnType<typeof vi.fn>).mockReturnValue({ | ||
isOpen: true, | ||
setIsOpen: mockSetIsOpen, | ||
}); | ||
|
||
render( | ||
<Wallet> | ||
<ConnectWallet /> | ||
<WalletDropdown /> | ||
</Wallet>, | ||
); | ||
|
||
expect(screen.getByTestId('wallet-dropdown')).toBeDefined(); | ||
|
||
fireEvent.click(document.body); | ||
|
||
expect(mockSetIsOpen).toHaveBeenCalledWith(false); | ||
}); | ||
|
||
it('should not close the wallet when clicking inside', () => { | ||
(useWalletContext as ReturnType<typeof vi.fn>).mockReturnValue({ | ||
isOpen: true, | ||
setIsOpen: mockSetIsOpen, | ||
}); | ||
|
||
render( | ||
<Wallet> | ||
<ConnectWallet /> | ||
<WalletDropdown /> | ||
</Wallet>, | ||
); | ||
|
||
const walletDropdown = screen.getByTestId('wallet-dropdown'); | ||
expect(walletDropdown).toBeDefined(); | ||
|
||
fireEvent.click(walletDropdown); | ||
|
||
expect(mockSetIsOpen).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should not trigger click handler when wallet is closed', () => { | ||
render( | ||
<Wallet> | ||
<ConnectWallet /> | ||
<WalletDropdown /> | ||
</Wallet>, | ||
); | ||
|
||
expect(screen.queryByTestId('wallet-dropdown')).toBeNull(); | ||
|
||
fireEvent.click(document.body); | ||
|
||
expect(mockSetIsOpen).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it('should remove event listener on unmount', () => { | ||
const removeEventListenerSpy = vi.spyOn(document, 'removeEventListener'); | ||
|
||
const { unmount } = render( | ||
<Wallet> | ||
<ConnectWallet /> | ||
<WalletDropdown /> | ||
</Wallet>, | ||
); | ||
|
||
unmount(); | ||
|
||
expect(removeEventListenerSpy).toHaveBeenCalledWith( | ||
'click', | ||
expect.any(Function), | ||
); | ||
}); | ||
}); |
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 |
---|---|---|
@@ -1,46 +1,52 @@ | ||
import { Children, useMemo, useRef, useEffect } from 'react'; | ||
import { Children, useEffect, useMemo, useRef } from 'react'; | ||
import type { WalletReact } from '../types'; | ||
import { ConnectWallet } from './ConnectWallet'; | ||
import { WalletDropdown } from './WalletDropdown'; | ||
import { WalletProvider, useWalletContext } from './WalletProvider'; | ||
|
||
function WalletContent({ children }: WalletReact) { | ||
const WalletContent = ({ children }: WalletReact) => { | ||
const { isOpen, setIsOpen } = useWalletContext(); | ||
const containerRef = useRef<HTMLDivElement>(null); | ||
const walletContainerRef = useRef<HTMLDivElement>(null); | ||
|
||
const { connect, dropdown } = useMemo(() => { | ||
const childrenArray = Children.toArray(children); | ||
return { | ||
// @ts-ignore | ||
connect: childrenArray.filter(({ type }) => type === ConnectWallet), | ||
// @ts-ignore | ||
dropdown: childrenArray.filter(({ type }) => type === WalletDropdown), | ||
}; | ||
}, [children]); | ||
|
||
// Handle clicking outside the wallet component to close the dropdown. | ||
useEffect(() => { | ||
const handleClickOutside = (event: MouseEvent) => { | ||
if (containerRef.current && !containerRef.current.contains(event.target as Node) && isOpen) { | ||
const handleClickOutsideComponent = (event: MouseEvent) => { | ||
if ( | ||
walletContainerRef.current && | ||
!walletContainerRef.current.contains(event.target as Node) && | ||
isOpen | ||
) { | ||
setIsOpen(false); | ||
} | ||
}; | ||
|
||
document.addEventListener('mousedown', handleClickOutside); | ||
return () => { | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
document.addEventListener('click', handleClickOutsideComponent); | ||
return () => | ||
document.removeEventListener('click', handleClickOutsideComponent); | ||
}, [isOpen, setIsOpen]); | ||
|
||
return ( | ||
<div ref={containerRef} className="relative w-fit shrink-0"> | ||
<div ref={walletContainerRef} className="relative w-fit shrink-0"> | ||
{connect} | ||
{isOpen && dropdown} | ||
</div> | ||
); | ||
} | ||
}; | ||
|
||
export function Wallet({ children }: WalletReact) { | ||
export const Wallet = ({ children }: WalletReact) => { | ||
return ( | ||
<WalletProvider> | ||
<WalletContent>{children}</WalletContent> | ||
</WalletProvider> | ||
); | ||
} | ||
}; |