Skip to content

Commit

Permalink
Merge pull request #22 from oasisprotocol/xz/dappwright
Browse files Browse the repository at this point in the history
Add dAppwright testing
  • Loading branch information
aefhm authored Feb 10, 2025
2 parents 82cb789 + 5f723d6 commit cc6be03
Show file tree
Hide file tree
Showing 9 changed files with 5,670 additions and 4,781 deletions.
91 changes: 91 additions & 0 deletions .github/workflows/ci-playwright.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: playwright-test
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
playwright-test:
runs-on: ubuntu-latest
services:
sapphire-localnet-ci:
image: ghcr.io/oasisprotocol/sapphire-localnet:latest
ports:
- 8545:8545
- 8546:8546
env:
OASIS_DOCKER_START_EXPLORER: no
options: >-
--rm
--health-cmd="test -f /CONTAINER_READY"
--health-start-period=90s
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 18

- uses: pnpm/action-setup@v4
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false

- name: Get pnpm store directory
id: pnpm-cache
run: |
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
working-directory: backend
run: pnpm install

- name: Build backend
working-directory: backend
run: pnpm build

- name: Deploy backend
working-directory: backend
id: deploy
run: |
echo "message_box_address=$(pnpm hardhat deploy localhost --network sapphire-localnet | grep -o '0x.*')" >> $GITHUB_OUTPUT
- name: Install dependencies
working-directory: frontend
run: pnpm install

- name: Build
working-directory: frontend
run: pnpm build

- name: Install Playwright dependencies
run: pnpm test:setup
working-directory: frontend

- name: Run playwright tests (with xvfb-run to support headed extension test)
working-directory: frontend
run: xvfb-run pnpm test
env:
VITE_MESSAGE_BOX_ADDR: ${{ steps.deploy.outputs.message_box_address }}

- name: Upload playwright test-results
if: ${{ failure() }}
uses: actions/upload-artifact@v4
with:
name: playwright-test-results
path: frontend/test-results
retention-days: 5
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
node_modules/
*.log

frontend/dist
frontend/playwright-report
frontend/test-results
8 changes: 5 additions & 3 deletions backend/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import '@nomicfoundation/hardhat-ethers'
import '@oasisprotocol/sapphire-hardhat'
import '@typechain/hardhat'
import canonicalize from 'canonicalize'
import { JsonRpcProvider } from 'ethers'
import { JsonRpcProvider, Wallet } from 'ethers'
import 'hardhat-watcher'
import { TASK_COMPILE } from 'hardhat/builtin-tasks/task-names'
import { HardhatUserConfig, task } from 'hardhat/config'
import { SiweMessage } from 'siwe'
import 'solidity-coverage'
import { HDAccountsUserConfig } from 'hardhat/types'

const TASK_EXPORT_ABIS = 'export-abis'

Expand Down Expand Up @@ -94,15 +95,16 @@ task('setMessage')
})

