feat(frontend): Setup FE E2E (#180)
This commit is contained in:
parent
e6700b2e73
commit
0680bec431
11 changed files with 735 additions and 884 deletions
7
.github/workflows/cosmwasm-basic.yml
vendored
7
.github/workflows/cosmwasm-basic.yml
vendored
|
@ -6,14 +6,16 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/cosmwasm-basic.yml
|
- .github/workflows/cosmwasm-basic.yml
|
||||||
- apps/**
|
|
||||||
- cosmwasm/**
|
- cosmwasm/**
|
||||||
|
- apps/**
|
||||||
|
- "!apps/transfers/frontend/**"
|
||||||
push:
|
push:
|
||||||
branches: main
|
branches: main
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/cosmwasm-basic.yml
|
- .github/workflows/cosmwasm-basic.yml
|
||||||
- apps/**
|
|
||||||
- cosmwasm/**
|
- cosmwasm/**
|
||||||
|
- apps/**
|
||||||
|
- "!apps/transfers/frontend/**"
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CARGO_INCREMENTAL: 0
|
CARGO_INCREMENTAL: 0
|
||||||
|
@ -29,7 +31,6 @@ defaults:
|
||||||
working-directory: apps/mtcs/contracts/cw-tee-mtcs
|
working-directory: apps/mtcs/contracts/cw-tee-mtcs
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
test-wasm:
|
test-wasm:
|
||||||
name: Test Suite
|
name: Test Suite
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
NEXT_PUBLIC_CHAIN_ID=testing
|
|
||||||
NEXT_PUBLIC_CHAIN_RPC_URL=http://0.0.0.0:26657
|
|
||||||
NEXT_PUBLIC_CHAIN_REST_URL=http://0.0.0.0:1317
|
|
||||||
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=02ef4f843722d9badf8f5571d8f20cd1a21022fe52b9257d3a235c85dfc0ce11c0
|
|
||||||
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=wasm1jfgr0vgunezkhfmdy7krrupu6yjhx224nxtjptll2ylkkqhyzeshrspu9
|
|
|
@ -3,3 +3,9 @@ NEXT_PUBLIC_CHAIN_RPC_URL=http://0.0.0.0:26657
|
||||||
NEXT_PUBLIC_CHAIN_REST_URL=http://0.0.0.0:1317
|
NEXT_PUBLIC_CHAIN_REST_URL=http://0.0.0.0:1317
|
||||||
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=02ef4f843722d9badf8f5571d8f20cd1a21022fe52b9257d3a235c85dfc0ce11c0
|
NEXT_PUBLIC_ENCLAVE_PUBLIC_KEY=02ef4f843722d9badf8f5571d8f20cd1a21022fe52b9257d3a235c85dfc0ce11c0
|
||||||
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=wasm1jfgr0vgunezkhfmdy7krrupu6yjhx224nxtjptll2ylkkqhyzeshrspu9
|
NEXT_PUBLIC_TRANSFERS_CONTRACT_ADDRESS=wasm1jfgr0vgunezkhfmdy7krrupu6yjhx224nxtjptll2ylkkqhyzeshrspu9
|
||||||
|
|
||||||
|
# E2E Testing
|
||||||
|
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_WALLET_PASSWORD=;pzPCXB^@92byC
|
||||||
|
|
7
apps/transfers/frontend/.gitignore
vendored
7
apps/transfers/frontend/.gitignore
vendored
|
@ -7,7 +7,12 @@
|
||||||
.yarn/install-state.gz
|
.yarn/install-state.gz
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/test-results/
|
||||||
|
/playwright-report/
|
||||||
|
/blob-report/
|
||||||
|
/playwright/.cache/
|
||||||
|
**/extensions/*
|
||||||
|
!**/extensions/.gitkeep
|
||||||
|
|
||||||
# next.js
|
# next.js
|
||||||
/.next/
|
/.next/
|
||||||
|
|
1379
apps/transfers/frontend/package-lock.json
generated
1379
apps/transfers/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -7,6 +7,8 @@
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
|
"test": "playwright test",
|
||||||
|
"test:ui": "playwright test --ui",
|
||||||
"generate": "node ./scripts/generate.js && node ./scripts/rebuild-component-index.js",
|
"generate": "node ./scripts/generate.js && node ./scripts/rebuild-component-index.js",
|
||||||
"rebuild-component-index": "node ./scripts/rebuild-component-index.js"
|
"rebuild-component-index": "node ./scripts/rebuild-component-index.js"
|
||||||
},
|
},
|
||||||
|
@ -24,13 +26,16 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@keplr-wallet/types": "0.12.103",
|
"@keplr-wallet/types": "0.12.103",
|
||||||
"@netlify/plugin-nextjs": "^5.3.3",
|
"@netlify/plugin-nextjs": "^5.3.3",
|
||||||
|
"@playwright/test": "^1.46.1",
|
||||||
"@tailwindcss/forms": "^0.5.7",
|
"@tailwindcss/forms": "^0.5.7",
|
||||||
"@tailwindcss/typography": "^0.5.13",
|
"@tailwindcss/typography": "^0.5.13",
|
||||||
"@types/lodash": "^4.17.5",
|
"@types/lodash": "^4.17.5",
|
||||||
"@types/node": "^20",
|
"@types/node": "^20",
|
||||||
"@types/react": "npm:types-react@rc",
|
"@types/react": "npm:types-react@rc",
|
||||||
"@types/react-dom": "npm:types-react-dom@rc",
|
"@types/react-dom": "npm:types-react-dom@rc",
|
||||||
|
"@types/unzipper": "^0.10.10",
|
||||||
"babel-plugin-react-compiler": "0.0.0-experimental-696af53-20240625",
|
"babel-plugin-react-compiler": "0.0.0-experimental-696af53-20240625",
|
||||||
|
"dotenv": "16.4.5",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
"eslint-plugin-react-compiler": "^0.0.0",
|
"eslint-plugin-react-compiler": "^0.0.0",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
|
@ -38,7 +43,8 @@
|
||||||
"prettier-plugin-tailwindcss": "^0.6.5",
|
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||||
"tailwindcss": "^3.4.4",
|
"tailwindcss": "^3.4.4",
|
||||||
"tiny-invariant": "^1.3.3",
|
"tiny-invariant": "^1.3.3",
|
||||||
"typescript": "^5"
|
"typescript": "^5",
|
||||||
|
"unzipper": "^0.12.3"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@types/react": "npm:types-react@rc",
|
"@types/react": "npm:types-react@rc",
|
||||||
|
|
78
apps/transfers/frontend/playwright.config.ts
Normal file
78
apps/transfers/frontend/playwright.config.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import { defineConfig, devices } from '@playwright/test'
|
||||||
|
import path from 'path'
|
||||||
|
import dotenv from 'dotenv'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read environment variables from file.
|
||||||
|
* https://github.com/motdotla/dotenv
|
||||||
|
*/
|
||||||
|
dotenv.config({ path: path.resolve(__dirname, '.env.local') })
|
||||||
|
|
||||||
|
const baseURL = process.env.TEST_BASE_URL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See https://playwright.dev/docs/test-configuration.
|
||||||
|
*/
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './tests/e2e',
|
||||||
|
/* Run tests in files in parallel */
|
||||||
|
fullyParallel: true,
|
||||||
|
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||||
|
forbidOnly: !!process.env.CI,
|
||||||
|
/* Retry on CI only */
|
||||||
|
retries: process.env.CI ? 2 : 0,
|
||||||
|
/* Opt out of parallel tests on CI. */
|
||||||
|
workers: process.env.CI ? 1 : undefined,
|
||||||
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
|
reporter: 'html',
|
||||||
|
/* 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('/')`. */
|
||||||
|
baseURL,
|
||||||
|
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
/* Run your local dev server before starting the tests */
|
||||||
|
webServer: {
|
||||||
|
command: 'npm run dev',
|
||||||
|
url: baseURL,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
globalSetup: require.resolve('./tests/global.setup'),
|
||||||
|
/* Configure projects for major browsers */
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
name: 'chromium',
|
||||||
|
use: { ...devices['Desktop Chrome'] },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// name: 'firefox',
|
||||||
|
// use: { ...devices['Desktop Firefox'] },
|
||||||
|
// },
|
||||||
|
|
||||||
|
// {
|
||||||
|
// name: 'webkit',
|
||||||
|
// use: { ...devices['Desktop Safari'] },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/* Test against mobile viewports. */
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Chrome',
|
||||||
|
// use: { ...devices['Pixel 5'] },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Mobile Safari',
|
||||||
|
// use: { ...devices['iPhone 12'] },
|
||||||
|
// },
|
||||||
|
|
||||||
|
/* Test against branded browsers. */
|
||||||
|
// {
|
||||||
|
// name: 'Microsoft Edge',
|
||||||
|
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// name: 'Google Chrome',
|
||||||
|
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
})
|
0
apps/transfers/frontend/tests/e2e/extensions/.gitkeep
Normal file
0
apps/transfers/frontend/tests/e2e/extensions/.gitkeep
Normal file
89
apps/transfers/frontend/tests/e2e/fixtures.ts
Normal file
89
apps/transfers/frontend/tests/e2e/fixtures.ts
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
import {
|
||||||
|
test as baseTest,
|
||||||
|
chromium,
|
||||||
|
expect,
|
||||||
|
BrowserContext,
|
||||||
|
} from '@playwright/test'
|
||||||
|
import path from 'path'
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
page: async ({ context }, use) => {
|
||||||
|
const page = await context.newPage()
|
||||||
|
|
||||||
|
await use(page)
|
||||||
|
await page.close()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
export default test
|
15
apps/transfers/frontend/tests/e2e/transfers.spec.ts
Normal file
15
apps/transfers/frontend/tests/e2e/transfers.spec.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import test from './fixtures'
|
||||||
|
|
||||||
|
test.beforeEach(async ({ page }) => {
|
||||||
|
await page.goto('/')
|
||||||
|
})
|
||||||
|
|
||||||
|
test.describe('Transfers', () => {
|
||||||
|
test('app should render correctly', async ({ page }) => {
|
||||||
|
await test.expect(page.getByText('Balance:')).toBeVisible()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('balance should be 0 at first', async ({ page }) => {
|
||||||
|
await test.expect(page.getByText('$0')).toBeVisible()
|
||||||
|
})
|
||||||
|
})
|
23
apps/transfers/frontend/tests/global.setup.ts
Normal file
23
apps/transfers/frontend/tests/global.setup.ts
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { existsSync } from 'fs'
|
||||||
|
import { Readable } from 'stream'
|
||||||
|
import path from 'path'
|
||||||
|
import unzipper from 'unzipper'
|
||||||
|
|
||||||
|
async function globalSetup() {
|
||||||
|
const folderName = `keplr-extension-manifest-v3-v${process.env.TEST_KEPLR_EXTENSION_VERSION}`
|
||||||
|
const folderPath = path.join(__dirname, 'e2e', 'extensions', folderName)
|
||||||
|
|
||||||
|
// Download and decompress Keplr extension if it does not exist yet
|
||||||
|
if (!existsSync(folderPath)) {
|
||||||
|
const downloadUrl = `https://github.com/chainapsis/keplr-wallet/releases/download/v${process.env.TEST_KEPLR_EXTENSION_VERSION}/${folderName}.zip`
|
||||||
|
const resp = await fetch(downloadUrl)
|
||||||
|
|
||||||
|
if (resp.ok && resp.body) {
|
||||||
|
Readable.fromWeb(resp.body as any).pipe(
|
||||||
|
unzipper.Extract({ path: folderPath }),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default globalSetup
|
Loading…
Reference in a new issue