diff --git a/README.md b/README.md new file mode 100644 index 0000000..bf81792 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +## Wallet Test Framework: Coinbase + +A tool to automate the Coinbase wallet use use with Wallet Test Framework. + +## Installation + +### Node + +This project requires Nodejs version 20.6 or later. + +### Dependencies + +```bash +npm install +``` + +### Chrome Extension + +The glue requires a local copy of Coinbase Wallet. The publicly available extension may be fetched with: + +```bash +wget \ + -O coinbase.crx \ + 'https://clients2.google.com/service/update2/crx?response=redirect&prodversion=118.0.5993.70&acceptformat=crx2,crx3&x=id%3Dhnfanknocfeofbddgcijnmhnfnkdnaad%26uc' +``` + +## Building + +```bash +npm run build +``` + +### Tests & Linting (Optional) + +```bash +npm test +``` + +## Running + +```bash +npx glue-coinbase \ + --extension-path /path/to/crx/file +``` diff --git a/package-lock.json b/package-lock.json index a71ba2e..c05b914 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "MIT", "dependencies": { "@wallet-test-framework/glue": "^0.7.0", - "@wallet-test-framework/glue-ws": "file:../glue-ws", + "@wallet-test-framework/glue-ws": "^0.1.0", "meow": "^12.1.1", "selenium-webdriver": "^4.14.0" }, @@ -32,32 +32,6 @@ "typescript": "^5.2.2" } }, - "../glue-ws": { - "name": "@wallet-test-framework/glue-ws", - "version": "0.1.0-dev", - "license": "MIT", - "dependencies": { - "@wallet-test-framework/glue": "^0.7.0", - "rpc-websockets": "^7.6.1" - }, - "devDependencies": { - "@jgoz/esbuild-plugin-typecheck": "^3.1.0", - "@trivago/prettier-plugin-sort-imports": "^4.2.1", - "@tsconfig/recommended": "^1.0.3", - "@types/mocha": "^10.0.3", - "@types/node": "^20.3.1", - "@types/ws": "^8.5.8", - "@typescript-eslint/eslint-plugin": "6.9.0", - "@typescript-eslint/parser": "^6.9.0", - "esbuild": "0.19.5", - "eslint": "8.52.0", - "eslint-config-prettier": "^9.0.0", - "mocha": "^10.2.0", - "prettier": "3.0.3", - "tsx": "^3.14.0", - "typescript": "^5.2.2" - } - }, "node_modules/@aashutoshrathi/word-wrap": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", @@ -226,6 +200,17 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", + "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", @@ -1064,8 +1049,13 @@ "integrity": "sha512-pSvX7ebNaxudDQ+G/AS3xpiJ0A6xM85yZcmVNq/1fWqKgtHBkSTATtIzCDMYWE9v/6jw0AekX/lwINbX9SF6Xg==" }, "node_modules/@wallet-test-framework/glue-ws": { - "resolved": "../glue-ws", - "link": true + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@wallet-test-framework/glue-ws/-/glue-ws-0.1.0.tgz", + "integrity": "sha512-Gi4NgQ6LRVMmEc4CH8lSNodRnCsEl5LVcUxtFFtVTDGUCD4eJ1G2XnHCX+m6oCczmLlXgnXlrZt781ECfRdXaQ==", + "dependencies": { + "@wallet-test-framework/glue": "^0.7.0", + "rpc-websockets": "^7.6.1" + } }, "node_modules/acorn": { "version": "8.10.0", @@ -1166,6 +1156,19 @@ "node": ">=8" } }, + "node_modules/bufferutil": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz", + "integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1590,6 +1593,11 @@ "node": ">=0.10.0" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2078,6 +2086,17 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/node-gyp-build": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.6.1.tgz", + "integrity": "sha512-24vnklJmyRS8ViBNI8KbtK/r/DmXQMRiOMXTNz2nrTnAYUwjmEEbnnpB/+kt+yWRv73bPsSPRFddrcIbAxSiMQ==", + "optional": true, + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -2269,6 +2288,11 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2302,6 +2326,25 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rpc-websockets": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-7.6.2.tgz", + "integrity": "sha512-+M1fOYMPxvOQDHbSItkD/an4fRwPZ1Nft1zv48G84S0TyChG2A1GXmjWkbs3o2NxW+q36H9nM2uLo5yojTrPaA==", + "dependencies": { + "@babel/runtime": "^7.17.2", + "eventemitter3": "^4.0.7", + "uuid": "^8.3.2", + "ws": "^8.5.0" + }, + "funding": { + "type": "paypal", + "url": "https://paypal.me/kozjak" + }, + "optionalDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2548,11 +2591,32 @@ "punycode": "^2.1.0" } }, + "node_modules/utf-8-validate": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz", + "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=6.14.2" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 0d2fe84..7e9117c 100644 --- a/package.json +++ b/package.json @@ -53,7 +53,7 @@ }, "dependencies": { "@wallet-test-framework/glue": "^0.7.0", - "@wallet-test-framework/glue-ws": "file:../glue-ws", + "@wallet-test-framework/glue-ws": "^0.1.0", "meow": "^12.1.1", "selenium-webdriver": "^4.14.0" } diff --git a/src/glue.ts b/src/glue.ts index cc50bcc..0717559 100644 --- a/src/glue.ts +++ b/src/glue.ts @@ -80,10 +80,16 @@ class CoinbaseDriver { this.glue = glue; } - public static async create(glue: CoinbaseGlue): Promise { + public static async create( + glue: CoinbaseGlue, + extensionPath: string, + browserVersion: string | null | undefined, + ): Promise { const chrome = new Chrome.Options(); - chrome.setBrowserVersion("117.0.5938.149"); - chrome.addExtensions("/home/sam/coinbase.crx"); + if (typeof browserVersion === "string") { + chrome.setBrowserVersion(browserVersion); + } + chrome.addExtensions(extensionPath); const driver = await new Builder() .forBrowser("chrome") @@ -276,13 +282,31 @@ class CoinbaseDriver { export class CoinbaseGlue extends Glue { private static async buildDriver( glue: CoinbaseGlue, + extensionPath: string, + browserVersion: string | null | undefined, ): Promise { - const coinbase = await CoinbaseDriver.create(glue); + const coinbase = await CoinbaseDriver.create( + glue, + extensionPath, + browserVersion, + ); await coinbase.setup(); return coinbase; } - private readonly driver = CoinbaseGlue.buildDriver(this); + private readonly driver; + + constructor( + extensionPath: string, + browserVersion: string | null | undefined, + ) { + super(); + this.driver = CoinbaseGlue.buildDriver( + this, + extensionPath, + browserVersion, + ); + } async launch(url: string): Promise { const cb = await this.driver; @@ -310,7 +334,7 @@ export class CoinbaseGlue extends Glue { await settingsLink.click(); const networksMenu = await driver.findElement( - By.css("[data-testid='settings-networks-menu-cell-pressable']"), + By.css("[data-testid='network-setting-cell-pressable']"), ); await driver.wait(until.elementIsVisible(networksMenu), 2000); await networksMenu.click(); diff --git a/src/index.ts b/src/index.ts index c414c2a..eba87d7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -29,6 +29,13 @@ export async function main(args: string[]): Promise { argv: args.slice(2), importMeta: import.meta, flags: { + extensionPath: { + type: "string", + isRequired: true, + }, + browserVersion: { + type: "string", + }, testUrl: { type: "string", default: "https://wallet-test-framework.herokuapp.com/", @@ -36,7 +43,10 @@ export async function main(args: string[]): Promise { }, }); - const implementation = new CoinbaseGlue(); + const implementation = new CoinbaseGlue( + cli.flags.extensionPath, + cli.flags.browserVersion, + ); const serveResult = serveGlue(implementation, { port: 0 }); try {