feat(e2e): Transfers FE e2e tests (#192)
This commit is contained in:
parent
4f2bcea5be
commit
d2225f1b70
26 changed files with 451 additions and 128 deletions
|
@ -18,7 +18,6 @@ Install rust by executing a script from the internet (😅):
|
|||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
|
||||
Check the version with `cargo version`.
|
||||
|
||||
Finally add the wasm target:
|
||||
|
@ -242,37 +241,7 @@ data come through.
|
|||
|
||||
### Run the Frontend
|
||||
|
||||
Now on your own machine, checkout the
|
||||
https://github.com/informalsystems/cycles-hackathon-app.
|
||||
|
||||
Copy the `.env.example` file to `.env.local`:
|
||||
|
||||
```bash
|
||||
cp .env.example .env.local
|
||||
```
|
||||
|
||||
and set the relevant fields. You should have the contract address and TEE pubkey
|
||||
from the output of the `deploy.sh` and `handshake.sh` scripts, respectfully. The
|
||||
chain id is probably `testing` and the IP address for the URLs is probably
|
||||
`143.244.186.205`. Modify accordingly. For example:
|
||||
|
||||
```bash
|
||||
#.env.local
|
||||
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=wasm1ch9ed27cdu3a4fkx37gnagm7jcthj0rggnmmjwwwe4xhwmk0d65q8fn9pz
|
||||
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=030c25e39743fd4c7553d87873919281d567b5c328fb903cbfbe9541518736a2d2
|
||||
NEXT_PUBLIC_CHAIN_ID=testing
|
||||
NEXT_PUBLIC_CHAIN_RPC_URL=http://143.244.186.205:26657
|
||||
NEXT_PUBLIC_CHAIN_REST_URL=http://143.244.186.205:1317
|
||||
```
|
||||
|
||||
Install and run the app:
|
||||
|
||||
```bash
|
||||
npm install -f
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You can now open the app in http://localhost:3000/.
|
||||
Now on your own machine, follow [these steps](./frontend/README.md#development).
|
||||
|
||||
Make sure you have Keplr installed in your browser and you should now be able to
|
||||
use the app!
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
# App required variables
|
||||
NEXT_PUBLIC_TARGET_CHAIN=localWasm
|
||||
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=02360955ff74750f6ea0b539f41cce89451f591e4c835d0a5406e6effa96dd169d
|
||||
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=wasm1jfgr0vgunezkhfmdy7krrupu6yjhx224nxtjptll2ylkkqhyzeshrspu9
|
||||
|
||||
# E2E Testing
|
||||
# E2E testing required variables
|
||||
TEST_BASE_URL=http://127.0.0.1:3000
|
||||
TEST_KEPLR_EXTENSION_VERSION=0.12.124
|
||||
TEST_WALLET_MNEMONIC=debris topic trash february punch advance tackle alert reduce box chase lend buffalo effort napkin drip mountain result rely swear tornado master devote what
|
||||
TEST_SECONDARY_WALLET_MNEMONIC=employ jungle nuclear clutch general vicious thrive width time asthma shadow orchard wage affair matrix slush room weapon prize that record path grit tourist
|
||||
TEST_SECONDARY_WALLET_ADDRESS=wasm1lejkm8nevz4hgmafyfm03upkkj76cvzj4geapv
|
||||
TEST_WALLET_PASSWORD=;pzPCXB^@92byC
|
||||
PLAYWRIGHT_HTML_OPEN=never
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
# Transfer App
|
||||
|
||||
This is an example frontend that illustrates how to interact with a Transfer Quartz App.
|
||||
This is an example frontend that illustrates how to interact with a Quartz app.
|
||||
|
||||
This example offers:
|
||||
This example offers the ability to:
|
||||
|
||||
- Deposit amounts into a balance
|
||||
- Withdraw the whole deposit
|
||||
- Transfer amounts between wallet addresses in a private-preserving way
|
||||
- Query your encrypted balance to capture changes
|
||||
- Transfer amounts between wallets in a private-preserving way
|
||||
- Query your encrypted balance
|
||||
- Switch between Keplr wallets
|
||||
|
||||
## Requirements
|
||||
|
@ -26,7 +26,7 @@ Install dependencies:
|
|||
npm ci
|
||||
```
|
||||
|
||||
The App requires some environment variables to fully work. Be sure to set up those accordingly to your local environment.
|
||||
The app requires some environment variables to fully work. Be sure to set up those accordingly to your local environment.
|
||||
|
||||
You should start from the template:
|
||||
|
||||
|
@ -34,10 +34,52 @@ You should start from the template:
|
|||
cp .env.example .env.local
|
||||
```
|
||||
|
||||
Required environment variables:
|
||||
|
||||
```
|
||||
# Choose target chain configuration
|
||||
NEXT_PUBLIC_TARGET_CHAIN=<localWasm | localNeutron | doWasm>
|
||||
# Enclave public key to encrypt transfers
|
||||
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=<public_key>
|
||||
# Target transfers contract
|
||||
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=<contract_address>
|
||||
```
|
||||
|
||||
Run the app:
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
And now everything is up & running 🎉
|
||||
App will be running on http://localhost:3000/ and now everything is up & running 🎉
|
||||
|
||||
## E2E Testing
|
||||
|
||||
For tests to work, you need to set up the following required environment variables:
|
||||
|
||||
```
|
||||
# Frontend base url
|
||||
TEST_BASE_URL=<url>
|
||||
# Keplr browser extension version
|
||||
TEST_KEPLR_EXTENSION_VERSION=<version>
|
||||
# Main wallet mnemonic (Use only funded wallets)
|
||||
TEST_WALLET_MNEMONIC=<mnemonic>
|
||||
# Secondary wallet mnemonic (Use only funded wallets)
|
||||
TEST_SECONDARY_WALLET_MNEMONIC=<mnemonic>
|
||||
# Secondary wallet address
|
||||
TEST_SECONDARY_WALLET_ADDRESS=<wallet_address>
|
||||
# Keplr wallet password. It can be whatever
|
||||
TEST_WALLET_PASSWORD=<password>
|
||||
```
|
||||
|
||||
Run all E2E tests:
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
If want to run the tests with the Playwright dedicated interface, run:
|
||||
|
||||
```bash
|
||||
npm run test:ui
|
||||
```
|
||||
|
|
6
apps/transfers/frontend/env.d.ts
vendored
6
apps/transfers/frontend/env.d.ts
vendored
|
@ -3,5 +3,11 @@ namespace NodeJS {
|
|||
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY: string
|
||||
NEXT_PUBLIC_TARGET_CHAIN: string
|
||||
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS: string
|
||||
TEST_BASE_URL: string
|
||||
TEST_KEPLR_EXTENSION_VERSION: string
|
||||
TEST_WALLET_MNEMONIC: string
|
||||
TEST_SECONDARY_WALLET_MNEMONIC: string
|
||||
TEST_SECONDARY_WALLET_ADDRESS: string
|
||||
TEST_WALLET_PASSWORD: string
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
"lint": "eslint .",
|
||||
"lint:fix": "eslint . --fix",
|
||||
"test": "playwright test",
|
||||
"test:ui": "playwright test --ui"
|
||||
"test:ui": "playwright test --ui",
|
||||
"prepare": "npx playwright install chromium"
|
||||
},
|
||||
"dependencies": {
|
||||
"eciesjs": "^0.4.7",
|
||||
|
|
|
@ -25,6 +25,8 @@ export default defineConfig({
|
|||
workers: process.env.CI ? 1 : undefined,
|
||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||
reporter: 'html',
|
||||
timeout: 0,
|
||||
globalTimeout: process.env.CI ? 5 * 60 * 1000 : undefined,
|
||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||
use: {
|
||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
||||
|
|
|
@ -22,6 +22,7 @@ export default function Landing() {
|
|||
<main className="flex min-h-screen flex-col items-center gap-4 p-24">
|
||||
<p>Connect your Keplr wallet to log in</p>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.primary"
|
||||
onClick={connectWallet}
|
||||
>
|
||||
|
|
|
@ -30,12 +30,14 @@ export default function SetSeed() {
|
|||
</code>
|
||||
<div className="flex flex-col gap-4">
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.primary"
|
||||
onClick={acceptPhrase}
|
||||
>
|
||||
Continue with the autogenerated seed phrase
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.secondary"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
>
|
||||
|
|
|
@ -96,7 +96,7 @@ export default function Dashboard() {
|
|||
).then((data) => {
|
||||
setLoading(false)
|
||||
setBalance(retrieveBalance(data))
|
||||
showSuccess('Balance updated correctly')
|
||||
showSuccess('Balance updated successfully')
|
||||
})
|
||||
}
|
||||
},
|
||||
|
@ -167,6 +167,7 @@ export default function Dashboard() {
|
|||
|
||||
<StyledText
|
||||
className="w-full justify-start bg-emerald-500"
|
||||
as="button"
|
||||
variant="button.primary"
|
||||
onClick={() => setIsDepositModalOpen(true)}
|
||||
>
|
||||
|
@ -174,6 +175,7 @@ export default function Dashboard() {
|
|||
Deposit
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.primary"
|
||||
className="w-full justify-start bg-violet-500"
|
||||
onClick={() => setIsTransferModalOpen(true)}
|
||||
|
@ -182,6 +184,7 @@ export default function Dashboard() {
|
|||
Transfer
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
className="w-full justify-start bg-amber-500"
|
||||
variant="button.primary"
|
||||
onClick={() => setIsWithdrawModalOpen(true)}
|
||||
|
@ -192,6 +195,7 @@ export default function Dashboard() {
|
|||
<div className="my-1 w-full border-black/25"></div>
|
||||
<StyledText
|
||||
className="w-full justify-start"
|
||||
as="button"
|
||||
variant="button.secondary"
|
||||
onClick={() => {
|
||||
const res = confirm(
|
||||
|
|
|
@ -101,6 +101,7 @@ export function DepositModalWindow(props: ModalWindowProps) {
|
|||
Deposit
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.secondary"
|
||||
onClick={props.onClose}
|
||||
>
|
||||
|
|
|
@ -66,6 +66,7 @@ export function EnterSeedModal({
|
|||
Continue
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
|
|
|
@ -100,7 +100,7 @@ export function ModalWindow({
|
|||
onTransitionEnd={handleTransitionEnd}
|
||||
{...otherProps}
|
||||
>
|
||||
{children}
|
||||
{isOpen && children}
|
||||
</div>
|
||||
</>,
|
||||
document.body,
|
||||
|
|
|
@ -154,6 +154,7 @@ export function TransferModalWindow(props: ModalWindowProps) {
|
|||
Transfer
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.secondary"
|
||||
onClick={props.onClose}
|
||||
>
|
||||
|
|
|
@ -59,6 +59,7 @@ export function WithdrawModalWindow(props: ModalWindowProps) {
|
|||
Withdraw
|
||||
</StyledText>
|
||||
<StyledText
|
||||
as="button"
|
||||
variant="button.secondary"
|
||||
onClick={props.onClose}
|
||||
>
|
||||
|
|
7
apps/transfers/frontend/src/config/routes.ts
Normal file
7
apps/transfers/frontend/src/config/routes.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
type Url = 'landing' | 'seed' | 'dashboard'
|
||||
|
||||
export const routes: Record<Url, string> = {
|
||||
landing: '/',
|
||||
seed: '/set-seed',
|
||||
dashboard: '/dashboard',
|
||||
}
|
39
apps/transfers/frontend/tests/e2e/auth.spec.ts
Normal file
39
apps/transfers/frontend/tests/e2e/auth.spec.ts
Normal file
|
@ -0,0 +1,39 @@
|
|||
import { routes } from '@/config/routes'
|
||||
import test from './fixtures'
|
||||
import { connectWallet } from './helpers/connectWalet'
|
||||
import { setSeedPhrase } from './helpers/setSeedPhrase'
|
||||
|
||||
const { dashboard, landing, seed } = routes
|
||||
|
||||
test.describe('Auth', () => {
|
||||
test('can go nowhere but landing page without a wallet', async ({ page }) => {
|
||||
await page.goto(seed)
|
||||
await page.goto(dashboard)
|
||||
await test
|
||||
.expect(page.getByRole('button', { name: /connect/i }))
|
||||
.toBeVisible()
|
||||
})
|
||||
|
||||
test('can go nowhere but seed page without a seed phrase', async ({
|
||||
context,
|
||||
page,
|
||||
}) => {
|
||||
await connectWallet({ context, page })
|
||||
|
||||
await page.goto(landing)
|
||||
await page.goto(dashboard)
|
||||
await test.expect(page.getByText(/recovery seed phrase/i)).toBeVisible()
|
||||
})
|
||||
|
||||
test('cannot go to anon pages once fully logged in', async ({
|
||||
context,
|
||||
page,
|
||||
}) => {
|
||||
await connectWallet({ context, page })
|
||||
await setSeedPhrase({ page })
|
||||
|
||||
await page.goto(landing)
|
||||
await page.goto(seed)
|
||||
await test.expect(page.getByText(/balance:/i)).toBeVisible()
|
||||
})
|
||||
})
|
|
@ -1,88 +1,49 @@
|
|||
import {
|
||||
test as baseTest,
|
||||
chromium,
|
||||
expect,
|
||||
BrowserContext,
|
||||
} from '@playwright/test'
|
||||
import { test as baseTest, chromium } from '@playwright/test'
|
||||
import path from 'path'
|
||||
|
||||
import { importWallet } from './helpers/importWallet'
|
||||
|
||||
let extensionUrl: string
|
||||
const pathToExtension = path.join(
|
||||
__dirname,
|
||||
'extensions',
|
||||
`keplr-extension-manifest-v3-v${process.env.TEST_KEPLR_EXTENSION_VERSION}`,
|
||||
)
|
||||
|
||||
// Tests fixtures
|
||||
const test = baseTest.extend<{}, { _globalContext: BrowserContext }>({
|
||||
// Shared context for tests so Keplr initialization runs only once for all tests
|
||||
_globalContext: [
|
||||
async ({}, use) => {
|
||||
const mnemonicWords = process.env.TEST_KEPLR_MNEMONIC!.split(' ')
|
||||
const pathToExtension = path.join(
|
||||
__dirname,
|
||||
'extensions',
|
||||
`keplr-extension-manifest-v3-v${process.env.TEST_KEPLR_EXTENSION_VERSION}`,
|
||||
)
|
||||
// We launch browser with the extension
|
||||
const context = await chromium.launchPersistentContext('', {
|
||||
headless: false,
|
||||
args: [
|
||||
`--disable-extensions-except=${pathToExtension}`,
|
||||
`--load-extension=${pathToExtension}`,
|
||||
],
|
||||
})
|
||||
const page = await context.waitForEvent('page')
|
||||
const extensionId = /\/\/(.*?)\//.exec(page.url())![1]
|
||||
const test = baseTest.extend<{
|
||||
extensionUrl: string
|
||||
}>({
|
||||
// Overwritten Playwright context to setup Keplr wallet before all tests
|
||||
context: async ({}, use) => {
|
||||
// Launch browser with Keplr installed
|
||||
const context = await chromium.launchPersistentContext('', {
|
||||
headless: false,
|
||||
args: [
|
||||
`--disable-extensions-except=${pathToExtension}`,
|
||||
`--load-extension=${pathToExtension}`,
|
||||
],
|
||||
})
|
||||
|
||||
// Keplr import wallet flow
|
||||
await page.waitForURL(new RegExp(`${extensionId}/register.html`))
|
||||
await expect(page.getByText('Import an existing wallet')).toBeVisible()
|
||||
await page
|
||||
.getByRole('button', { name: 'Import an existing wallet' })
|
||||
.click()
|
||||
await expect(
|
||||
page.getByText('Use recovery phrase or private key'),
|
||||
).toBeVisible()
|
||||
await page
|
||||
.getByRole('button', { name: 'Use recovery phrase or private key' })
|
||||
.click()
|
||||
const page = await context.waitForEvent('page')
|
||||
|
||||
await page.getByText('24 Words').click()
|
||||
const seedInputs = await page.locator('input')
|
||||
for (let i = 0; i < mnemonicWords.length; i++) {
|
||||
await seedInputs.nth(i).fill(mnemonicWords[i])
|
||||
}
|
||||
await page.getByRole('button', { name: 'Import', exact: true }).click()
|
||||
// Retrieve target URL to interact with Keplr extension
|
||||
const extensionId = /\/\/(.*?)\//.exec(page.url())![1]
|
||||
extensionUrl = `chrome-extension://${extensionId}`
|
||||
|
||||
await page
|
||||
.getByPlaceholder('e.g. Trading, NFT Vault, Investment')
|
||||
.fill('Playwright Wallet')
|
||||
const inputs = await page.getByPlaceholder(
|
||||
'At least 8 characters in length',
|
||||
)
|
||||
for (let i = 0; i < (await inputs.count()); i++) {
|
||||
await inputs.nth(i).fill(process.env.TEST_KEPLR_PASSWORD!)
|
||||
}
|
||||
await page.getByRole('button', { name: 'Next' }).click()
|
||||
// Import a wallet to be used in tests
|
||||
await importWallet({
|
||||
extensionUrl,
|
||||
mnemonic: process.env.TEST_WALLET_MNEMONIC,
|
||||
name: 'main',
|
||||
page,
|
||||
})
|
||||
|
||||
await expect(page.getByText('Select Chains')).toBeVisible()
|
||||
await page.getByRole('button', { name: 'Save' }).click()
|
||||
|
||||
// Accept app suggested testnet info
|
||||
await page.goto('/')
|
||||
const addChainPage = await context.waitForEvent('page')
|
||||
await addChainPage.getByRole('button', { name: 'Approve' }).click()
|
||||
|
||||
// Wait for App to load
|
||||
await test.expect(page.getByText('Balance:')).toBeVisible()
|
||||
|
||||
await use(context)
|
||||
await context.close()
|
||||
},
|
||||
{ scope: 'worker' },
|
||||
],
|
||||
context: async ({ _globalContext }, use) => {
|
||||
await use(_globalContext)
|
||||
await use(context)
|
||||
await context.close()
|
||||
},
|
||||
page: async ({ context }, use) => {
|
||||
const page = await context.newPage()
|
||||
|
||||
await use(page)
|
||||
await page.close()
|
||||
extensionUrl: async ({}, use) => {
|
||||
await use(extensionUrl)
|
||||
},
|
||||
})
|
||||
|
||||
|
|
21
apps/transfers/frontend/tests/e2e/helpers/connectWalet.ts
Normal file
21
apps/transfers/frontend/tests/e2e/helpers/connectWalet.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { BrowserContext, Page } from '@playwright/test'
|
||||
|
||||
import { routes } from '@/config/routes'
|
||||
|
||||
export const connectWallet = async ({
|
||||
context,
|
||||
page,
|
||||
}: {
|
||||
context: BrowserContext
|
||||
page: Page
|
||||
}) => {
|
||||
// Connect to Keplr wallet
|
||||
await page.goto(routes.landing)
|
||||
await page.getByRole('button', { name: /connect/i }).click()
|
||||
|
||||
// Accept app suggested testnet info
|
||||
const addChainPage = await context.waitForEvent('page')
|
||||
|
||||
await addChainPage.getByRole('button', { name: /approve/i }).click()
|
||||
await addChainPage.waitForEvent('close')
|
||||
}
|
20
apps/transfers/frontend/tests/e2e/helpers/getBalance.ts
Normal file
20
apps/transfers/frontend/tests/e2e/helpers/getBalance.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { BrowserContext, Page } from '@playwright/test'
|
||||
import { signTx } from './signTx'
|
||||
|
||||
export const getBalance = async ({
|
||||
context,
|
||||
page,
|
||||
}: {
|
||||
context: BrowserContext
|
||||
page: Page
|
||||
}) => {
|
||||
// Check new balance
|
||||
await page.getByRole('button', { name: /get/i }).click()
|
||||
|
||||
await signTx({ context, page })
|
||||
|
||||
// Wait for the success alert to appear so we know balance updated
|
||||
await page.getByText(/\$/i).waitFor({ state: 'visible' })
|
||||
|
||||
return page.getByText(/\$/i).textContent()
|
||||
}
|
41
apps/transfers/frontend/tests/e2e/helpers/importWallet.ts
Normal file
41
apps/transfers/frontend/tests/e2e/helpers/importWallet.ts
Normal file
|
@ -0,0 +1,41 @@
|
|||
import { Page } from '@playwright/test'
|
||||
|
||||
export const importWallet = async ({
|
||||
extensionUrl,
|
||||
mnemonic,
|
||||
name,
|
||||
page,
|
||||
}: {
|
||||
extensionUrl: string
|
||||
mnemonic: string
|
||||
name: string
|
||||
page: Page
|
||||
}) => {
|
||||
await page.goto(`${extensionUrl}/register.html`)
|
||||
|
||||
const mnemonicWords = mnemonic.split(' ')
|
||||
|
||||
await page.getByRole('button', { name: /import/i }).click()
|
||||
await page.getByRole('button', { name: /use/i }).click()
|
||||
await page.getByRole('button', { name: /24/ }).click()
|
||||
|
||||
const seedInputs = await page.locator('input')
|
||||
|
||||
for (let i = 0; i < mnemonicWords.length; i++) {
|
||||
await seedInputs.nth(i).fill(mnemonicWords[i])
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: 'Import', exact: true }).click()
|
||||
await page.getByPlaceholder('e.g. Trading, NFT Vault,').fill(name)
|
||||
|
||||
const inputs = await page.getByPlaceholder('At least 8 characters in length')
|
||||
|
||||
for (let i = 0; i < (await inputs.count()); i++) {
|
||||
await inputs.nth(i).fill(process.env.TEST_WALLET_PASSWORD)
|
||||
}
|
||||
|
||||
await page.getByRole('button', { name: /next/i }).click()
|
||||
await page.getByRole('button', { name: /save/i }).click()
|
||||
|
||||
await page.close()
|
||||
}
|
17
apps/transfers/frontend/tests/e2e/helpers/setSeedPhrase.ts
Normal file
17
apps/transfers/frontend/tests/e2e/helpers/setSeedPhrase.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import { Page } from '@playwright/test'
|
||||
|
||||
export const setSeedPhrase = async ({
|
||||
page,
|
||||
seedPhrase,
|
||||
}: {
|
||||
page: Page
|
||||
seedPhrase?: string
|
||||
}) => {
|
||||
if (!seedPhrase) {
|
||||
await page.getByRole('button', { name: /continue with/i }).click()
|
||||
} else {
|
||||
await page.getByRole('button', { name: /enter my own/i }).click()
|
||||
await page.locator('input').fill(seedPhrase)
|
||||
await page.getByRole('button', { name: 'Continue', exact: true }).click()
|
||||
}
|
||||
}
|
16
apps/transfers/frontend/tests/e2e/helpers/signTx.ts
Normal file
16
apps/transfers/frontend/tests/e2e/helpers/signTx.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { BrowserContext, Page } from '@playwright/test'
|
||||
|
||||
export const signTx = async ({
|
||||
context,
|
||||
page,
|
||||
}: {
|
||||
context: BrowserContext
|
||||
page: Page
|
||||
}) => {
|
||||
// Sign tx
|
||||
const signPage = await context.waitForEvent('page')
|
||||
|
||||
await signPage.getByRole('button', { name: /approve/i }).click()
|
||||
await signPage.waitForEvent('close')
|
||||
await page.getByText(/successfully/i).waitFor({ state: 'visible' })
|
||||
}
|
19
apps/transfers/frontend/tests/e2e/helpers/swapWallet.ts
Normal file
19
apps/transfers/frontend/tests/e2e/helpers/swapWallet.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { BrowserContext } from '@playwright/test'
|
||||
|
||||
export const swapWallet = async ({
|
||||
context,
|
||||
extensionUrl,
|
||||
name,
|
||||
}: {
|
||||
context: BrowserContext
|
||||
extensionUrl: string
|
||||
name: string
|
||||
}) => {
|
||||
const page = await context.newPage()
|
||||
|
||||
await page.goto(`${extensionUrl}/popup.html`)
|
||||
await page.locator('div[cursor="pointer"] > svg').nth(1).click()
|
||||
await page.getByText(name).click()
|
||||
|
||||
await page.close()
|
||||
}
|
31
apps/transfers/frontend/tests/e2e/seed-phrase.spec.ts
Normal file
31
apps/transfers/frontend/tests/e2e/seed-phrase.spec.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import test from './fixtures'
|
||||
import { connectWallet } from './helpers/connectWalet'
|
||||
import { setSeedPhrase } from './helpers/setSeedPhrase'
|
||||
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await connectWallet({ context, page })
|
||||
})
|
||||
|
||||
test.describe('Seed Phrase', () => {
|
||||
test('can use autogenerated seed phrase', async ({ page }) => {
|
||||
await setSeedPhrase({ page })
|
||||
await test
|
||||
.expect(
|
||||
await page.evaluate(() =>
|
||||
window.localStorage.getItem('ephemeral-mnemonic'),
|
||||
),
|
||||
)
|
||||
.toBeDefined()
|
||||
})
|
||||
|
||||
test('can enter and use a custom seed phrase', async ({ page }) => {
|
||||
await setSeedPhrase({ page, seedPhrase: process.env.TEST_WALLET_MNEMONIC })
|
||||
await test
|
||||
.expect(
|
||||
await page.evaluate(() =>
|
||||
window.localStorage.getItem('ephemeral-mnemonic'),
|
||||
),
|
||||
)
|
||||
.toEqual(process.env.TEST_WALLET_MNEMONIC)
|
||||
})
|
||||
})
|
|
@ -1,15 +1,131 @@
|
|||
import test from './fixtures'
|
||||
import { importWallet } from './helpers/importWallet'
|
||||
import { getBalance } from './helpers/getBalance'
|
||||
import { swapWallet } from './helpers/swapWallet'
|
||||
import { signTx } from './helpers/signTx'
|
||||
import { connectWallet } from './helpers/connectWalet'
|
||||
import { setSeedPhrase } from './helpers/setSeedPhrase'
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await page.goto('/')
|
||||
test.describe.configure({ mode: 'serial' })
|
||||
test.beforeEach(async ({ context, page }) => {
|
||||
await connectWallet({ context, page })
|
||||
await setSeedPhrase({ page, seedPhrase: process.env.TEST_WALLET_MNEMONIC })
|
||||
})
|
||||
|
||||
let mainBalance: number
|
||||
|
||||
test.describe('Transfers', () => {
|
||||
test('app should render correctly', async ({ page }) => {
|
||||
await test.expect(page.getByText('Balance:')).toBeVisible()
|
||||
test('can deposit a sum successfully', async ({ context, page }) => {
|
||||
// Initialize the balance
|
||||
mainBalance = Number(
|
||||
(await getBalance({ context, page }))!.replace('$', ''),
|
||||
)
|
||||
|
||||
await page.getByRole('button', { name: /deposit/i }).click()
|
||||
await page.keyboard.type('20')
|
||||
await page
|
||||
.getByRole('button', { name: /deposit/i })
|
||||
.nth(1)
|
||||
.click()
|
||||
|
||||
await signTx({ context, page })
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /cancel/i, includeHidden: false })
|
||||
.click()
|
||||
|
||||
// Check new balance
|
||||
await page.waitForTimeout(4000)
|
||||
|
||||
mainBalance += 20
|
||||
|
||||
await test
|
||||
.expect(await getBalance({ context, page }))
|
||||
.toEqual(`$${mainBalance}`)
|
||||
})
|
||||
|
||||
test('balance should be 0 at first', async ({ page }) => {
|
||||
await test.expect(page.getByText('$0')).toBeVisible()
|
||||
test('can transfer to another wallet successfully', async ({
|
||||
context,
|
||||
extensionUrl,
|
||||
page,
|
||||
}) => {
|
||||
// Import a secondary wallet to transfer to
|
||||
await importWallet({
|
||||
extensionUrl,
|
||||
mnemonic: process.env.TEST_SECONDARY_WALLET_MNEMONIC,
|
||||
page: await context.newPage(),
|
||||
name: 'secondary',
|
||||
})
|
||||
|
||||
// Initialize the secondary account balance after importing
|
||||
const secondaryBalance = Number(
|
||||
(await getBalance({ context, page }))!.replace('$', ''),
|
||||
)
|
||||
|
||||
// Swap back to main wallet
|
||||
await swapWallet({ context, extensionUrl, name: 'main' })
|
||||
|
||||
// Transfer to the secondary wallet
|
||||
await page.getByRole('button', { name: /transfer/i }).click()
|
||||
await page.keyboard.type(process.env.TEST_SECONDARY_WALLET_ADDRESS)
|
||||
await page.getByPlaceholder('0.00').fill('10')
|
||||
await page
|
||||
.getByRole('button', { name: /transfer/i })
|
||||
.nth(1)
|
||||
.click()
|
||||
|
||||
await signTx({ context, page })
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /cancel/i, includeHidden: false })
|
||||
.click()
|
||||
|
||||
// Check new balance
|
||||
await page.waitForTimeout(4000)
|
||||
mainBalance -= 10
|
||||
await test
|
||||
.expect(await getBalance({ context, page }))
|
||||
.toEqual(`$${mainBalance}`)
|
||||
|
||||
// Swap to secondary to check if the transfer was received
|
||||
await swapWallet({ context, extensionUrl, name: 'secondary' })
|
||||
|
||||
await test
|
||||
.expect(await getBalance({ context, page }))
|
||||
.toEqual(`$${secondaryBalance + 10}`)
|
||||
|
||||
// Set balance to 0 again for cleaning purposes
|
||||
await page.getByRole('button', { name: /withdraw/i }).click()
|
||||
await page
|
||||
.getByRole('button', { name: /withdraw/i })
|
||||
.nth(1)
|
||||
.click()
|
||||
|
||||
await signTx({ context, page })
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /cancel/i, includeHidden: false })
|
||||
.click()
|
||||
|
||||
// Back to main wallet
|
||||
await swapWallet({ context, extensionUrl, name: 'main' })
|
||||
})
|
||||
|
||||
test('can withdraw deposited sum successfully', async ({ context, page }) => {
|
||||
await page.getByRole('button', { name: /withdraw/i }).click()
|
||||
await page
|
||||
.getByRole('button', { name: /withdraw/i })
|
||||
.nth(1)
|
||||
.click()
|
||||
|
||||
await signTx({ context, page })
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: /cancel/i, includeHidden: false })
|
||||
.click()
|
||||
|
||||
// Check new balance
|
||||
await page.waitForTimeout(4000)
|
||||
await test.expect(await getBalance({ context, page })).toEqual('$0')
|
||||
})
|
||||
})
|
||||
|
|
|
@ -13,9 +13,9 @@ async function globalSetup() {
|
|||
const resp = await fetch(downloadUrl)
|
||||
|
||||
if (resp.ok && resp.body) {
|
||||
Readable.fromWeb(resp.body as any).pipe(
|
||||
unzipper.Extract({ path: folderPath }),
|
||||
)
|
||||
await Readable.fromWeb(resp.body as any)
|
||||
.pipe(unzipper.Extract({ path: folderPath }))
|
||||
.promise()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue