Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI test automation #3367

Open
wants to merge 14 commits into
base: development
Choose a base branch
from
33 changes: 33 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: e2e
on: workflow_dispatch

jobs:
playwright:
runs-on: ubuntu-latest
environment: ci
permissions: read-all
defaults:
run:
working-directory: "./wormhole-connect"
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
cache-dependency-path: "wormhole-connect/package-lock.json"
- name: Install dependencies
run: npm ci
- name: Install headless Chromium for Playwright Browser
run: npx playwright install chromium --with-deps --no-shell
- name: Run Playwright tests
run: npm run test:e2e
env:
REACT_APP_TEST_EVM_ADDR: ${{ secrets.REACT_APP_TEST_EVM_ADDR }}
REACT_APP_TEST_EVM_PK: ${{ secrets.REACT_APP_TEST_EVM_PK }}
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: wormhole-connect/playwright-report/
retention-days: 30
6 changes: 6 additions & 0 deletions wormhole-connect/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,9 @@ yarn-debug.log*
yarn-error.log*

tsconfig.tsbuildinfo

# Playwright
/blob-report/
/playwright/.cache/
/playwright-report/
/test-results/
4 changes: 2 additions & 2 deletions wormhole-connect/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<title>Wormhole Connect Demo App</title>
<script type="module" src="/src/demo.tsx"></script>
<title>Wormhole Connect Sample App</title>
<script type="module" src="/src/SampleApp.tsx"></script>
</head>
<body style="margin: 0"></body>
</html>
73 changes: 73 additions & 0 deletions wormhole-connect/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion wormhole-connect/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@
"checksdn": "npx tsx scripts/ofac/checkSdnListForUpdates.ts",
"checkForeignAssets": "npx tsx scripts/checkForeignAssetsConfig.ts",
"preview": "vite preview",
"unused:exports": "ts-unused-exports ./tsconfig.json --ignoreLocallyUsed"
"unused:exports": "ts-unused-exports ./tsconfig.json --ignoreLocallyUsed",
"test:e2e": "playwright test",
"test:e2e:ui": "playwright test --ui"
},
"browserslist": {
"production": [
Expand All @@ -103,6 +105,7 @@
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
"@babel/preset-env": "^7.24.4",
"@kev1n-peters/vite-plugin-node-polyfills": "^0.17.4",
"@playwright/test": "^1.50.1",
"@types/node": "^20",
"@types/node-fetch": "^2.6.3",
"@types/react": "^18.0.26",
Expand All @@ -112,6 +115,7 @@
"@typescript-eslint/eslint-plugin": "^5.48.1",
"@typescript-eslint/parser": "^5.48.1",
"@vitejs/plugin-react-swc": "^3.5.0",
"dotenv": "^16.4.7",
"env-cmd": "^10.1.0",
"eslint": "^8.31.0",
"eslint-config-prettier": "^8.6.0",
Expand Down
30 changes: 30 additions & 0 deletions wormhole-connect/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/e2e/specs',
timeout: 60000,
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 1,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:5173',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'], channel: 'chromium' },
},
],

webServer: {
command: 'npm run start',
url: 'http://localhost:5173',
reuseExistingServer: !process.env.CI,
timeout: 60000,
},
});
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import ErrorBoundary from './components/ErrorBoundary';
import DemoApp from './components/DemoApp';
import SampleApp from './components/SampleApp';

// This is the demo app used for local development
// This is the sample app used for local development

const root = ReactDOM.createRoot(document.querySelector('body') as HTMLElement);

