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:
|
||||
paths:
|
||||
- .github/workflows/cosmwasm-basic.yml
|
||||
- apps/**
|
||||
- cosmwasm/**
|
||||
- apps/**
|
||||
- "!apps/transfers/frontend/**"
|
||||
push:
|
||||
branches: main
|
||||
paths:
|
||||
- .github/workflows/cosmwasm-basic.yml
|
||||
- apps/**
|
||||
- cosmwasm/**
|
||||
- apps/**
|
||||
- "!apps/transfers/frontend/**"
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
|
@ -29,7 +31,6 @@ defaults:
|
|||
working-directory: apps/mtcs/contracts/cw-tee-mtcs
|
||||
|
||||
jobs:
|
||||
|
||||
test-wasm:
|
||||
name: Test Suite
|
||||
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_ENCLAVE_PUBLIC_KEY=02ef4f843722d9badf8f5571d8f20cd1a21022fe52b9257d3a235c85dfc0ce11c0
|
||||
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
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
**/extensions/*
|
||||
!**/extensions/.gitkeep
|
||||
|
||||
# next.js
|
||||
/.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",
|
||||
"start": "next start",
|
||||
"lint": "next lint",
|
||||
"test": "playwright test",
|
||||
"test:ui": "playwright test --ui",
|
||||
"generate": "node ./scripts/generate.js && node ./scripts/rebuild-component-index.js",
|
||||
"rebuild-component-index": "node ./scripts/rebuild-component-index.js"
|
||||
},
|
||||
|
@ -24,13 +26,16 @@
|
|||
"devDependencies": {
|
||||
"@keplr-wallet/types": "0.12.103",
|
||||
"@netlify/plugin-nextjs": "^5.3.3",
|
||||
"@playwright/test": "^1.46.1",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"@tailwindcss/typography": "^0.5.13",
|
||||
"@types/lodash": "^4.17.5",
|
||||
"@types/node": "^20",
|
||||
"@types/react": "npm:types-react@rc",
|
||||
"@types/react-dom": "npm:types-react-dom@rc",
|
||||
"@types/unzipper": "^0.10.10",
|
||||
"babel-plugin-react-compiler": "0.0.0-experimental-696af53-20240625",
|
||||
"dotenv": "16.4.5",
|
||||
"eslint": "^8",
|
||||
"eslint-plugin-react-compiler": "^0.0.0",
|
||||
"postcss": "^8",
|
||||
|
@ -38,7 +43,8 @@
|
|||
"prettier-plugin-tailwindcss": "^0.6.5",
|
||||
"tailwindcss": "^3.4.4",
|
||||
"tiny-invariant": "^1.3.3",
|
||||
"typescript": "^5"
|
||||
"typescript": "^5",
|
||||
"unzipper": "^0.12.3"
|
||||
},
|
||||
"overrides": {
|
||||
"@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