Skip to content

Commit

Permalink
Added the first part of selenium testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Carson Aberle committed Sep 15, 2023
1 parent 020bcda commit 2ca001b
Show file tree
Hide file tree
Showing 19 changed files with 781 additions and 6 deletions.
2 changes: 2 additions & 0 deletions packages/testing/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
dist
6 changes: 6 additions & 0 deletions packages/testing/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"root": true
}
2 changes: 2 additions & 0 deletions packages/testing/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
src
node_modules
161 changes: 161 additions & 0 deletions packages/testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# @sei-js/react

A React helper library for [@sei-js/core](https://www.npmjs.com/package/@sei-js/core) written in Typescript.

## Tutorial

For an in depth tutorial, please see [our documentation](https://docs.seinetwork.io/front-end-development/react-tutorial).

## Installation

```shell
yarn add @sei-js/react
```

# WalletProvider
The first step is to wrap your entire application in a Sei wallet provider and pass in a chainId, rest url, and rpc url.
```javascript
<SeiWalletProvider
chainConfiguration={{
chainId: 'atlantic-2',
restUrl: 'https://rest.atlantic-2.seinetwork.io/',
rpcUrl: 'https://rpc.atlantic-2.seinetwork.io'
}}>
<YourApp />
</SeiWalletProvider>
```

# Hooks

| Hook | Params |
|-----------------------------------------------|-----------------------|
| [useWallet](#useWallet) | --- |
| [useQueryClient](#useQueryClient) | (rpcAddress?: string) |
| [useSigningClient](#useSigningClient) | (rpcAddress?: string) |
| [useSeiCosmWasmClient](#useSeiCosmWasmClient) | --- |

## useWallet

A hook to connect one of our supported wallets to your application.


```javascript
import { useWallet } from '@sei-js/react';

const { offlineSigner, accounts, connectedWallet } = useWallet();
```

### Return Values

| Property | Type | Description |
|------------------|-----------|---------------------------------------------------------|
| connectedWallet | string? | The currently connected wallet |
| chainId | string | Sei chain id |
| restUrl | string | The rest url associated with the connected wallet |
| rpcUrl | string | The rpc url associated with the connected wallet |
| offlineSigner | object? | The offline signer associated with the connected wallet |
| accounts | object[]? | The accounts associated with the connected wallet |

## useQueryClient

```javascript
import { useQueryClient } from '@sei-js/react';

const { queryClient, isLoading } = useQueryClient();
```

| Property | Type | Description |
|-------------|------------------------|---------------------------------------------------------|
| queryClient | StargateSigningClient? | A stargate signing client. |
| isLoading | boolean | Boolean value for when the initial loading is happening |

## useSigningClient

```javascript
import { useSigningClient } from '@sei-js/react';

const { signingClient, isLoading } = useSigningClient();
```

| Property | Type | Description |
|---------------|------------------------|---------------------------------------------------------|
| signingClient | StargateSigningClient? | A stargate signing client. |
| isLoading | boolean | Boolean value for when the initial loading is happening |

## useSeiCosmWasmClient

```javascript
import { useSeiCosmWasmClient } from '@sei-js/react';

const { cosmWasmClient } = useSeiCosmWasmClient();
```

| Property | Type | Description |
|----------------|-----------------|-----------------------------------------|
| cosmWasmClient | CosmWasmClient? | A cosm wasm client for smart contracts. |

# UI Components
This package contains two helpful UI components for connecting to a wallet provider.

## \<WalletConnectButton />
This component renders a button that will open a modal to connect to a wallet provider.

```javascript
import React from "react";
import {useWallet, WalletConnectButton} from "../lib";

const Component = () => {
const { connectedWallet } = useWallet();

return (
<div>
<WalletConnectButton />
<p>Connected wallet: {connectedWallet?.walletInfo?.name || "---"}</p>
</div>
);
};

export default Component;

```
| Property | Type | Description |
|-----------------|--------------|-----------------------------------------------------------------------------|
| wallets | SeiWallet[]? | A stargate signing client. |
| buttonClassName | string | A css class name for styling the button |
| primaryColor | string | A hex value of the color you want to tint the text and icons with |
| secondaryColor | string | A secondary hex value of the color you want to tint the text and icons with |
| backgroundColor | string | A hex value of the color you want to use as a background |
*If your page has a <WalletConnectButton/> on the page it can be opened programmatically by calling the hook "useSelectWallet"*
## useSelectWallet()
This hook allows you to programmatically open and close the wallet modal.
```javascript
import React from "react";
import { useWallet, useSelectWallet } from "../lib";

const Component = () => {
const { connectedWallet } = useWallet();
const { openModal, closeModal } = useSelectWallet();

return (
<div>
<button onClick={openModal}>Open Modal</button>
<p>Connected wallet: {connectedWallet?.walletInfo?.name || "---"}</p>
</div>
);
};

export default Component;

```
### Other helpful packages
- [@sei-js/core](https://www.npmjs.com/package/@sei-js/core) - TypeScript library containing helper functions for wallet connection, transaction sig
ning, and RPC querying.
- [@sei-js/proto](https://www.npmjs.com/package/@sei-js/proto) - TypeScript library for Sei protobufs generated using Telescope
10 changes: 10 additions & 0 deletions packages/testing/babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-typescript"
],
"plugins": [
"@babel/plugin-transform-runtime"
]
}
5 changes: 5 additions & 0 deletions packages/testing/jest.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
};
50 changes: 50 additions & 0 deletions packages/testing/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{
"name": "@sei-js/testing",
"version": "3.0.2",
"description": "React library for helping with writing great tests for your Sei projects.",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"scripts": {
"prebuild": "rimraf dist",
"build": "yarn build:types && yarn build:js && yarn build:prettier",
"build:types": "tsc --project tsconfig.declarations.json",
"build:js": "babel src --out-dir dist --extensions '.js,.jsx,.ts,.tsx' --source-maps --copy-files",
"build:prettier": "prettier --write dist"
},
"homepage": "https://github.com/sei-protocol/sei-js#readme",
"keywords": [
"sei",
"javascript",
"typescript",
"react",
"jest",
"selenium"
],
"repository": "[email protected]:sei-protocol/sei-js.git",
"license": "MIT",
"private": false,
"dependencies": {
"adm-zip": "^0.5.10",
"axios": "^1.5.0",
"chromedriver": "^116.0.0",
"selenium-webdriver": "^4.12.0"
},
"peerDependencies": {
"react": "^17.0.0 || ^18.0.0",
"react-dom": "^17.0.0 || ^18.0.0"
},
"devDependencies": {
"@babel/core": "^7.22.9",
"@babel/plugin-transform-runtime": "^7.22.9",
"@babel/preset-env": "^7.22.9",
"@babel/preset-react": "^7.22.5",
"@babel/preset-typescript": "^7.22.5",
"@types/adm-zip": "^0.5.1",
"@types/react": "^17.0.0 || ^18.0.0",
"@types/react-outside-click-handler": "^1.3.1",
"@types/selenium-webdriver": "^4.1.16",
"prettier": "^3.0.0",
"react": "^17.0.0 || ^18.0.0",
"typescript": "5.1.6"
}
}
11 changes: 11 additions & 0 deletions packages/testing/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import * as process from 'process';
import { Buffer } from 'buffer';

// Polyfill process and buffer for browser
Object.assign(self, {
process,
global: self,
Buffer
});

export * from './lib';
1 change: 1 addition & 0 deletions packages/testing/src/lib/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './selenium';
Binary file added packages/testing/src/lib/selenium/compass.crx
Binary file not shown.
48 changes: 48 additions & 0 deletions packages/testing/src/lib/selenium/compass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { By, until, WebDriver } from 'selenium-webdriver';

export const setupWallet = async (driver: WebDriver, extensionId: string, seedPhrase: string, password: string) => {
await driver.get(`chrome-extension://${extensionId}/tabs/welcome.html`);

const createWalletButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Restore a wallet')]")), 100000);
await driver.wait(until.elementIsVisible(createWalletButton), 5000);
await createWalletButton.click();

const passwordInput = await driver.wait(until.elementLocated(By.xpath("//input[@placeholder='Enter your new password']")), 100000);
await driver.wait(until.elementIsVisible(passwordInput), 5000);
await passwordInput.click();
await passwordInput.sendKeys(password);

const confirmPasswordInput = await driver.wait(until.elementLocated(By.xpath("//input[@placeholder='Re-enter your new password']")), 100000);
await driver.wait(until.elementIsVisible(confirmPasswordInput), 5000);
await confirmPasswordInput.click();
await confirmPasswordInput.sendKeys(password);

const checkBox = await driver.wait(until.elementLocated(By.xpath('//*[@id="__plasmo"]/main/div/div/div[2]/form/div[3]/div/div')), 100000);
await driver.wait(until.elementIsVisible(checkBox), 5000);
await checkBox.click();

const createPasswordButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Create Password')]")), 100000);
await driver.wait(until.elementIsVisible(createPasswordButton), 5000);
await createPasswordButton.click();

const restoreWalletButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Restore a wallet')]")), 100000);
await driver.wait(until.elementIsVisible(restoreWalletButton), 5000);
await restoreWalletButton.click();

const passphraseButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Using a passphrase')]")), 100000);
await driver.wait(until.elementIsVisible(passphraseButton), 5000);
await passphraseButton.click();

const seedPhraseInput = await driver.wait(until.elementLocated(By.xpath("//textarea[@placeholder='cake pizza cat...']")), 100000);
await driver.wait(until.elementIsVisible(seedPhraseInput), 5000);
await seedPhraseInput.click();
await seedPhraseInput.sendKeys(seedPhrase);
};

export const confirmTransaction = async (driver: WebDriver, confirm = true) => {
//Confirm or reject transaction
};

export const unlockWallet = async (driver: WebDriver, password: string) => {
//Unlock wallet with password
};
Binary file added packages/testing/src/lib/selenium/fin.crx
Binary file not shown.
80 changes: 80 additions & 0 deletions packages/testing/src/lib/selenium/fin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { By, until, WebDriver } from 'selenium-webdriver';

const WAIT_TIME = 5000;
export const setupWallet = async (driver: WebDriver, extensionId: string, seedPhrase: string, password: string) => {
await driver.get(`chrome-extension://${extensionId}/tabs/welcome.html`);

const createWalletButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Restore a wallet')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(createWalletButton), WAIT_TIME);
await driver.sleep(1000);
await createWalletButton.click();

const passwordInput = await driver.wait(until.elementLocated(By.xpath("//input[@placeholder='Enter your new password']")), WAIT_TIME);
await driver.wait(until.elementIsVisible(passwordInput), WAIT_TIME);
await passwordInput.click();
await passwordInput.sendKeys(password);

const confirmPasswordInput = await driver.wait(until.elementLocated(By.xpath("//input[@placeholder='Re-enter your new password']")), WAIT_TIME);
await driver.wait(until.elementIsVisible(confirmPasswordInput), WAIT_TIME);
await confirmPasswordInput.click();
await confirmPasswordInput.sendKeys(password);

const checkBox = await driver.wait(until.elementLocated(By.xpath('//*[@id="__plasmo"]/main/div/div/div[2]/form/div[3]/div/div')), WAIT_TIME);
await driver.wait(until.elementIsVisible(checkBox), WAIT_TIME);
await checkBox.click();

const createPasswordButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Create Password')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(createPasswordButton), WAIT_TIME);
await createPasswordButton.click();

const restoreWalletButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Restore a wallet')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(restoreWalletButton), WAIT_TIME);
await restoreWalletButton.click();

// const passphraseButton = await driver.wait(until.elementLocated(By.xpath('//*[@id="__plasmo"]/main/div[2]/div/div[2]/div[1]/div/div[2]/div')), 100000);
const passphraseButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Using a passphrase / private key')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(passphraseButton), WAIT_TIME);
await passphraseButton.click();

const seedPhraseInput = await driver.wait(until.elementLocated(By.xpath("//textarea[@placeholder='cake pizza cat...']")), WAIT_TIME);
await driver.wait(until.elementIsVisible(seedPhraseInput), WAIT_TIME);
await seedPhraseInput.click();
await seedPhraseInput.sendKeys(seedPhrase);

const restoreButton = await driver.wait(until.elementLocated(By.xpath("//button[contains(text(), 'Restore')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(restoreButton), WAIT_TIME);
await restoreButton.click();

const clearButton = await driver.wait(until.elementLocated(By.xpath("//*[contains(text(), 'Clear')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(clearButton), WAIT_TIME);
await clearButton.click();

const nameInput = await driver.wait(until.elementLocated(By.xpath("//input[@value='']")), WAIT_TIME);
await driver.wait(until.elementIsVisible(nameInput), WAIT_TIME);
await nameInput.click();
await nameInput.sendKeys('Automated Wallet');

const startUsingButton = await driver.wait(until.elementLocated(By.xpath("//button[contains(text(), 'Start Using')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(startUsingButton), WAIT_TIME);
await startUsingButton.click();
};

export const connectToApp = async (driver: WebDriver, connect = true) => {
if (connect) {
const connectButton = await driver.wait(until.elementLocated(By.xpath("//button[contains(text(), 'Connect')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(connectButton), WAIT_TIME);
await connectButton.click();
} else {
const rejectButton = await driver.wait(until.elementLocated(By.xpath("//button[contains(text(), 'Reject')]")), WAIT_TIME);
await driver.wait(until.elementIsVisible(rejectButton), WAIT_TIME);
await rejectButton.click();
}
};

export const confirmTransaction = async (driver: WebDriver, confirm = true) => {
//Confirm or reject transaction
};

export const unlockWallet = async (driver: WebDriver, password: string) => {
//Unlock wallet with password
};
1 change: 1 addition & 0 deletions packages/testing/src/lib/selenium/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './wallet';
4 changes: 4 additions & 0 deletions packages/testing/src/lib/selenium/registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export type Extension = { id?: string; path: string; provider: 'compass' | 'fin' };

export const COMPASS_LATEST_RELEASE: Extension = { provider: 'compass', path: './compass.crx' };
export const FIN_LATEST_RELEASE: Extension = { provider: 'fin', path: './fin.crx' };
Loading

0 comments on commit 2ca001b

Please sign in to comment.