root.render(
<React.StrictMode>
<ErrorBoundary>
<DemoApp />
<SampleApp />
</ErrorBoundary>
</React.StrictMode>,
);
9 changes: 9 additions & 0 deletions wormhole-connect/src/WormholeConnect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ export default function WormholeConnect({
config,
theme,
}: WormholeConnectProps) {
React.useEffect(() => {
// IMPORTANT: This is a workaround to expose the Redux store to the window object so it can be used in automated tests.
if (!globalThis.dispatchReduxAction) {
(window as any).dispatchReduxAction = (action: any) => {
store.dispatch(action);
};
}
}, []);

// Handle theme changes at any time
const muiTheme = React.useMemo(
() => generateTheme(theme ?? { mode: 'dark' }),
Expand Down
2 changes: 1 addition & 1 deletion wormhole-connect/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Header(props: Props) {
};
const { classes } = useStyles(styleProps);
return (
<div className={classes.title} data-test-id={props.testId}>
<div className={classes.title} data-testid={props.testId}>
{props.text}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@

/*
*
* For the purposes of the DemoApp config sandbox, we expose the same exports
* For the purposes of the SampleApp config sandbox, we expose the same exports
* that are available from the production @wormhole-foundation/wormhole-connect
* library.
*
* These can be referenced in the same way in the DemoApp sandbox so that the
* These can be referenced in the same way in the SampleApp sandbox so that the
* config works when it's copy and pasted into an actual integrator project.
*
* The exports are:
Expand All @@ -27,7 +27,7 @@
* - nttTestRoutesMainnet
* - nttTestRoutesTestnet
* These just call nttRoutes() with a working config so that we can
* easily test NTT in the DemoApp.
* easily test NTT in SampleApp.
*
*/
import { routes } from '@wormhole-foundation/sdk';
Expand Down Expand Up @@ -136,11 +136,11 @@
history.replaceState({}, '', url.toString());
};

const LOCAL_STORAGE_KEY_BG = 'wormhole-connect:demo:custom-bg';
const LOCAL_STORAGE_KEY_CONFIG = 'wormhole-connect:demo:custom-config';
const LOCAL_STORAGE_KEY_THEME = 'wormhole-connect:demo:custom-theme';
const LOCAL_STORAGE_KEY_BG = 'wormhole-connect:sample:custom-bg';
const LOCAL_STORAGE_KEY_CONFIG = 'wormhole-connect:sample:custom-config';
const LOCAL_STORAGE_KEY_THEME = 'wormhole-connect:sample:custom-theme';

function DemoApp() {
function SampleApp() {
const [customConfig, setCustomConfig] = useState<WormholeConnectConfig>();
const [customConfigOpen, setCustomConfigOpen] = useState(false);
const [customConfigInput, setCustomConfigInput] = useState(
Expand Down Expand Up @@ -196,13 +196,13 @@
localStorage.setItem(LOCAL_STORAGE_KEY_BG, input);
};

useEffect(emitCustomConfig, []);

Check warning on line 199 in wormhole-connect/src/components/SampleApp/index.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useEffect has missing dependencies: 'customConfigInput' and 'isLoadingCustomConfig'. Either include them or remove the dependency array
useEffect(emitCustomTheme, []);

Check warning on line 200 in wormhole-connect/src/components/SampleApp/index.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook useEffect has a missing dependency: 'customThemeInput'. Either include it or remove the dependency array. You can also replace multiple useState variables with useReducer if 'setCustomTheme' needs the current value of 'customThemeInput'

return (
<main style={{ background: backgroundColor }}>
<article>
<div id="demo-contents">
<div id="sample-app">
{!isLoadingCustomConfig && (
<WormholeConnect config={customConfig} theme={customTheme} />
)}
Expand All @@ -212,7 +212,7 @@
<aside>
<header>
<div>
<h1>Wormhole Connect - demo app</h1>
<h1>Wormhole Connect Sample App</h1>
</div>
</header>

Expand Down Expand Up @@ -420,4 +420,4 @@
);
}

export default DemoApp;
export default SampleApp;
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ article {
display: flex;
}

#demo-contents {
#sample-app {
flex-grow: 2;
}

Expand Down
7 changes: 7 additions & 0 deletions wormhole-connect/src/config/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ export type UiConfig = {

// Set to true to disable the ability to paste in a token address
disableUserInputtedTokens?: boolean;

// UI test options
testOptions?: TestOptions;
};

export type TestOptions = {
enableHeadlessSigner?: boolean;
};

export interface DefaultInputs {
Expand Down
5 changes: 4 additions & 1 deletion wormhole-connect/src/hooks/useConfirmTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,10 @@ const useConfirmTransaction = (props: Props): ReturnProps => {
try {
const fromConfig = config.chains[sourceChain];

if (fromConfig?.context === Context.ETH) {
if (
fromConfig?.context === Context.ETH &&
!config.ui.testOptions?.enableHeadlessSigner
) {
const chainId = fromConfig.chainId;

if (typeof chainId !== 'number') {
Expand Down
19 changes: 13 additions & 6 deletions wormhole-connect/src/routes/sdkv2/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
TokenId as TokenId,
TransferState,
TransactionId,
Signer,
} from '@wormhole-foundation/sdk';
import { Token } from 'config/tokens';

Expand Down Expand Up @@ -262,12 +263,18 @@ export class SDKv2Route {
throw quote.error;
}

const signer = await SDKv2Signer.fromChain(
fromChain,
senderAddress,
{},
TransferWallet.SENDING,
);
let signer: Signer;

if (config.ui.testOptions?.enableHeadlessSigner) {
signer = await SDKv2Signer.fromPrivateKey(fromChain);
} else {
signer = await SDKv2Signer.fromChain(
fromChain,
senderAddress,
{},
TransferWallet.SENDING,
);
}

let receipt = await route.initiate(
req,
Expand Down
Loading
Loading