diff --git a/apps/laboratory/src/components/AppKitHooks.tsx b/apps/laboratory/src/components/AppKitHooks.tsx index e259704d4e..911e7d9926 100644 --- a/apps/laboratory/src/components/AppKitHooks.tsx +++ b/apps/laboratory/src/components/AppKitHooks.tsx @@ -1,7 +1,35 @@ import { Box, Button, Heading } from '@chakra-ui/react' -import { type AppKitNetwork, mainnet, polygon, solana, solanaTestnet } from '@reown/appkit/networks' -import { useAppKit, useAppKitAccount, useAppKitNetwork, useDisconnect } from '@reown/appkit/react' +import { + bitcoin, + bitcoinTestnet, + mainnet, + polygon, + solana, + solanaTestnet +} from '@reown/appkit/networks' +import { + type CaipNetwork, + useAppKit, + useAppKitAccount, + useAppKitNetwork, + useDisconnect +} from '@reown/appkit/react' + +function getNetworkToSwitch(activeNetwork: CaipNetwork | undefined) { + if (!activeNetwork) { + return mainnet + } + + switch (activeNetwork.chainNamespace) { + case 'bip122': + return activeNetwork.id === bitcoin.id ? bitcoinTestnet : bitcoin + case 'solana': + return activeNetwork.id === solana.id ? solanaTestnet : solana + default: + return activeNetwork.id === polygon.id ? mainnet : polygon + } +} export function AppKitHooks() { const { open } = useAppKit() @@ -10,15 +38,11 @@ export function AppKitHooks() { const { disconnect } = useDisconnect() function handleSwitchNetwork() { - const isEIPNamespace = caipNetwork?.chainNamespace === 'eip155' - // eslint-disable-next-line no-nested-ternary - const networkToSwitch: AppKitNetwork = isEIPNamespace - ? caipNetwork?.id === polygon.id - ? mainnet - : polygon - : caipNetwork?.id === solana.id - ? solanaTestnet - : solana + const networkToSwitch = getNetworkToSwitch(caipNetwork) + + if (!networkToSwitch) { + return + } switchNetwork(networkToSwitch) } diff --git a/apps/laboratory/src/components/Bitcoin/BitcoinSendTransferTest.tsx b/apps/laboratory/src/components/Bitcoin/BitcoinSendTransferTest.tsx index 4614e2f703..7b6a5e31dd 100644 --- a/apps/laboratory/src/components/Bitcoin/BitcoinSendTransferTest.tsx +++ b/apps/laboratory/src/components/Bitcoin/BitcoinSendTransferTest.tsx @@ -1,15 +1,17 @@ import { useState } from 'react' -import { Box, Button, Input, InputGroup, InputLeftAddon, useToast } from '@chakra-ui/react' +import { Box, Button, Input, InputGroup, InputLeftAddon } from '@chakra-ui/react' import type { BitcoinConnector } from '@reown/appkit-adapter-bitcoin' import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react' +import { useChakraToast } from '@/src/components/Toast' + export function BitcoinSendTransferTest() { const { walletProvider } = useAppKitProvider('bip122') const { address } = useAppKitAccount({ namespace: 'bip122' }) - const toast = useToast() + const toast = useChakraToast() const [loading, setLoading] = useState(false) const [recipient, setRecipient] = useState(address || '') const [amount, setAmount] = useState('1500') @@ -18,8 +20,8 @@ export function BitcoinSendTransferTest() { if (!walletProvider) { toast({ title: 'No wallet provider', - status: 'error', - isClosable: true + description: 'Please connect your wallet', + type: 'error' }) } @@ -32,11 +34,11 @@ export function BitcoinSendTransferTest() { toast({ title: `Transfer sent: ${signature}`, - status: 'success', - isClosable: true + description: 'Transfer sent successfully', + type: 'success' }) } catch (error) { - toast({ title: 'Error', description: (error as Error).message, status: 'error' }) + toast({ title: 'Error', description: (error as Error).message, type: 'error' }) } finally { setLoading(false) } diff --git a/apps/laboratory/src/components/Bitcoin/BitcoinSignMessageTest.tsx b/apps/laboratory/src/components/Bitcoin/BitcoinSignMessageTest.tsx index a5c329572b..213a1c6064 100644 --- a/apps/laboratory/src/components/Bitcoin/BitcoinSignMessageTest.tsx +++ b/apps/laboratory/src/components/Bitcoin/BitcoinSignMessageTest.tsx @@ -1,15 +1,18 @@ import { useState } from 'react' -import { Box, Button, Input, InputGroup, InputLeftAddon, useToast } from '@chakra-ui/react' +import { Box, Button, Input, InputGroup, InputLeftAddon } from '@chakra-ui/react' import type { BitcoinConnector } from '@reown/appkit-adapter-bitcoin' import { useAppKitAccount, useAppKitProvider } from '@reown/appkit/react' +import { useChakraToast } from '@/src/components/Toast' +import { ConstantsUtil } from '@/src/utils/ConstantsUtil' + export function BitcoinSignMessageTest() { + const toast = useChakraToast() const { walletProvider } = useAppKitProvider('bip122') const { address } = useAppKitAccount({ namespace: 'bip122' }) - const toast = useToast() const [loading, setLoading] = useState(false) const [message, setMessage] = useState('Hello, World!') @@ -17,8 +20,8 @@ export function BitcoinSignMessageTest() { if (!walletProvider || !address) { toast({ title: 'No connection detected', - status: 'error', - isClosable: true + description: 'Please connect your wallet', + type: 'error' }) return @@ -31,9 +34,17 @@ export function BitcoinSignMessageTest() { address, message }) - toast({ title: 'Signature', description: signature, status: 'success' }) + toast({ + title: ConstantsUtil.SigningSucceededToastTitle, + description: signature, + type: 'success' + }) } catch (error) { - toast({ title: 'Error', description: (error as Error).message, status: 'error' }) + toast({ + title: ConstantsUtil.SigningFailedToastTitle, + description: 'Failed to sign message', + type: 'error' + }) } finally { setLoading(false) } diff --git a/apps/laboratory/src/components/Bitcoin/BitcoinSignPSBTTest.tsx b/apps/laboratory/src/components/Bitcoin/BitcoinSignPSBTTest.tsx index 99854383b7..f5832073b4 100644 --- a/apps/laboratory/src/components/Bitcoin/BitcoinSignPSBTTest.tsx +++ b/apps/laboratory/src/components/Bitcoin/BitcoinSignPSBTTest.tsx @@ -1,19 +1,11 @@ import { useState } from 'react' -import { - Box, - Button, - Checkbox, - Flex, - Input, - InputGroup, - InputLeftAddon, - useToast -} from '@chakra-ui/react' +import { Box, Button, Checkbox, Flex, Input, InputGroup, InputLeftAddon } from '@chakra-ui/react' import type { BitcoinConnector } from '@reown/appkit-adapter-bitcoin' import { useAppKitAccount, useAppKitNetwork, useAppKitProvider } from '@reown/appkit/react' +import { useChakraToast } from '@/src/components/Toast' import { BitcoinUtil } from '@/src/utils/BitcoinUtil' export function BitcoinSignPSBTTest() { @@ -21,7 +13,7 @@ export function BitcoinSignPSBTTest() { const { address } = useAppKitAccount({ namespace: 'bip122' }) const { caipNetwork } = useAppKitNetwork() - const toast = useToast() + const toast = useChakraToast() const [loading, setLoading] = useState(false) const [recipient, setRecipient] = useState(address || '') const [amount, setAmount] = useState('1500') @@ -31,8 +23,8 @@ export function BitcoinSignPSBTTest() { if (!walletProvider || !address || !caipNetwork) { toast({ title: 'No connection detected', - status: 'error', - isClosable: true + description: 'Please connect your wallet', + type: 'error' }) return @@ -41,8 +33,8 @@ export function BitcoinSignPSBTTest() { if (caipNetwork.chainNamespace !== 'bip122') { toast({ title: 'The selected chain is not bip122', - status: 'error', - isClosable: true + description: 'Please switch to a bip122 network', + type: 'error' }) return @@ -65,9 +57,9 @@ export function BitcoinSignPSBTTest() { params.broadcast = broadcast const signature = await walletProvider.signPSBT(params) - toast({ title: 'PSBT Signature', description: signature.psbt, status: 'success' }) + toast({ title: 'PSBT Signature', description: signature.psbt, type: 'success' }) } catch (error) { - toast({ title: 'Error', description: (error as Error).message, status: 'error' }) + toast({ title: 'Error', description: (error as Error).message, type: 'error' }) } finally { setLoading(false) } diff --git a/apps/laboratory/src/components/Bitcoin/BitcoinTests.tsx b/apps/laboratory/src/components/Bitcoin/BitcoinTests.tsx index 0aa9fa0ee0..e4bfeddb86 100644 --- a/apps/laboratory/src/components/Bitcoin/BitcoinTests.tsx +++ b/apps/laboratory/src/components/Bitcoin/BitcoinTests.tsx @@ -30,7 +30,7 @@ export function BitcoinTests() { return ( - Test Interactions + Bitcoin Test Interactions diff --git a/apps/laboratory/src/components/UPA/UpaTests.tsx b/apps/laboratory/src/components/UPA/UpaTests.tsx index bc345bd102..01333a5dee 100644 --- a/apps/laboratory/src/components/UPA/UpaTests.tsx +++ b/apps/laboratory/src/components/UPA/UpaTests.tsx @@ -6,7 +6,7 @@ export function UpaTests() { return ( - Test Interactions + UP Test Interactions } spacing="4"> diff --git a/apps/laboratory/src/components/Wagmi/WagmiPermissionsAsyncTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiPermissionsAsyncTest.tsx index 07528f28ae..bb454c5461 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiPermissionsAsyncTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiPermissionsAsyncTest.tsx @@ -13,7 +13,7 @@ export function WagmiPermissionsAsyncTest() { return ( - Test Interactions + Wagmi Test Interactions } spacing="4"> diff --git a/apps/laboratory/src/components/Wagmi/WagmiPermissionsSyncTest.tsx b/apps/laboratory/src/components/Wagmi/WagmiPermissionsSyncTest.tsx index cfe2e72896..ffdf8c6693 100644 --- a/apps/laboratory/src/components/Wagmi/WagmiPermissionsSyncTest.tsx +++ b/apps/laboratory/src/components/Wagmi/WagmiPermissionsSyncTest.tsx @@ -14,7 +14,7 @@ export function WagmiPermissionsSyncTest() { return ( - Test Interactions + Wagmi Test Interactions } spacing="4"> diff --git a/apps/laboratory/tests/shared/utils/project.ts b/apps/laboratory/tests/shared/utils/project.ts index bb29619884..78d21d52b4 100644 --- a/apps/laboratory/tests/shared/utils/project.ts +++ b/apps/laboratory/tests/shared/utils/project.ts @@ -2,7 +2,7 @@ import { devices } from '@playwright/test' import { DESKTOP_DEVICES, MOBILE_DEVICES } from '../constants/devices' -const LIBRARIES = ['ethers', 'ethers5', 'wagmi', 'solana'] as const +const LIBRARIES = ['ethers', 'ethers5', 'wagmi', 'solana', 'bitcoin'] as const const MULTICHAIN_LIBRARIES = [ 'multichain-basic', 'multichain-ethers-solana', @@ -61,6 +61,10 @@ const SINGLE_ADAPTER_EVM_TESTS = [ 'email-after-farcaster.spec.ts' ] +const BITCOIN_IGNORE_TESTS = SINGLE_ADAPTER_EVM_TESTS.filter( + test => !test.includes('wallet.spec.ts') +) + const SINGLE_ADAPTER_EVM_MOBILE_TESTS = ['mobile-wallet-features.spec.ts'] const SINGLE_ADAPTER_SOLANA_TESTS = [ @@ -85,7 +89,7 @@ function createRegex(tests: string[], isDesktop = true) { // Desktop const SINGLE_ADAPTER_EVM_TESTS_REGEX = createRegex(SINGLE_ADAPTER_EVM_TESTS) const SINGLE_ADAPTER_SOLANA_TESTS_REGEX = createRegex(SINGLE_ADAPTER_SOLANA_TESTS) - +const BITCOIN_IGNORE_TESTS_REGEX = createRegex(BITCOIN_IGNORE_TESTS) // Mobile const SINGLE_ADAPTER_EVM_MOBILE_REGEX = createRegex(SINGLE_ADAPTER_EVM_MOBILE_TESTS, false) const SINGLE_ADAPTER_SOLANA_MOBILE_TESTS_REGEX = createRegex( @@ -112,6 +116,12 @@ const customProjectProperties: CustomProjectProperties = { 'Desktop Firefox/wagmi': { testMatch: SINGLE_ADAPTER_EVM_TESTS_REGEX }, + 'Desktop Chrome/bitcoin': { + testIgnore: BITCOIN_IGNORE_TESTS_REGEX + }, + 'Desktop Firefox/bitcoin': { + testIgnore: BITCOIN_IGNORE_TESTS_REGEX + }, 'Desktop Chrome/solana': { testMatch: SINGLE_ADAPTER_SOLANA_TESTS_REGEX, testIgnore: /siwe-email\.spec\.ts|siwe-extension\.spec\.ts|multichain-.*\.spec\.ts/u @@ -203,6 +213,7 @@ export function getProjects() { const multichainProjects = MULTICHAIN_PERMUTATIONS.map(createProject) const projects = [...libraryDesktopProjects, ...libraryMobileProjects, ...multichainProjects] + console.log('projects', projects) return projects } diff --git a/apps/laboratory/tests/shared/validators/ModalValidator.ts b/apps/laboratory/tests/shared/validators/ModalValidator.ts index 1a9e399c7c..ce8ca75241 100644 --- a/apps/laboratory/tests/shared/validators/ModalValidator.ts +++ b/apps/laboratory/tests/shared/validators/ModalValidator.ts @@ -39,7 +39,7 @@ export class ModalValidator { }) } - async expectBalanceFetched(currency: 'SOL' | 'ETH') { + async expectBalanceFetched(currency: 'BTC' | 'SOL' | 'ETH') { const accountButton = this.page.locator('appkit-account-button') await expect(accountButton, `Account button should show balance as ${currency}`).toContainText( `0.000 ${currency}` @@ -307,9 +307,13 @@ export class ModalValidator { await expect(switchNetworkButton).toBeVisible() } - async expectOnrampButton() { + async expectOnrampButton(visible: boolean) { const onrampButton = this.page.getByTestId('w3m-account-default-onramp-button') - await expect(onrampButton).toBeVisible() + if (visible) { + await expect(onrampButton).toBeVisible() + } else { + await expect(onrampButton).not.toBeVisible() + } } async expectWalletGuide(_library: string, guide: 'get-started' | 'explore') { diff --git a/apps/laboratory/tests/shared/validators/WalletValidator.ts b/apps/laboratory/tests/shared/validators/WalletValidator.ts index ed27f3f945..98edbc0dd8 100644 --- a/apps/laboratory/tests/shared/validators/WalletValidator.ts +++ b/apps/laboratory/tests/shared/validators/WalletValidator.ts @@ -54,16 +54,18 @@ export class WalletValidator { }) } - async expectReceivedSign({ chainName = 'Ethereum' }) { + async expectReceivedSign({ chainName = 'Ethereum', expectNetworkName = true }) { await expect( this.page.getByTestId('session-approve-button'), 'Session approve button should be visible' ).toBeVisible({ timeout: MAX_WAIT }) - await expect( - this.page.getByTestId('request-details-chain'), - 'Request details should contain chain name' - ).toContainText(chainName) + if (expectNetworkName) { + await expect( + this.page.getByTestId('request-details-chain'), + 'Request details should contain chain name' + ).toContainText(chainName) + } } } diff --git a/apps/laboratory/tests/wallet.spec.ts b/apps/laboratory/tests/wallet.spec.ts index 4fbe14e36b..18e7bbf8bf 100644 --- a/apps/laboratory/tests/wallet.spec.ts +++ b/apps/laboratory/tests/wallet.spec.ts @@ -1,7 +1,14 @@ import { type BrowserContext, test } from '@playwright/test' import type { CaipNetworkId } from '@reown/appkit' -import { mainnet, polygon, solana, solanaTestnet } from '@reown/appkit/networks' +import { + bitcoin, + bitcoinTestnet, + mainnet, + polygon, + solana, + solanaTestnet +} from '@reown/appkit/networks' import { DEFAULT_CHAIN_NAME } from './shared/constants' import { ModalPage } from './shared/pages/ModalPage' @@ -17,6 +24,39 @@ let walletValidator: WalletValidator let context: BrowserContext /* eslint-enable init-declarations */ +function getBalanceSymbolByLibrary(library: string) { + switch (library) { + case 'bitcoin': + return 'BTC' + case 'solana': + return 'SOL' + default: + return 'ETH' + } +} + +function getNetworksByLibrary(library: string) { + switch (library) { + case 'bitcoin': + return [bitcoin, bitcoinTestnet] + case 'solana': + return [solana, solanaTestnet] + default: + return [mainnet, polygon] + } +} + +function getLastNetworkNameByLibrary(library: string) { + switch (library) { + case 'bitcoin': + return bitcoinTestnet.name + case 'solana': + return solanaTestnet.name + default: + return polygon.name + } +} + // -- Setup -------------------------------------------------------------------- const sampleWalletTest = test.extend<{ library: string }>({ library: ['wagmi', { option: true }] @@ -44,12 +84,16 @@ sampleWalletTest.afterAll(async () => { // -- Tests -------------------------------------------------------------------- sampleWalletTest('it should fetch balance as expected', async ({ library }) => { - await modalValidator.expectBalanceFetched(library === 'solana' ? 'SOL' : 'ETH') + await modalValidator.expectBalanceFetched(getBalanceSymbolByLibrary(library)) }) -sampleWalletTest('it should show onramp button accordingly', async () => { +sampleWalletTest('it should show onramp button accordingly', async ({ library }) => { await modalPage.openModal() - await modalValidator.expectOnrampButton() + if (library === 'bitcoin') { + await modalValidator.expectOnrampButton(false) + } else { + await modalValidator.expectOnrampButton(true) + } await modalPage.closeModal() }) @@ -59,6 +103,10 @@ sampleWalletTest('it should be connected instantly after page refresh', async () }) sampleWalletTest('it should show disabled networks', async ({ library }) => { + if (library === 'bitcoin') { + return + } + const disabledNetworks = library === 'solana' ? 'Solana Unsupported' : 'Gnosis' await modalPage.openModal() @@ -68,10 +116,9 @@ sampleWalletTest('it should show disabled networks', async ({ library }) => { }) sampleWalletTest('it should switch networks and sign', async ({ library }) => { - const chains = - library === 'solana' ? [solanaTestnet.name, solana.name] : [polygon.name, mainnet.name] - const caipNetworkId = - library === 'solana' ? [solanaTestnet.id, solana.id] : [polygon.id, mainnet.id] + const networks = getNetworksByLibrary(library) + const chains = networks.map(network => network.name) + const caipNetworkIds = networks.map(network => network.id) async function processChain(index: number) { if (index >= chains.length) { @@ -86,12 +133,15 @@ sampleWalletTest('it should switch networks and sign', async ({ library }) => { await modalValidator.expectSwitchedNetwork(chainName) await modalPage.closeModal() await modalValidator.expectCaipAddressHaveCorrectNetworkId( - caipNetworkId[index] as CaipNetworkId + caipNetworkIds[index] as CaipNetworkId ) // -- Sign ------------------------------------------------------------------ await modalPage.sign() - await walletValidator.expectReceivedSign({ chainName: chainNameOnWalletPage }) + await walletValidator.expectReceivedSign({ + chainName: chainNameOnWalletPage, + expectNetworkName: library !== 'bitcoin' + }) await walletPage.handleRequest({ accept: true }) await modalValidator.expectAcceptedSign() @@ -103,9 +153,9 @@ sampleWalletTest('it should switch networks and sign', async ({ library }) => { }) sampleWalletTest('it should switch networks using hook', async ({ library }) => { - const chains = library === 'solana' ? ['Solana Testnet', 'Solana'] : ['Polygon', 'Ethereum'] - const caipNetworkId = - library === 'solana' ? [solanaTestnet.id, solana.id] : [polygon.id, mainnet.id] + const networks = getNetworksByLibrary(library) + const chains = networks.map(network => network.name) + const caipNetworkIds = networks.map(network => network.id) async function processChain(index: number) { if (index >= chains.length) { @@ -120,7 +170,7 @@ sampleWalletTest('it should switch networks using hook', async ({ library }) => await modalValidator.expectSwitchedNetwork(chainName) await modalPage.closeModal() await modalValidator.expectCaipAddressHaveCorrectNetworkId( - caipNetworkId[index] as CaipNetworkId + caipNetworkIds[index] as CaipNetworkId ) await processChain(index + 1) @@ -131,7 +181,7 @@ sampleWalletTest('it should switch networks using hook', async ({ library }) => }) sampleWalletTest('it should show last connected network after refreshing', async ({ library }) => { - const chainName = library === 'solana' ? 'Solana Testnet' : 'Polygon' + const chainName = getLastNetworkNameByLibrary(library) await modalPage.switchNetwork(chainName) await modalValidator.expectSwitchedNetwork(chainName) @@ -146,19 +196,20 @@ sampleWalletTest('it should show last connected network after refreshing', async }) sampleWalletTest('it should reject sign', async ({ library }) => { - const chainName = library === 'solana' ? 'Solana Testnet' : 'Polygon' + const chainName = getLastNetworkNameByLibrary(library) await modalPage.sign() - await walletValidator.expectReceivedSign({ chainName }) + await walletValidator.expectReceivedSign({ chainName, expectNetworkName: library !== 'bitcoin' }) await walletPage.handleRequest({ accept: false }) await modalValidator.expectRejectedSign() }) sampleWalletTest('it should switch between multiple accounts', async ({ library }) => { // Multi address not available in Solana wallet - if (library === 'solana') { + if (library === 'solana' || library === 'bitcoin') { return } + const originalAddress = await modalPage.getAddress() await modalPage.openAccount() await modalPage.openProfileView() @@ -168,7 +219,7 @@ sampleWalletTest('it should switch between multiple accounts', async ({ library sampleWalletTest('it should show multiple accounts', async ({ library }) => { // Multi address not available in Solana wallet - if (library === 'solana') { + if (library === 'solana' || library === 'bitcoin') { return } @@ -179,7 +230,7 @@ sampleWalletTest('it should show multiple accounts', async ({ library }) => { }) sampleWalletTest('it should disconnect and connect to a single account', async ({ library }) => { - if (library === 'solana') { + if (library === 'solana' || library === 'bitcoin') { return } @@ -196,7 +247,7 @@ sampleWalletTest('it should disconnect and connect to a single account', async ( sampleWalletTest( 'it should show switch network modal if network is not supported and switch to supported network', async ({ library }) => { - if (library === 'solana') { + if (library === 'solana' || library === 'bitcoin') { return } @@ -232,6 +283,10 @@ sampleWalletTest('it should disconnect and close modal when connecting from wall }) sampleWalletTest('it should display wallet guide and show explore option', async ({ library }) => { + if (library === 'bitcoin') { + return + } + await modalPage.openConnectModal() await modalValidator.expectWalletGuide(library, 'get-started') await modalPage.clickWalletGuideGetStarted()