Skip to content

Commit

Permalink
chore: Verify API tests (#2467)
Browse files Browse the repository at this point in the history
  • Loading branch information
chris13524 authored Aug 19, 2024
1 parent 82baf60 commit d345a43
Show file tree
Hide file tree
Showing 14 changed files with 384 additions and 16 deletions.
2 changes: 1 addition & 1 deletion apps/laboratory/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"playwright:start": "pnpm start",
"playwright:install": "playwright install --with-deps",
"synpress": "synpress ./tests/wallet-setup",
"playwright:test": "playwright test --grep-invert 'metamask.spec.ts'",
"playwright:test": "playwright test",
"playwright:test:metamask": "playwright test --grep 'metamask.spec.ts'",
"playwright:test:basic": "playwright test --grep 'basic-tests.spec.ts'",
"playwright:test:wallet": "playwright test --grep 'wallet.spec.ts'",
Expand Down
47 changes: 47 additions & 0 deletions apps/laboratory/src/pages/library/verify-domain-mismatch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { createWeb3Modal } from '@web3modal/wagmi/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { WagmiProvider } from 'wagmi'
import { AppKitButtons } from '../../components/AppKitButtons'
import { WagmiTests } from '../../components/Wagmi/WagmiTests'
import { ThemeStore } from '../../utils/StoreUtil'
import { ConstantsUtil } from '../../utils/ConstantsUtil'
import { WagmiModalInfo } from '../../components/Wagmi/WagmiModalInfo'
import { getWagmiConfig } from '../../utils/WagmiConstants'

// Special project ID with verify enabled on localhost
const projectId = 'e4eae1aad4503db9966a04fd045a7e4d'

const queryClient = new QueryClient()

const wagmiConfig = getWagmiConfig('default', {
projectId
})

const modal = createWeb3Modal({
wagmiConfig,
projectId,
metadata: ConstantsUtil.Metadata,
termsConditionsUrl: 'https://walletconnect.com/terms',
privacyPolicyUrl: 'https://walletconnect.com/privacy'
})

ThemeStore.setModal(modal)

export default function Wagmi() {
const [ready, setReady] = useState(false)

useEffect(() => {
setReady(true)
}, [])

return ready ? (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<AppKitButtons />
<WagmiModalInfo />
<WagmiTests />
</QueryClientProvider>
</WagmiProvider>
) : null
}
55 changes: 55 additions & 0 deletions apps/laboratory/src/pages/library/verify-evil.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createWeb3Modal } from '@web3modal/wagmi/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { WagmiProvider } from 'wagmi'
import { AppKitButtons } from '../../components/AppKitButtons'
import { WagmiTests } from '../../components/Wagmi/WagmiTests'
import { ThemeStore } from '../../utils/StoreUtil'
import { WagmiModalInfo } from '../../components/Wagmi/WagmiModalInfo'
import { getWagmiConfig } from '../../utils/WagmiConstants'

const metadata = {
name: 'Evil Web3Modal',
description: 'Evil Web3Modal Laboratory',
url: 'https://malicious-app-verify-simulation.vercel.app/',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
verifyUrl: ''
}

// Special project ID with https://malicious-app-verify-simulation.vercel.app/ as the verified domain and this domain is marked as a scam
const projectId = '9d176efa3150a1df0a76c8c138b6b657'

const queryClient = new QueryClient()

const wagmiConfig = getWagmiConfig('default', {
projectId,
metadata
})

const modal = createWeb3Modal({
wagmiConfig,
projectId,
metadata,
termsConditionsUrl: 'https://walletconnect.com/terms',
privacyPolicyUrl: 'https://walletconnect.com/privacy'
})

ThemeStore.setModal(modal)

export default function Wagmi() {
const [ready, setReady] = useState(false)

useEffect(() => {
setReady(true)
}, [])

return ready ? (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<AppKitButtons />
<WagmiModalInfo />
<WagmiTests />
</QueryClientProvider>
</WagmiProvider>
) : null
}
56 changes: 56 additions & 0 deletions apps/laboratory/src/pages/library/verify-valid.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createWeb3Modal } from '@web3modal/wagmi/react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { useEffect, useState } from 'react'
import { WagmiProvider } from 'wagmi'
import { AppKitButtons } from '../../components/AppKitButtons'
import { WagmiTests } from '../../components/Wagmi/WagmiTests'
import { ThemeStore } from '../../utils/StoreUtil'
import { WagmiModalInfo } from '../../components/Wagmi/WagmiModalInfo'
import { getWagmiConfig } from '../../utils/WagmiConstants'

