From 615d4039027a123c10979c590f9e23ae0d68aa2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=AD=82?= <365125264@qq.com> Date: Mon, 19 Aug 2024 14:53:01 +0800 Subject: [PATCH] fix(web3-wagmi): Ensure AntDesignWeb3ConfigProvider component renders correctly when Wagmi config ssr is true (#1099) --- .changeset/fifty-pans-tease.md | 5 ++ .../wagmi-provider/__tests__/balance.test.tsx | 35 ++++++-- .../__tests__/config-with-ssr-wallet.test.tsx | 76 ++++++++++++++++++ .../connect-with-universal-wallet.test.tsx | 70 +++++++++++----- .../wagmi-provider/__tests__/connect.test.tsx | 37 +++++++-- .../src/wagmi-provider/__tests__/ens.test.tsx | 31 +++++-- .../src/wagmi-provider/__tests__/nft.test.tsx | 18 ++++- .../switch-chain-unconnected.test.tsx | 21 ++++- .../__tests__/switch-chain.test.tsx | 19 ++++- .../__tests__/unconnected.test.tsx | 80 ------------------- .../src/wagmi-provider/config-provider.tsx | 27 +++---- packages/wagmi/src/wagmi-provider/index.tsx | 3 +- 12 files changed, 276 insertions(+), 146 deletions(-) create mode 100644 .changeset/fifty-pans-tease.md create mode 100644 packages/wagmi/src/wagmi-provider/__tests__/config-with-ssr-wallet.test.tsx delete mode 100644 packages/wagmi/src/wagmi-provider/__tests__/unconnected.test.tsx diff --git a/.changeset/fifty-pans-tease.md b/.changeset/fifty-pans-tease.md new file mode 100644 index 000000000..04bafff60 --- /dev/null +++ b/.changeset/fifty-pans-tease.md @@ -0,0 +1,5 @@ +--- +'@ant-design/web3-wagmi': patch +--- + +fix(web3-wagmi): Ensure AntDesignWeb3ConfigProvider component renders correctly when Wagmi config ssr is true diff --git a/packages/wagmi/src/wagmi-provider/__tests__/balance.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/balance.test.tsx index 74b49c7a7..472fe8e6e 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/balance.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/balance.test.tsx @@ -2,6 +2,7 @@ import { ConnectButton, Connector } from '@ant-design/web3'; import { Mainnet } from '@ant-design/web3-assets'; import { render } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; +import type * as Wagmi from 'wagmi'; import { mainnet } from 'wagmi/chains'; import { MetaMask } from '../../wallets'; @@ -16,8 +17,10 @@ vi.mock('wagmi/actions', () => ({ disconnect: () => {}, })); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, @@ -59,14 +62,23 @@ vi.mock('wagmi', () => { }); describe('WagmiWeb3ConfigProvider balance', () => { - it('show balance', () => { + it('show balance', async () => { + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + connectors: [], + }); + const App = () => ( @@ -80,13 +92,22 @@ describe('WagmiWeb3ConfigProvider balance', () => { expect(baseElement.querySelector('.ant-web3-icon-ethereum-filled')).toBeTruthy(); }); - it('show address', () => { + it('show address', async () => { + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + connectors: [], + }); + const App = () => ( diff --git a/packages/wagmi/src/wagmi-provider/__tests__/config-with-ssr-wallet.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/config-with-ssr-wallet.test.tsx new file mode 100644 index 000000000..592b6925b --- /dev/null +++ b/packages/wagmi/src/wagmi-provider/__tests__/config-with-ssr-wallet.test.tsx @@ -0,0 +1,76 @@ +import { useProvider } from '@ant-design/web3'; +import { Mainnet } from '@ant-design/web3-assets'; +import { MetaMask, WagmiWeb3ConfigProvider } from '@ant-design/web3-wagmi'; +import { QueryClient } from '@tanstack/react-query'; +import { render } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; +import type * as Wagmi from 'wagmi'; +import { mainnet } from 'wagmi/chains'; +import { injected } from 'wagmi/connectors'; + +// Mock necessary modules +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + useConfig: () => ({ ssr: true }), + useAccount: () => ({ isDisconnected: true }), + useConnect: () => ({ connectAsync: vi.fn() }), + useSwitchChain: () => ({ switchChain: vi.fn() }), + useBalance: () => ({ data: {} }), + useEnsName: () => ({ data: null }), + useEnsAvatar: () => ({ data: null }), + }; +}); + +describe('WagmiWeb3ConfigProvider with SSR', () => { + it('should render correctly in SSR environment', async () => { + const { createConfig, http } = await import('wagmi'); + + const mockConfig = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + connectors: [ + injected({ + target: 'metaMask', + }), + ], + ssr: true, + }); + + const CustomConnector: React.FC = () => { + const { availableWallets, availableChains } = useProvider(); + return ( +
+
{availableWallets?.length}
+
{availableChains?.length}
+
{availableWallets![0]?.name}
+
{availableChains![0]?.id}
+
+ ); + }; + + const App = () => ( + + + + ); + const { getByTestId } = render(); + + // Check if the provider renders without errors + expect(getByTestId('wallets')).toBeDefined(); + expect(getByTestId('chains')).toBeDefined(); + + // Verify that wallets and chains are available + expect(getByTestId('wallets').textContent).toBe('1'); + expect(getByTestId('chains').textContent).toBe('1'); + + // Verify that the first wallet is MetaMask + expect(getByTestId('firstWalletName').textContent).toBe('MetaMask'); + + // Verify that the first chain is Ethereum Mainnet (chainId: 1) + expect(getByTestId('firstChainId').textContent).toBe('1'); + }); +}); diff --git a/packages/wagmi/src/wagmi-provider/__tests__/connect-with-universal-wallet.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/connect-with-universal-wallet.test.tsx index 6039f4a51..59c621914 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/connect-with-universal-wallet.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/connect-with-universal-wallet.test.tsx @@ -4,7 +4,8 @@ import { useProvider } from '@ant-design/web3'; import { Mainnet } from '@ant-design/web3-assets'; import { fireEvent, render } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import type { Connector } from 'wagmi'; +import type { Connector, Config as WagmiConfig } from 'wagmi'; +import type * as Wagmi from 'wagmi'; import { mainnet } from 'wagmi/chains'; import { TokenPocket } from '../../wallets'; @@ -32,12 +33,13 @@ vi.mock('wagmi/actions', () => ({ }, })); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, - // https://wagmi.sh/react/hooks/useAccount useAccount: () => { const [connected, setConnected] = React.useState(false); useEffect(() => { @@ -58,7 +60,7 @@ vi.mock('wagmi', () => { connectAsync: (options: any) => { connectAsync(options); event.emit('connectChanged', true); - return {}; + return { accounts: ['0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B'] }; }, }; }, @@ -76,10 +78,10 @@ vi.mock('wagmi', () => { }; }, useBalance: () => { - return {}; + return { data: {} }; }, - useEnsName: () => ({}), - useEnsAvatar: () => ({}), + useEnsName: () => ({ data: null }), + useEnsAvatar: () => ({ data: null }), }; }); @@ -105,12 +107,23 @@ describe('WagmiWeb3ConfigProvider connect with UniversalWallet', () => { ); }; + const { createConfig, http } = await import('wagmi'); + + const mockWagmiConfig: WagmiConfig = { + ...createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + }), + connectors: [walletConnetor, injectConnector], + }; + const App = () => ( @@ -119,10 +132,12 @@ describe('WagmiWeb3ConfigProvider connect with UniversalWallet', () => { expect(baseElement.querySelector('.custom-text')?.textContent).toBe('Connect'); fireEvent.click(baseElement.querySelector('.custom-text')!); await vi.waitFor(() => { - expect(connectAsync).toBeCalledWith({ - chainId: 1, - connector: walletConnetor, - }); + expect(connectAsync).toBeCalledWith( + expect.objectContaining({ + chainId: 1, + connector: walletConnetor, + }), + ); expect(baseElement.querySelector('.custom-text')?.textContent).toBe( '0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B', @@ -161,12 +176,23 @@ describe('WagmiWeb3ConfigProvider connect with UniversalWallet', () => { ); }; + const { createConfig, http } = await import('wagmi'); + + const mockWagmiConfig: WagmiConfig = { + ...createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + }), + connectors: [walletConnetor, injectConnector], + }; + const App = () => ( @@ -175,10 +201,12 @@ describe('WagmiWeb3ConfigProvider connect with UniversalWallet', () => { expect(baseElement.querySelector('.custom-text')?.textContent).toBe('Connect'); fireEvent.click(baseElement.querySelector('.custom-text')!); await vi.waitFor(() => { - expect(connectAsync).toBeCalledWith({ - chainId: 1, - connector: injectConnector, - }); + expect(connectAsync).toBeCalledWith( + expect.objectContaining({ + chainId: 1, + connector: injectConnector, + }), + ); expect(baseElement.querySelector('.custom-text')?.textContent).toBe( '0x21CDf0974d53a6e96eF05d7B324a9803735fFd3B', diff --git a/packages/wagmi/src/wagmi-provider/__tests__/connect.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/connect.test.tsx index efcfcc1ec..364f28698 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/connect.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/connect.test.tsx @@ -4,7 +4,8 @@ import { useProvider } from '@ant-design/web3'; import { Mainnet } from '@ant-design/web3-assets'; import { fireEvent, render } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; -import type { Connector } from 'wagmi'; +import type { Connector, Config as WagmiConfig } from 'wagmi'; +import type * as Wagmi from 'wagmi'; import { mainnet } from 'wagmi/chains'; import { MetaMask } from '../../wallets'; @@ -27,8 +28,10 @@ vi.mock('wagmi/actions', () => ({ }, })); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, @@ -107,12 +110,23 @@ describe('WagmiWeb3ConfigProvider connect', () => { ); }; + const { createConfig, http } = await import('wagmi'); + + const mockWagmiConfig: WagmiConfig = { + ...createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + }), + connectors: [mockConnector], + }; + const App = () => ( @@ -166,12 +180,23 @@ describe('WagmiWeb3ConfigProvider connect', () => { ); }; + const { createConfig, http } = await import('wagmi'); + + const mockWagmiConfig: WagmiConfig = { + ...createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + }), + connectors: [mockConnector], + }; + const App = () => ( diff --git a/packages/wagmi/src/wagmi-provider/__tests__/ens.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/ens.test.tsx index 78277ac72..41a59ed94 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/ens.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/ens.test.tsx @@ -2,7 +2,9 @@ import { useProvider } from '@ant-design/web3'; import { Mainnet } from '@ant-design/web3-assets'; import { render, waitFor } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; +import type * as Wagmi from 'wagmi'; import { mainnet } from 'wagmi/chains'; +import type { Chain as WagmiChain } from 'wagmi/chains'; import { MetaMask } from '../../wallets'; import { AntDesignWeb3ConfigProvider } from '../config-provider'; @@ -14,8 +16,10 @@ vi.mock('wagmi/actions', () => { }; }); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, @@ -72,14 +76,22 @@ describe('WagmiWeb3ConfigProvider ens', () => { ); }; + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + connectors: [], + }); const App = () => ( @@ -101,13 +113,22 @@ describe('WagmiWeb3ConfigProvider ens', () => { ); }; + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + connectors: [], + }); + const App = () => ( diff --git a/packages/wagmi/src/wagmi-provider/__tests__/nft.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/nft.test.tsx index 276c59122..1bd4e4eda 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/nft.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/nft.test.tsx @@ -2,6 +2,7 @@ import { NFTCard } from '@ant-design/web3'; import { Mainnet } from '@ant-design/web3-assets'; import { render, waitFor } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; +import type * as Wagmi from 'wagmi'; import { mainnet } from 'wagmi/chains'; import { MetaMask } from '../../wallets'; @@ -15,8 +16,10 @@ vi.mock('wagmi/actions', () => ({ disconnect: () => {}, })); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, @@ -68,12 +71,21 @@ describe('WagmiWeb3ConfigProvider getMetadata', () => { } as any); }); + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet], + transports: { + [mainnet.id]: http(), + }, + connectors: [], + }); + const App = () => ( diff --git a/packages/wagmi/src/wagmi-provider/__tests__/switch-chain-unconnected.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/switch-chain-unconnected.test.tsx index 8138ab81d..a008a84a2 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/switch-chain-unconnected.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/switch-chain-unconnected.test.tsx @@ -2,6 +2,7 @@ import { ConnectButton, Connector } from '@ant-design/web3'; import { Mainnet, Polygon } from '@ant-design/web3-assets'; import { fireEvent, render } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; +import type * as Wagmi from 'wagmi'; import { mainnet, polygon } from 'wagmi/chains'; import { MetaMask } from '../../wallets'; @@ -16,8 +17,10 @@ vi.mock('wagmi/actions', () => ({ disconnect: () => {}, })); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, @@ -59,13 +62,23 @@ vi.mock('wagmi', () => { }); describe('switch chain when not connected', () => { - it('switch chain when not connected', () => { + it('switch chain when not connected', async () => { + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet, polygon], + transports: { + [mainnet.id]: http(), + [polygon.id]: http(), + }, + connectors: [], + }); + const App = () => ( diff --git a/packages/wagmi/src/wagmi-provider/__tests__/switch-chain.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/switch-chain.test.tsx index 79dbafd5e..19ccfcd9b 100644 --- a/packages/wagmi/src/wagmi-provider/__tests__/switch-chain.test.tsx +++ b/packages/wagmi/src/wagmi-provider/__tests__/switch-chain.test.tsx @@ -4,6 +4,7 @@ import { ConnectButton, Connector } from '@ant-design/web3'; import { Mainnet, Polygon } from '@ant-design/web3-assets'; import { fireEvent, render } from '@testing-library/react'; import { describe, expect, it, vi } from 'vitest'; +import type * as Wagmi from 'wagmi'; import type { Chain as WagmiChain } from 'wagmi/chains'; import { mainnet, polygon } from 'wagmi/chains'; @@ -21,8 +22,10 @@ vi.mock('wagmi/actions', () => ({ disconnect: () => {}, })); -vi.mock('wagmi', () => { +vi.mock('wagmi', async (importOriginal) => { + const actual = await importOriginal(); return { + ...actual, useConfig: () => { return {}; }, @@ -76,12 +79,22 @@ vi.mock('wagmi', () => { describe('switch chain when connect', () => { it('switch chain when connect', async () => { + const { createConfig, http } = await import('wagmi'); + + const config = createConfig({ + chains: [mainnet, polygon], + transports: { + [mainnet.id]: http(), + [polygon.id]: http(), + }, + connectors: [], + }); + const App = () => ( diff --git a/packages/wagmi/src/wagmi-provider/__tests__/unconnected.test.tsx b/packages/wagmi/src/wagmi-provider/__tests__/unconnected.test.tsx deleted file mode 100644 index 7c695e651..000000000 --- a/packages/wagmi/src/wagmi-provider/__tests__/unconnected.test.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { ConnectButton, Connector } from '@ant-design/web3'; -import { Mainnet } from '@ant-design/web3-assets'; -import { fireEvent, render } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; - -import { MetaMask } from '../../wallets'; -import { AntDesignWeb3ConfigProvider } from '../config-provider'; - -const mockConnector = { - name: 'MetaMask', -}; - -vi.mock('wagmi/actions', () => ({ - getAccount: () => ({}), - disconnect: () => {}, -})); - -vi.mock('wagmi', () => { - return { - useConfig: () => { - return {}; - }, - // https://wagmi.sh/react/hooks/useAccount - useAccount: () => { - return { - chain: undefined, - address: undefined, - connector: mockConnector, - }; - }, - useConnect: () => { - return { - connectors: [mockConnector], - connectAsync: async () => { - return {}; - }, - }; - }, - useDisconnect: () => { - return { - disconnectAsync: () => {}, - }; - }, - useSwitchChain: () => { - return { - switchChain: () => {}, - }; - }, - useBalance: () => { - return {}; - }, - useEnsName: () => ({}), - useEnsAvatar: () => ({}), - }; -}); - -describe('WagmiWeb3ConfigProvider unconnected', () => { - it('when currentWagmiChain is undefined', async () => { - const App = () => ( - - - - - - ); - const { baseElement } = render(); - fireEvent.click(baseElement.querySelector('.ant-web3-connect-button-text')!); - await vi.waitFor(() => { - expect(baseElement.querySelector('.ant-web3-connect-button-text')?.textContent).toBe( - 'Connect Wallet', - ); - expect(baseElement.querySelector('.ant-web3-connect-button-chain-select')).toBeNull(); - }); - }); -}); diff --git a/packages/wagmi/src/wagmi-provider/config-provider.tsx b/packages/wagmi/src/wagmi-provider/config-provider.tsx index ed01cb3b6..a27962623 100644 --- a/packages/wagmi/src/wagmi-provider/config-provider.tsx +++ b/packages/wagmi/src/wagmi-provider/config-provider.tsx @@ -6,7 +6,7 @@ import { type Locale, type Wallet, } from '@ant-design/web3-common'; -import type { Chain as WagmiChain } from 'viem'; +import type { Config as WagmiConfig } from 'wagmi'; import { useAccount, useBalance, @@ -32,8 +32,7 @@ export interface AntDesignWeb3ConfigProviderProps { ens?: boolean; balance?: boolean; eip6963?: EIP6963Config; - readonly availableChains: readonly WagmiChain[]; - readonly availableConnectors: readonly WagmiConnector[]; + wagimConfig: WagmiConfig; } export const AntDesignWeb3ConfigProvider: React.FC = (props) => { @@ -41,12 +40,11 @@ export const AntDesignWeb3ConfigProvider: React.FC { - const commonConnector = availableConnectors.find( + const commonConnector = wagimConfig.connectors.find( (item) => item.name === name && !isEIP6963Connector(item), ); if (!eip6963) { return commonConnector; } - const eip6963Connector = availableConnectors.find( + const eip6963Connector = wagimConfig.connectors.find( (item) => item.name === name && isEIP6963Connector(item), ); return eip6963Connector || commonConnector; @@ -79,8 +77,7 @@ export const AntDesignWeb3ConfigProvider: React.FC { const autoAddEIP6963Wallets: Wallet[] = []; - - availableConnectors.forEach((connector) => { + wagimConfig.connectors.forEach((connector) => { if (isEIP6963Connector(connector)) { // check is need auto add eip6963 wallet if ( @@ -128,10 +125,10 @@ export const AntDesignWeb3ConfigProvider: React.FC item !== null) as Wallet[]; return [...supportWallets, ...autoAddEIP6963Wallets]; - }, [availableConnectors, walletFactorys, eip6963]); + }, [wagimConfig.connectors, walletFactorys, eip6963]); const chainList: Chain[] = React.useMemo(() => { - return availableChains + return wagimConfig.chains .map((item) => { const c = chainAssets?.find((asset) => { return asset.id === item.id; @@ -149,10 +146,10 @@ export const AntDesignWeb3ConfigProvider: React.FC item !== null) as Chain[]; - }, [availableChains, chainAssets]); + }, [wagimConfig.chains, chainAssets]); - const chainId = chain?.id || availableChains?.[0]?.id; - const chainName = chain?.name || availableChains?.[0]?.name; + const chainId = chain?.id || wagimConfig.chains?.[0]?.id; + const chainName = chain?.name || wagimConfig.chains?.[0]?.name; const [currentChain, setCurrentChain] = React.useState(undefined); React.useEffect(() => { @@ -164,7 +161,7 @@ export const AntDesignWeb3ConfigProvider: React.FC {children}