Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(sdk): further setup for integration tests #3282

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions sdk/integration-tests/flows.test.ts

This file was deleted.

95 changes: 95 additions & 0 deletions sdk/integration-tests/flows.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import { act, render, waitFor } from '@testing-library/react'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you planning to create more specific test modules? would be good to decouple a bit

aka test quote and order in isolation, and together

import { createRef } from 'react'
import { expect, test } from 'vitest'
import { useConnect } from 'wagmi'

import {
type Quote,
useOrder,
useQuote,
useValidateOrder,
} from '../src/index.js'

import {
ContextProvider,
ETHER,
MOCK_L1_ID,
MOCK_L2_ID,
ZERO_ADDRESS,
accounts,
createRenderHook,
createWagmiConfig,
testConnector,
} from './test-utils.js'

test('successfully processes order from quote to filled', async () => {
const wagmiConfig = createWagmiConfig()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see createWrapper in react.ts we can lift this out / reduce duplication

const renderHook = createRenderHook({ wagmiConfig })

const quoteHook = renderHook(() => {
return useQuote({
enabled: true,
mode: 'expense',
srcChainId: MOCK_L1_ID,
destChainId: MOCK_L2_ID,
deposit: {
amount: 2n * ETHER,
isNative: true,
},
expense: {
amount: 1n * ETHER,
isNative: true,
},
})
})
await waitFor(() => expect(quoteHook.result.current.isSuccess).toBe(true))

const quote = quoteHook.result.current.query.data as Quote
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious, why do you need this type cast?

expect(quote).toEqual({
deposit: { token: ZERO_ADDRESS, amount: 2n * ETHER },
expense: { token: ZERO_ADDRESS, amount: 1994017946161515453n },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the expense values are driven by the response from the solver api, so this assertion is unstable

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could instead assert the value exists, and is less than or equal to the deposit amount maybe?

})

const orderParams = {
deposit: { token: ZERO_ADDRESS, amount: 2n * ETHER },
expense: { token: ZERO_ADDRESS, amount: 1n * ETHER },
calls: [{ target: accounts[0], value: 1n * ETHER }],
srcChainId: MOCK_L1_ID,
destChainId: MOCK_L2_ID,
validateEnabled: false,
}

const validateHook = renderHook(() => {
return useValidateOrder({ enabled: true, order: orderParams })
})
await waitFor(() => {
return expect(validateHook.result.current.status === 'accepted').toBe(true)
})

const connectRef = createRef()
const orderRef = createRef()

// useOrder() can only be used with a connected account, so we need to render it conditionally
function TestOrder() {
orderRef.current = useOrder(orderParams)
return null
}

// Wrap TestOrder to only render if connected
function TestConnectAndOrder() {
const connectReturn = useConnect()
connectRef.current = connectReturn
return connectReturn.data ? <TestOrder /> : null
}

render(<TestConnectAndOrder />, { wrapper: ContextProvider })
act(() => {
connectRef.current?.connect({ connector: testConnector })
})

await waitFor(() => expect(orderRef.current).toBeDefined())
act(() => {
orderRef.current?.open()
})
await waitFor(() => expect(orderRef.current?.txHash).toBeDefined())
})
105 changes: 100 additions & 5 deletions sdk/integration-tests/test-utils.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,80 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { type RenderHookResult, renderHook } from '@testing-library/react'
import { http, type Chain, createWalletClient, publicActions } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import {
type Config,
type CreateConnectorFn,
WagmiProvider,
createConfig,
mock,
} from 'wagmi'

import { OmniProvider } from '../src/index.js'

export const ETHER = 1_000_000_000_000_000_000n // 18 decimals
export const MOCK_L1_ID = 1652
export const MOCK_L2_ID = 1654
export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
export const ZERO_ADDRESS =
'0x0000000000000000000000000000000000000000' as const

const MOCK_L1_CHAIN: Chain = {
id: MOCK_L1_ID,
name: 'Mock L1',
nativeCurrency: { decimals: 18, name: 'Ether', symbol: 'ETH' },
rpcUrls: {
default: {
http: ['http://localhost:8001'],
},
},
}

const MOCK_L2_CHAIN: Chain = {
id: MOCK_L2_ID,
name: 'Mock L2',
nativeCurrency: { decimals: 18, name: 'Ether', symbol: 'ETH' },
rpcUrls: {
default: {
http: ['http://localhost:8002'],
},
},
}

const MOCK_CHAINS: Record<number, Chain> = {
[MOCK_L1_ID]: MOCK_L1_CHAIN,
[MOCK_L2_ID]: MOCK_L2_CHAIN,
}

export const accounts = ['0xE0cF003AC27FaeC91f107E3834968A601842e9c6'] as const

const mockConnector = mock({ accounts })

const account = privateKeyToAccount(
'0xbb119deceaff95378015e684292e91a37ef2ae1522f300a2cfdcb5b004bbf00d',
)

function createClient({ chain }: { chain: Chain }) {
return createWalletClient({ account, chain, transport: http() })
}

export const testConnector: CreateConnectorFn = (config) => {
const connector = mockConnector(config)
connector.getClient = async ({ chainId } = {}) => {
const chain = chainId ? MOCK_CHAINS[chainId] : MOCK_L1_CHAIN
if (chain == null) {
throw new Error(`Unsupported chain: ${chainId}`)
}
return createClient({ chain })
}
return connector
}

export function createWagmiConfig() {
return createConfig({
chains: [MOCK_L1_CHAIN, MOCK_L2_CHAIN],
client: createClient,
})
}

export function createQueryClient() {
return new QueryClient({
Expand All @@ -16,10 +86,35 @@ export function createQueryClient() {
})
}

export function ContextProvider({ children }: { children: React.ReactNode }) {
export type TestConfig = {
queryClient?: QueryClient
wagmiConfig?: Config
}

export type ContextProviderProps = TestConfig & {
children: React.ReactNode
}

export function ContextProvider(props: ContextProviderProps) {
const client = props.queryClient ?? createQueryClient()
const config = props.wagmiConfig ?? createWagmiConfig()

return (
<QueryClientProvider client={createQueryClient()}>
<OmniProvider env="devnet">{children}</OmniProvider>
</QueryClientProvider>
<WagmiProvider config={config}>
<QueryClientProvider client={client}>
<OmniProvider env="devnet">{props.children}</OmniProvider>
</QueryClientProvider>
</WagmiProvider>
)
}

export function createRenderHook(config: TestConfig) {
return function customRenderHook<Result>(
render: () => Result,
): RenderHookResult<Result, ContextProviderProps> {
return renderHook(render, {
initialProps: config,
wrapper: ContextProvider,
})
}
}
Loading