const metadata = {
name: 'Web3Modal',
description: 'Web3Modal Laboratory',
// Allow localhost
url: 'http://localhost:3000',
icons: ['https://avatars.githubusercontent.com/u/37784886'],
verifyUrl: ''
}

// Special project ID with verify enabled on localhost
const projectId = 'e4eae1aad4503db9966a04fd045a7e4d'

const queryClient = new QueryClient()

const wagmiConfig = getWagmiConfig('default', {
projectId,
metadata
})

const modal = createWeb3Modal({
wagmiConfig,
projectId,
metadata,
termsConditionsUrl: 'https://walletconnect.com/terms',
privacyPolicyUrl: 'https://walletconnect.com/privacy'
})

ThemeStore.setModal(modal)

export default function Wagmi() {
const [ready, setReady] = useState(false)

useEffect(() => {
setReady(true)
}, [])

return ready ? (
<WagmiProvider config={wagmiConfig}>
<QueryClientProvider client={queryClient}>
<AppKitButtons />
<WagmiModalInfo />
<WagmiTests />
</QueryClientProvider>
</WagmiProvider>
) : null
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const connectors = [
optionalMethods: [...OPTIONAL_METHODS, 'wallet_grantPermissions']
})
]
const wagmiEmailConfig = getWagmiConfig('email', connectors)
const wagmiEmailConfig = getWagmiConfig('email', { connectors })
const modal = createWeb3Modal({
wagmiConfig: wagmiEmailConfig,
projectId: ConstantsUtil.ProjectId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const connectors = [
optionalMethods: [...OPTIONAL_METHODS, 'wallet_grantPermissions']
})
]
const wagmiEmailConfig = getWagmiConfig('email', connectors)
const wagmiEmailConfig = getWagmiConfig('email', { connectors })
const modal = createWeb3Modal({
wagmiConfig: wagmiEmailConfig,
projectId: ConstantsUtil.ProjectId,
Expand Down
8 changes: 3 additions & 5 deletions apps/laboratory/src/utils/WagmiConstants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import {
type Chain
} from 'wagmi/chains'
import { ConstantsUtil } from './ConstantsUtil'
import type { CreateConnectorFn } from 'wagmi'

export const WagmiConstantsUtil = {
chains: [
Expand All @@ -40,21 +39,20 @@ export const WagmiConstantsUtil = {
] as [Chain, ...Chain[]]
}

export function getWagmiConfig(type: 'default' | 'email', connectors: CreateConnectorFn[] = []) {
export function getWagmiConfig(type: 'default' | 'email', override = {}) {
const config = {
chains: WagmiConstantsUtil.chains,
projectId: ConstantsUtil.ProjectId,
metadata: ConstantsUtil.Metadata,
ssr: true,
connectors
...override
}

const emailConfig = {
...config,
auth: {
socials: ['google', 'x', 'discord', 'farcaster', 'github', 'apple', 'facebook']
},
connectors
}
}

const wagmiConfig = defaultWagmiConfig(type === 'email' ? emailConfig : config)
Expand Down
6 changes: 0 additions & 6 deletions apps/laboratory/tests/shared/fixtures/w3m-fixture.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
/* eslint no-console: 0 */

import { ModalPage } from '../pages/ModalPage'
import { ModalValidator } from '../validators/ModalValidator'
import { timeStart, timeEnd } from '../utils/logs'
import { timingFixture } from './timing-fixture'

// Declare the types of fixtures to use
export interface ModalFixture {
modalPage: ModalPage
modalValidator: ModalValidator
library: string
}

Expand All @@ -32,10 +30,6 @@ export const testMSiwe = timingFixture.extend<ModalFixture>({
const modalPage = new ModalPage(page, library, 'siwe')
await modalPage.load()
await use(modalPage)
},
modalValidator: async ({ modalPage }, use) => {
const modalValidator = new ModalValidator(modalPage.page)
await use(modalValidator)
}
})

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ModalFixture } from './w3m-fixture'
import { ModalPage } from '../pages/ModalPage'
import { timingFixture } from './timing-fixture'

export const testMVerifyDomainMismatch = timingFixture.extend<ModalFixture>({
library: ['wagmi', { option: true }],
modalPage: async ({ page, library }, use) => {
const modalPage = new ModalPage(page, library, 'verify-domain-mismatch')
await modalPage.load()
await use(modalPage)
}
})
12 changes: 12 additions & 0 deletions apps/laboratory/tests/shared/fixtures/w3m-verify-evil-fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ModalFixture } from './w3m-fixture'
import { ModalPage } from '../pages/ModalPage'
import { timingFixture } from './timing-fixture'

export const testMVerifyEvil = timingFixture.extend<ModalFixture>({
library: ['wagmi', { option: true }],
modalPage: async ({ page, library }, use) => {
const modalPage = new ModalPage(page, library, 'verify-evil')
await modalPage.load()
await use(modalPage)
}
})
12 changes: 12 additions & 0 deletions apps/laboratory/tests/shared/fixtures/w3m-verify-valid-fixture.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { ModalFixture } from './w3m-fixture'
import { ModalPage } from '../pages/ModalPage'
import { timingFixture } from './timing-fixture'

export const testMVerifyValid = timingFixture.extend<ModalFixture>({
library: ['wagmi', { option: true }],
modalPage: async ({ page, library }, use) => {
const modalPage = new ModalPage(page, library, 'verify-valid')
await modalPage.load()
await use(modalPage)
}
})
23 changes: 21 additions & 2 deletions apps/laboratory/tests/shared/pages/ModalPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,28 @@ import { DeviceRegistrationPage } from './DeviceRegistrationPage'
import type { TimingRecords } from '../fixtures/timing-fixture'
import { WalletPage } from './WalletPage'
import { WalletValidator } from '../validators/WalletValidator'
import { routeInterceptUrl } from '../utils/verify'

export type ModalFlavor = 'default' | 'siwe' | 'email' | 'wallet' | 'external' | 'all'
const maliciousUrl = 'https://malicious-app-verify-simulation.vercel.app'

export type ModalFlavor =
| 'default'
| 'siwe'
| 'email'
| 'wallet'
| 'external'
| 'verify-valid'
| 'verify-domain-mismatch'
| 'verify-evil'
| 'all'

function getUrlByFlavor(baseUrl: string, library: string, flavor: ModalFlavor) {
const urlsByFlavor: Partial<Record<ModalFlavor, string>> = {
default: `${baseUrl}library/${library}/`,
external: `${baseUrl}library/external/`
external: `${baseUrl}library/external/`,
'verify-valid': `${baseUrl}library/verify-valid/`,
'verify-domain-mismatch': `${baseUrl}library/verify-domain-mismatch/`,
'verify-evil': maliciousUrl
}

return urlsByFlavor[flavor] || `${baseUrl}library/${library}-${flavor}/`
Expand All @@ -37,6 +52,10 @@ export class ModalPage {
}

async load() {
if (this.flavor === 'verify-evil') {
await routeInterceptUrl(this.page, maliciousUrl, this.baseURL, '/library/verify-evil/')
}

await this.page.goto(this.url)
}

Expand Down
38 changes: 38 additions & 0 deletions apps/laboratory/tests/shared/utils/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { Page } from '@playwright/test'

/*
* This function makes requests to the intercept URL be handled by the base URL
* This allows the browser APIs to think interceptUrl is the URL the page is on
*/
// eslint-disable-next-line max-params
export async function routeInterceptUrl(
page: Page,
interceptUrl: string,
baseUrl: string,
path: string
) {
await page.route(`${interceptUrl}/**/*`, async (route, request) => {
// eslint-disable-next-line init-declarations
let url: string
if (request.url() === `${interceptUrl}/`) {
url = `${baseUrl}${path}`
} else {
url = request.url().replace(interceptUrl, baseUrl)
}
const response = await fetch(url, {
method: request.method(),
headers: request.headers(),
body: request.postData()
})
const headers: Record<string, string> = {}
response.headers.forEach((value: string, key: string) => {
headers[key] = value
})
const body = Buffer.from(await response.arrayBuffer())
await route.fulfill({
status: response.status,
headers,
body
})
})
}
Loading

0 comments on commit d345a43

Please sign in to comment.