feat(frontend): Setup FE E2E (#180)

This commit is contained in:
Juan Enrique Alcaraz 2024-08-28 10:07:41 +02:00 committed by GitHub
parent e6700b2e73
commit 0680bec431
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 735 additions and 884 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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/

File diff suppressed because it is too large Load diff

View file

@ -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",

View 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' },
// },
],
})

View 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

View 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()
})
})

View 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