// Hardhat Node and sapphire-dev test mnemonic.
const TEST_HDWALLET = {
const TEST_HDWALLET: HDAccountsUserConfig = {
mnemonic: 'test test test test test test test test test test test junk',
path: "m/44'/60'/0'/0",
initialIndex: 0,
count: 20,
passphrase: '',
}
const firstPrivateKey = Wallet.fromPhrase(TEST_HDWALLET.mnemonic).privateKey

const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : TEST_HDWALLET
const accounts = process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [firstPrivateKey]

const config: HardhatUserConfig = {
networks: {
Expand Down
2 changes: 1 addition & 1 deletion frontend/.env.development
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
VITE_NETWORK=0x5afd
VITE_WEB3_GATEWAY=http://localhost:8545
VITE_MESSAGE_BOX_ADDR=0x2dE080e97B0caE9825375D31f5D0eD5751fDf16D
VITE_MESSAGE_BOX_ADDR=0x5FbDB2315678afecb367f032d93F642f64180aa3
10 changes: 9 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"preview": "vite preview"
"preview": "vite preview",
"test": "playwright test",
"test:setup": "playwright install --with-deps"
},
"dependencies": {
"@fontsource-variable/figtree": "^5.1.1",
Expand All @@ -17,6 +19,8 @@
"@metamask/jazzicon": "^2.0.0",
"@oasisprotocol/demo-starter-backend": "workspace:^",
"@oasisprotocol/sapphire-ethers-v6": "^6.0.1",
"@playwright/test": "^1.47.2",
"@tenkeylabs/dappwright": "^2.9.1",
"ethers": "^6.10.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
Expand All @@ -30,6 +34,10 @@
"@vitejs/plugin-react-swc": "^3.5.0",
"typescript": "^5.5.3",
"vite": "^5.4.8",
"vite-plugin-node-polyfills": "^0.23.0",
"vite-plugin-svgr": "^4.2.0"
},
"peerDependencies": {
"playwright-core": ">1.0"
}
}
50 changes: 50 additions & 0 deletions frontend/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { defineConfig, devices } from '@playwright/test'

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
timeout: 2 * 60 * 1000,
testDir: './test/',
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 ? 1 : 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'], ['list', { printSteps: true }]],
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
baseURL: process.env.FRONTEND_URL || 'http://localhost:5173/',
trace: 'on-first-retry',
headless: false,
video: 'on-first-retry',
screenshot: {
mode: 'only-on-failure',
fullPage: true,
},
contextOptions: {
recordVideo: {
dir: 'test-results',
}
}
},
/* Configure projects for major browsers */
projects: [
{
name: 'main',
testDir: './test/',
use: { ...devices['Desktop Chrome'] },
},
],
/* Run your local dev server before starting the tests */
webServer: {
command: 'pnpm dev',
url: process.env.FRONTEND_URL || 'http://localhost:5173/',
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
stderr: 'pipe',
},
})
59 changes: 59 additions & 0 deletions frontend/test/e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { BrowserContext, expect, test as baseTest } from '@playwright/test'
import dappwright, { Dappwright, MetaMaskWallet } from '@tenkeylabs/dappwright'

export const test = baseTest.extend<{
context: BrowserContext
wallet: Dappwright
}>({
context: async ({}, use) => {
// Launch context with extension
const [wallet, _, context] = await dappwright.bootstrap('', {
wallet: 'metamask',
version: MetaMaskWallet.recommendedVersion,
seed: 'test test test test test test test test test test test junk', // Hardhat's default https://hardhat.org/hardhat-network/docs/reference#accounts
headless: false,
})

// Add Sapphire Localnet as a custom network
await wallet.addNetwork({
networkName: 'Sapphire Localnet',
rpc: 'http://localhost:8545',
chainId: 23293,
symbol: 'ROSE',
})

await use(context)
},

wallet: async ({ context }, use) => {
const metamask = await dappwright.getWallet('metamask', context)

await use(metamask)
},
})

test.beforeEach(async ({ page }) => {
await page.goto('http://localhost:5173')
})

// TODO: improve test without reloading wallet page
test('set and view message', async ({ wallet, page }) => {
// Load page
await wallet.page.reload()
await wallet.page.getByRole('button', { name: 'Connect' }).click()

// Set a message
await page.locator(':text-matches("0x.{40}")').fill('hola amigos')
await page.getByRole('button', { name: 'Set Message' }).click()
await wallet.page.reload()
await wallet.page.getByRole('button', { name: 'Confirm' }).click()

// Reveal the message
await expect(page.getByRole('button', { name: 'Set Message' })).toBeVisible({ timeout: 60_000 })
await page.locator('[data-label="Tap to reveal"]').click()
await wallet.page.reload()
await wallet.page.getByRole('button', { name: 'Confirm' }).click()

// Assert message has been set
await expect(page.locator('[data-label="Tap to reveal"]').locator('input')).toHaveValue('hola amigos')
})
3 changes: 2 additions & 1 deletion frontend/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { execSync } from 'node:child_process'
import { defineConfig } from 'vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'
import react from '@vitejs/plugin-react-swc'
import svgr from 'vite-plugin-svgr'

// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), svgr()],
plugins: [react(), svgr(), nodePolyfills()],
define: {
APP_VERSION: JSON.stringify(process.env.npm_package_version),
BUILD_COMMIT: JSON.stringify(execSync('git rev-parse HEAD').toString()),
Expand Down
Loading

0 comments on commit cc6be03

Please sign in to comment.