From 88588722270107470752eec256bbf11bdd214e82 Mon Sep 17 00:00:00 2001 From: yushi Date: Fri, 7 Mar 2025 00:21:24 +0800 Subject: [PATCH 01/11] support ledger emulator --- packages/yoroi-extension/chrome/constants.js | 2 + .../chrome/manifest.development.js | 5 +- .../yoroi-extension/chrome/manifest.test.js | 5 +- packages/yoroi-extension/ledger/types/enum.js | 1 + packages/yoroi-extension/ledger/utils/cmn.js | 3 + .../ledger/utils/speculosHttpTransport.js | 81 +++++++++++ packages/yoroi-extension/package-lock.json | 130 ++++++++++++------ packages/yoroi-extension/package.json | 2 + 8 files changed, 183 insertions(+), 46 deletions(-) create mode 100644 packages/yoroi-extension/ledger/utils/speculosHttpTransport.js diff --git a/packages/yoroi-extension/chrome/constants.js b/packages/yoroi-extension/chrome/constants.js index 6394b5a431..29cb3295aa 100644 --- a/packages/yoroi-extension/chrome/constants.js +++ b/packages/yoroi-extension/chrome/constants.js @@ -63,3 +63,5 @@ export const injectedScripts = [ 'cardanoApiInject.js', 'initialInject.js', ]; + +export const speculosEndpoint = 'http://localhost:5000'; diff --git a/packages/yoroi-extension/chrome/manifest.development.js b/packages/yoroi-extension/chrome/manifest.development.js index c55040cd3b..5feacb05d6 100644 --- a/packages/yoroi-extension/chrome/manifest.development.js +++ b/packages/yoroi-extension/chrome/manifest.development.js @@ -2,9 +2,7 @@ /* eslint-disable import/no-unused-modules */ import buildManifest from './manifest.template'; -import { - genCSP, -} from './constants'; +import { genCSP, speculosEndpoint } from './constants'; import { Servers, serverToPermission, @@ -21,6 +19,7 @@ export default (isDebug: boolean, shouldInjectConnector: boolean): * => buildMan 'connect-src': [ serverToPermission(Servers.Primary), serverToPermission(Servers.Testnet), + speculosEndpoint, ], 'frame-src': [ POOLS_UI_URL_FOR_YOROI, diff --git a/packages/yoroi-extension/chrome/manifest.test.js b/packages/yoroi-extension/chrome/manifest.test.js index 8cc58bf78e..899e3488f8 100644 --- a/packages/yoroi-extension/chrome/manifest.test.js +++ b/packages/yoroi-extension/chrome/manifest.test.js @@ -6,9 +6,7 @@ import { Ports, portToPermission, } from '../scripts/connections'; -import { - genCSP, -} from './constants'; +import { genCSP, speculosEndpoint } from './constants'; import pkg from '../package.json'; export default (isDebug: boolean, shouldInjectConnector: boolean): * => buildManifest({ @@ -19,6 +17,7 @@ export default (isDebug: boolean, shouldInjectConnector: boolean): * => buildMan additional: { 'connect-src': [ portToPermission(Ports.DevBackendServer), + speculosEndpoint, ], }, }), diff --git a/packages/yoroi-extension/ledger/types/enum.js b/packages/yoroi-extension/ledger/types/enum.js index d7823db40c..07b9944a90 100644 --- a/packages/yoroi-extension/ledger/types/enum.js +++ b/packages/yoroi-extension/ledger/types/enum.js @@ -35,5 +35,6 @@ export const TRANSPORT_ID = Object.freeze({ U2F: 'u2f', WEB_USB: 'webusb', WEB_HID: 'webhid', + SPECULOS_HTTP: 'speculos_http', }); export type TransportIdType = $Values; diff --git a/packages/yoroi-extension/ledger/utils/cmn.js b/packages/yoroi-extension/ledger/utils/cmn.js index 39408573f8..95b11e6e7f 100644 --- a/packages/yoroi-extension/ledger/utils/cmn.js +++ b/packages/yoroi-extension/ledger/utils/cmn.js @@ -107,6 +107,9 @@ export const makeTransport = async (transportId: TransportIdType): any => { case TRANSPORT_ID.WEB_HID: transportFactory = require('@ledgerhq/hw-transport-webhid').default; break; + case TRANSPORT_ID.SPECULOS_HTTP: + transportFactory = require('./speculosHttpTransport').default; + break; default: throw new Error('Transport protocol not supported'); } diff --git a/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js b/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js new file mode 100644 index 0000000000..09dbb73e4f --- /dev/null +++ b/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js @@ -0,0 +1,81 @@ +// adapted from https://github.com/LedgerHQ/ledger-live/blob/develop/libs/ledgerjs/packages/hw-transport-http/src/HttpTransport.ts + +import Transport from "@ledgerhq/hw-transport"; +import { TransportError } from "@ledgerhq/errors"; +import axios from "axios"; + +const SPECULOS_ENDPOINT = 'http://localhost:5000/apdu'; +/** + * HTTP transport implementation + */ + +export default class HttpTransport extends Transport { + static isSupported = (): Promise => Promise.resolve(typeof fetch === "function"); + // this transport is not discoverable + static list = (): any => Promise.resolve([]); + static listen = (_observer: any) => ({ + unsubscribe: () => {}, + }); + static check = async (url: string, timeout = 5000) => { + const response = await axios({ + url, + timeout, + }); + + if (response.status !== 200) { + throw new TransportError( + "failed to access HttpTransport(" + url + "): status " + response.status, + "HttpTransportNotAccessible", + ); + } + }; + + static async open(url: string, timeout?: number): Promise { + await HttpTransport.check(url, timeout); + return new HttpTransport(url); + } + + + static async create(): Promise { + return new HttpTransport(SPECULOS_ENDPOINT); + } + + url: string; + + constructor(url: string) { + super(); + this.url = url; + } + + async exchange(apdu: Buffer): Promise { + const apduHex = apdu.toString("hex"); + const response = await axios({ + method: "POST", + url: this.url, + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + data: JSON.stringify({ + data: apduHex, + }), + }); + + if (response.status !== 200) { + throw new TransportError( + "failed to communicate to server. code=" + response.status, + "HttpTransportStatus" + response.status, + ); + } + + const body: any = await response.data; + if (body.error) throw body.error; + return Buffer.from(body.data, "hex"); + } + + setScrambleKey() {} + + close(): Promise { + return Promise.resolve(); + } +} diff --git a/packages/yoroi-extension/package-lock.json b/packages/yoroi-extension/package-lock.json index a359a8136a..dabb5a9bb9 100644 --- a/packages/yoroi-extension/package-lock.json +++ b/packages/yoroi-extension/package-lock.json @@ -21,6 +21,8 @@ "@emurgo/cross-csl-browser": "6.2.1", "@emurgo/yoroi-eutxo-txs": "1.0.5", "@emurgo/yoroi-lib": "2.2.6", + "@ledgerhq/errors": "^6.19.1", + "@ledgerhq/hw-transport": "^6.31.4", "@ledgerhq/hw-transport-u2f": "5.36.0-deprecated", "@ledgerhq/hw-transport-webauthn": "5.36.0-deprecated", "@ledgerhq/hw-transport-webhid": "5.51.1", @@ -3646,28 +3648,28 @@ } }, "node_modules/@ledgerhq/devices": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.3.0.tgz", - "integrity": "sha512-h5Scr+yIae8yjPOViCHLdMjpqn4oC2Whrsq8LinRxe48LEGMdPqSV1yY7+3Ch827wtzNpMv+/ilKnd8rY+rTlg==", + "version": "8.4.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/devices/-/devices-8.4.4.tgz", + "integrity": "sha512-sz/ryhe/R687RHtevIE9RlKaV8kkKykUV4k29e7GAVwzHX1gqG+O75cu1NCJUHLbp3eABV5FdvZejqRUlLis9A==", "dependencies": { - "@ledgerhq/errors": "^6.16.4", + "@ledgerhq/errors": "^6.19.1", "@ledgerhq/logs": "^6.12.0", "rxjs": "^7.8.1", "semver": "^7.3.5" } }, "node_modules/@ledgerhq/errors": { - "version": "6.16.4", - "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.16.4.tgz", - "integrity": "sha512-M57yFaLYSN+fZCX0E0zUqOmrV6eipK+s5RhijHoUNlHUqrsvUz7iRQgpd5gRgHB5VkIjav7KdaZjKiWGcHovaQ==" + "version": "6.19.1", + "resolved": "https://registry.npmjs.org/@ledgerhq/errors/-/errors-6.19.1.tgz", + "integrity": "sha512-75yK7Nnit/Gp7gdrJAz0ipp31CCgncRp+evWt6QawQEtQKYEDfGo10QywgrrBBixeRxwnMy1DP6g2oCWRf1bjw==" }, "node_modules/@ledgerhq/hw-transport": { - "version": "6.30.6", - "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.30.6.tgz", - "integrity": "sha512-fT0Z4IywiuJuZrZE/+W0blkV5UCotDPFTYKLkKCLzYzuE6javva7D/ajRaIeR+hZ4kTmKF4EqnsmDCXwElez+w==", + "version": "6.31.4", + "resolved": "https://registry.npmjs.org/@ledgerhq/hw-transport/-/hw-transport-6.31.4.tgz", + "integrity": "sha512-6c1ir/cXWJm5dCWdq55NPgCJ3UuKuuxRvf//Xs36Bq9BwkV2YaRQhZITAkads83l07NAdR16hkTWqqpwFMaI6A==", "dependencies": { - "@ledgerhq/devices": "^8.3.0", - "@ledgerhq/errors": "^6.16.4", + "@ledgerhq/devices": "^8.4.4", + "@ledgerhq/errors": "^6.19.1", "@ledgerhq/logs": "^6.12.0", "events": "^3.3.0" } @@ -10232,6 +10234,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -12894,6 +12908,19 @@ "ieee754": "^1.1.13" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -13192,12 +13219,9 @@ "integrity": "sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==" }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -13217,9 +13241,9 @@ "dev": true }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dependencies": { "es-errors": "^1.3.0" }, @@ -13228,13 +13252,14 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -15384,15 +15409,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -15422,6 +15452,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stdin": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", @@ -15599,11 +15641,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -15775,9 +15817,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -20587,6 +20629,14 @@ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", diff --git a/packages/yoroi-extension/package.json b/packages/yoroi-extension/package.json index d2037f7d26..389061ef42 100644 --- a/packages/yoroi-extension/package.json +++ b/packages/yoroi-extension/package.json @@ -159,6 +159,8 @@ "@emurgo/cross-csl-browser": "6.2.1", "@emurgo/yoroi-eutxo-txs": "1.0.5", "@emurgo/yoroi-lib": "2.2.6", + "@ledgerhq/errors": "^6.19.1", + "@ledgerhq/hw-transport": "^6.31.4", "@ledgerhq/hw-transport-u2f": "5.36.0-deprecated", "@ledgerhq/hw-transport-webauthn": "5.36.0-deprecated", "@ledgerhq/hw-transport-webhid": "5.51.1", From 7ff1d196057dfba0bff3a4f0b54ad4e47abb50bd Mon Sep 17 00:00:00 2001 From: yushi Date: Fri, 7 Mar 2025 15:13:48 +0800 Subject: [PATCH 02/11] e2e ledger wallet test with emulator --- .../helpers/ledgerEmulatorController.js | 40 +++++++ packages/e2e-tests/helpers/windowManager.js | 1 + packages/e2e-tests/pages/addNewWallet.page.js | 34 ++++-- .../e2e-tests/pages/ledgerConnect.page.js | 65 +++++++++++ .../test/hw/connectLedgerWallet.test.js | 102 ++++++++++++++++++ .../test/hw/connectTrezorWallet.test.js | 2 +- .../storage/database/prepackaged/networks.js | 12 +-- .../yoroi-extension/chrome/manifest.test.js | 11 +- .../yoroi-extension/ledger/stores/index.js | 11 +- 9 files changed, 251 insertions(+), 27 deletions(-) create mode 100644 packages/e2e-tests/helpers/ledgerEmulatorController.js create mode 100644 packages/e2e-tests/pages/ledgerConnect.page.js create mode 100644 packages/e2e-tests/test/hw/connectLedgerWallet.test.js diff --git a/packages/e2e-tests/helpers/ledgerEmulatorController.js b/packages/e2e-tests/helpers/ledgerEmulatorController.js new file mode 100644 index 0000000000..eaa82899c2 --- /dev/null +++ b/packages/e2e-tests/helpers/ledgerEmulatorController.js @@ -0,0 +1,40 @@ +const SPECULOS_ENDPOINT = 'http://localhost:5000'; + +export class LedgerEmulatorController { + constructor(logger) { + this.logger = logger; + } + + async _click(button) { + await fetch( + `${SPECULOS_ENDPOINT}/button/${button}`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: '{"action":"press-and-release"}', + } + ); + } + + async clickBoth() { + await this._click('both'); + } + + async clickLeft() { + await this._click('left'); + } + + async clickRight() { + await this._click('right'); + } + + // todo + async readScreen() { + // POST to http://localhost:5000/apdu + // '{"apduHex":"d700000000"}' + // and receive from http://localhost:5000/events?stream=true + // 'data: {"text": "...screen text...", "x": 42, "y": 42, "w": 45, "h": 13, "clear": false}' + } +} diff --git a/packages/e2e-tests/helpers/windowManager.js b/packages/e2e-tests/helpers/windowManager.js index 8f4909d82f..d9f1aa15a9 100644 --- a/packages/e2e-tests/helpers/windowManager.js +++ b/packages/e2e-tests/helpers/windowManager.js @@ -8,6 +8,7 @@ export const popupConnectorWindowTitle = 'Yoroi Dapp Connector'; export const extensionTabName = 'Yoroi'; export const faqTabName = 'Yoroi - EMURGO'; export const trezorConnectTabName = 'Trezor'; +export const ledgerConnectTabName = 'Ledger Connect | Yoroi'; export const backgroungTabName = 'background'; export const serviceWorkersTabName = 'chrome://serviceworker-internals'; export const serviceWorkersLink = 'chrome://serviceworker-internals'; diff --git a/packages/e2e-tests/pages/addNewWallet.page.js b/packages/e2e-tests/pages/addNewWallet.page.js index 004fe0ee57..7896781ef2 100644 --- a/packages/e2e-tests/pages/addNewWallet.page.js +++ b/packages/e2e-tests/pages/addNewWallet.page.js @@ -24,12 +24,24 @@ class AddNewWallet extends WalletCommonBase { locator: '.WalletConnectHWOptionDialog_connectTrezor', method: 'css', }; + ledgerHWButtonLocator = { + locator: '.WalletConnectHWOptionDialog_connectLedger', + method: 'css', + }; checkDialogLocator = { locator: '.CheckDialog', method: 'css', }; nextButtonLocator = { - locator: 'primaryButton', + locator: 'dialog-next-button', + method: 'id', + }; + connectButtonLocator = { + locator: 'dialog-connect-button', + method: 'id', + }; + saveButtonLocator = { + locator: 'dialog-save-button', method: 'id', }; connectDialogLocator = { @@ -95,6 +107,12 @@ class AddNewWallet extends WalletCommonBase { await this.click(this.trezorHWButtonLocator); }); } + async selectLedgerHW() { + this.logger.info(`AddNewWallet::selectLedgerHW is called`); + await this.waitPresentedAndAct(this.ledgerHWButtonLocator, async () => { + await this.click(this.ledgerHWButtonLocator); + }); + } async confirmChecking() { this.logger.info(`AddNewWallet::confirmChecking is called`); await this.customWaitIsPresented(this.checkDialogLocator, fiveSeconds, quarterSecond); @@ -107,13 +125,13 @@ class AddNewWallet extends WalletCommonBase { } }); } - async connectTrezor() { - this.logger.info(`AddNewWallet::connectTrezor is called`); + async connectHardwareWallet() { + this.logger.info(`AddNewWallet::connectHardwareWallet is called`); await this.customWaitIsPresented(this.connectDialogLocator, fiveSeconds, quarterSecond); - await this.waitPresentedAndAct(this.nextButtonLocator, async () => { - const btnEnabled = await this.buttonIsEnabled(this.nextButtonLocator); + await this.waitPresentedAndAct(this.connectButtonLocator, async () => { + const btnEnabled = await this.buttonIsEnabled(this.connectButtonLocator); if (btnEnabled) { - await this.click(this.nextButtonLocator); + await this.click(this.connectButtonLocator); } else { throw new Error(`The button ${this.nextButtonLocator.locator} is disabled`); } @@ -126,8 +144,8 @@ class AddNewWallet extends WalletCommonBase { await this.input(this.hwWalletNameInputLocator, walletName); } async saveHWInfo() { - await this.waitPresentedAndAct(this.nextButtonLocator, async () => { - await this.click(this.nextButtonLocator); + await this.waitPresentedAndAct(this.saveButtonLocator, async () => { + await this.click(this.saveButtonLocator); }); } // ::end trezor connect section diff --git a/packages/e2e-tests/pages/ledgerConnect.page.js b/packages/e2e-tests/pages/ledgerConnect.page.js new file mode 100644 index 0000000000..5ef0da8565 --- /dev/null +++ b/packages/e2e-tests/pages/ledgerConnect.page.js @@ -0,0 +1,65 @@ +import { defaultWaitTimeout, halfSecond } from '../helpers/timeConstants.js'; +import BasePage from './basepage.js'; + +class LedgerConnect extends BasePage { + // locators + /* + dontAskAgainCheckboxLocator = { + locator: '.custom-checkbox', + method: 'css', + }; + confirmUsingTrezorButtonLocator = { + locator: '.confirm', + method: 'css', + }; + exportTrezorButtonLocator = { + locator: '.confirm', + method: 'css', + }; + */ + nanoSButtonLocator = { + locator: 'button', + method: 'css', + }; + // functions + /* + async tickCheckbox() { + this.logger.info(`TrezorConnect::tickCheckbox is called`); + const result = await this.customWaiter( + async () => { + const elAmount = await this.findElements(this.dontAskAgainCheckboxLocator); + this.logger.info(`TrezorConnect::tickCheckbox. Elements found: ${elAmount.length}`); + // this conditions was found empirically + return elAmount.length === 4; + }, + defaultWaitTimeout, + halfSecond + ); + if (result) { + const allCheckboxes = await this.findElements(this.dontAskAgainCheckboxLocator); + // this conditions was found empirically + await allCheckboxes[0].click(); + } else { + this.logger.error(`TrezorConnect::tickCheckbox A correct checkbox is not found`); + throw new Error('A correct checkbox is not found'); + } + } + async allowConnection() { + this.logger.info(`TrezorConnect::allowConnection is called`); + await this.waitForElement(this.confirmUsingTrezorButtonLocator); + await this.clickByScript(this.confirmUsingTrezorButtonLocator); + } + async allowPubKeysExport() { + this.logger.info(`TrezorConnect::allowPubKeysExport is called`); + await this.waitForElement(this.exportTrezorButtonLocator); + await this.clickByScript(this.exportTrezorButtonLocator); + } + */ + async selectNanoS() { + this.logger.info(`LedgerConnect::selectNanoS is called`); + await this.waitForElement(this.nanoSButtonLocator); + await this.clickByScript(this.nanoSButtonLocator); + } +} + +export default LedgerConnect; diff --git a/packages/e2e-tests/test/hw/connectLedgerWallet.test.js b/packages/e2e-tests/test/hw/connectLedgerWallet.test.js new file mode 100644 index 0000000000..a10289b9d1 --- /dev/null +++ b/packages/e2e-tests/test/hw/connectLedgerWallet.test.js @@ -0,0 +1,102 @@ +import { expect } from 'chai'; +import { getDriver } from '../../utils/driverBootstrap.js'; +import { customAfterEach } from '../../utils/customHooks.js'; +import { getTestLogger } from '../../utils/utils.js'; +import { LedgerEmulatorController } from '../../helpers/ledgerEmulatorController.js'; +import { + WindowManager, + extensionTabName, + ledgerConnectTabName, +} from '../../helpers/windowManager.js'; +import BasePage from '../../pages/basepage.js'; +import InitialStepsPage from '../../pages/initialSteps.page.js'; +import AddNewWallet from '../../pages/addNewWallet.page.js'; +import LedgerConnect from '../../pages/ledgerConnect.page.js'; +import TransactionsSubTab from '../../pages/wallet/walletTab/walletTransactions.page.js'; +import { oneMinute } from '../../helpers/timeConstants.js'; + +describe('Connect Ledger HW wallet', function () { + this.timeout(2 * oneMinute); + let webdriver = null; + let logger = null; + let ledgerLogger = null; + let ledgerController = null; + let wmLogger = null; + let windowManager = null; + + before(function (done) { + webdriver = getDriver(); + logger = getTestLogger(this.test.parent.title); + ledgerLogger = getTestLogger('ledger', this.test.parent.title); + ledgerController = new LedgerEmulatorController(ledgerLogger); + wmLogger = getTestLogger('windowManager', this.test.parent.title); + windowManager = new WindowManager(webdriver, wmLogger); + const basePage = new BasePage(webdriver, logger); + basePage.goToExtension(); + done(); + }); + + it('Initials steps', async function () { + await windowManager.init(); + const initialStepsPage = new InitialStepsPage(webdriver, logger); + await initialStepsPage.skipInitialSteps(); + }); + + it('Ledger initialization', async function () { + //todo ping Speculos + }); + + it('Selecting Connect HW wallet', async function () { + const addNewWalletPage = new AddNewWallet(webdriver, logger); + await addNewWalletPage.selectConnectHW(); + await addNewWalletPage.selectCardanoNetwork(); + await addNewWalletPage.selectLedgerHW(); + await addNewWalletPage.confirmChecking(); + await addNewWalletPage.connectHardwareWallet(); + }); + + it('Approve connection', async function () { + await windowManager.findNewWindowAndSwitchTo(ledgerConnectTabName); + const ledgerConnectPage = new LedgerConnect(webdriver, logger); + + await ledgerConnectPage.selectNanoS(); + await new Promise(resolve => setTimeout(resolve, 1000)); + await ledgerController.clickBoth(); + await windowManager.waitForClosingAndSwitchTo(ledgerConnectTabName, extensionTabName); + }); + + it('Enter wallet details', async function () { + const addNewWalletPage = new AddNewWallet(webdriver, logger); + await addNewWalletPage.enterHWWalletName('Speculos'); + await addNewWalletPage.saveHWInfo(); + }); + + it('Check new wallet', async function () { + const transactionsPage = new TransactionsSubTab(webdriver, logger); + await transactionsPage.waitPrepareWalletBannerIsClosed(); + await transactionsPage.closeUpdatesModalWindow(); + const txPageIsDisplayed = await transactionsPage.isDisplayed(); + expect(txPageIsDisplayed, 'The transactions page is not displayed').to.be.true; + const walletInfo = await transactionsPage.getSelectedWalletInfo(); + expect(walletInfo.balance, 'The wallet balance is different').to.equal( + 0, + ); + expect(walletInfo.name, `The wallet name should be YoroSpeculos.`).to.equal( + 'YoroSpeculos', + ); + expect( + walletInfo.plate, + `The wallet plate should be PAXX-9560` + ).to.equal('PAXX-9560'); + }); + + afterEach(function (done) { + customAfterEach(this, webdriver, logger); + done(); + }); + + after(async function () { + const basePage = new BasePage(webdriver, logger); + basePage.closeBrowser(); + }); +}); diff --git a/packages/e2e-tests/test/hw/connectTrezorWallet.test.js b/packages/e2e-tests/test/hw/connectTrezorWallet.test.js index f0a8d53d77..869b77cecc 100644 --- a/packages/e2e-tests/test/hw/connectTrezorWallet.test.js +++ b/packages/e2e-tests/test/hw/connectTrezorWallet.test.js @@ -54,7 +54,7 @@ describe('Connect Trezor HW wallet', function () { await addNewWalletPage.selectCardanoNetwork(); await addNewWalletPage.selectTrezorHW(); await addNewWalletPage.confirmChecking(); - await addNewWalletPage.connectTrezor(); + await addNewWalletPage.connectHardwareWallet(); }); it('Approve connection', async function () { diff --git a/packages/yoroi-extension/app/api/ada/lib/storage/database/prepackaged/networks.js b/packages/yoroi-extension/app/api/ada/lib/storage/database/prepackaged/networks.js index 66d911fa7d..53a458b6e2 100644 --- a/packages/yoroi-extension/app/api/ada/lib/storage/database/prepackaged/networks.js +++ b/packages/yoroi-extension/app/api/ada/lib/storage/database/prepackaged/networks.js @@ -15,9 +15,7 @@ export const networks = Object.freeze({ NetworkName: 'Cardano Mainnet', NetworkFeatureName: 'mainnet', Backend: { - BackendService: environment.isTest() - ? 'http://localhost:21000' - : 'https://api.yoroiwallet.com', + BackendService: 'https://api.yoroiwallet.com', TokenInfoService: 'https://cdn.yoroiwallet.com', BackendServiceZero: 'https://zero.yoroiwallet.com', @@ -58,9 +56,7 @@ export const networks = Object.freeze({ NetworkName: 'Cardano Preprod Testnet', NetworkFeatureName: 'preprod', Backend: { - BackendService: environment.isTest() - ? 'http://localhost:21000' - : 'https://preprod-backend.yoroiwallet.com', + BackendService: 'https://preprod-backend.yoroiwallet.com', TokenInfoService: 'https://stage-cdn.yoroiwallet.com', BackendServiceZero: 'https://yoroi-backend-zero-preprod.emurgornd.com', @@ -100,9 +96,7 @@ export const networks = Object.freeze({ NetworkName: 'Cardano Preview Testnet', NetworkFeatureName: 'preview', Backend: { - BackendService: environment.isTest() - ? 'http://localhost:21000' - : 'https://preview-backend.emurgornd.com', + BackendService: 'https://preview-backend.emurgornd.com', TokenInfoService: 'https://stage-cdn.yoroiwallet.com', BackendServiceZero: 'https://yoroi-backend-zero-preview.emurgornd.com', }, diff --git a/packages/yoroi-extension/chrome/manifest.test.js b/packages/yoroi-extension/chrome/manifest.test.js index 899e3488f8..1416c71ef2 100644 --- a/packages/yoroi-extension/chrome/manifest.test.js +++ b/packages/yoroi-extension/chrome/manifest.test.js @@ -2,21 +2,18 @@ /* eslint-disable import/no-unused-modules */ import buildManifest from './manifest.template'; -import { - Ports, - portToPermission, -} from '../scripts/connections'; +import { Servers, serverToPermission } from '../scripts/connections'; import { genCSP, speculosEndpoint } from './constants'; import pkg from '../package.json'; export default (isDebug: boolean, shouldInjectConnector: boolean): * => buildManifest({ - description: '[localhost] Cardano ADA wallet', - defaultTitle: '[localhost] Yoroi', + description: 'e2e test Cardano ADA wallet', + defaultTitle: 'e2e test Yoroi', contentSecurityPolicy: genCSP({ isDev: isDebug, additional: { 'connect-src': [ - portToPermission(Ports.DevBackendServer), + serverToPermission(Servers.Primary), speculosEndpoint, ], }, diff --git a/packages/yoroi-extension/ledger/stores/index.js b/packages/yoroi-extension/ledger/stores/index.js index 4d8637ab62..44ea3e3b46 100644 --- a/packages/yoroi-extension/ledger/stores/index.js +++ b/packages/yoroi-extension/ledger/stores/index.js @@ -10,6 +10,9 @@ import type { URLParams } from '../types/cmn'; import type { TransportIdType } from '../types/enum'; import { TRANSPORT_ID } from '../types/enum'; import packageInfo from '../../package.json'; +import { NetworkType, type ConfigType } from '../../config/config-types'; + +declare var CONFIG: ConfigType; const appVersion = packageInfo.version; @@ -32,8 +35,11 @@ export default class RootStore { // Parse Transport let transportId: TransportIdType; - const urlTransportId = urlParams.get('transport') || DEFAULT_TRANSPORT_PROTOCOL; - switch (urlTransportId) { + if (CONFIG.network.name === NetworkType.TEST) { + transportId = TRANSPORT_ID.SPECULOS_HTTP; + } else { + const urlTransportId = urlParams.get('transport') || DEFAULT_TRANSPORT_PROTOCOL; + switch (urlTransportId) { case TRANSPORT_ID.U2F: transportId = TRANSPORT_ID.U2F; break; @@ -43,6 +49,7 @@ export default class RootStore { default: transportId = DEFAULT_TRANSPORT_PROTOCOL; break; + } } // Parse Locale From a4f95dd11f4dc52ed5f0000b898b18e84ab88c77 Mon Sep 17 00:00:00 2001 From: Denis Nebytov Date: Mon, 10 Mar 2025 15:16:48 +0300 Subject: [PATCH 03/11] using port 5001 --- packages/e2e-tests/helpers/ledgerEmulatorController.js | 2 +- packages/yoroi-extension/chrome/constants.js | 2 +- packages/yoroi-extension/chrome/manifest.test.js | 1 + packages/yoroi-extension/ledger/utils/speculosHttpTransport.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/e2e-tests/helpers/ledgerEmulatorController.js b/packages/e2e-tests/helpers/ledgerEmulatorController.js index eaa82899c2..26272d63ce 100644 --- a/packages/e2e-tests/helpers/ledgerEmulatorController.js +++ b/packages/e2e-tests/helpers/ledgerEmulatorController.js @@ -1,4 +1,4 @@ -const SPECULOS_ENDPOINT = 'http://localhost:5000'; +const SPECULOS_ENDPOINT = 'http://localhost:5001'; export class LedgerEmulatorController { constructor(logger) { diff --git a/packages/yoroi-extension/chrome/constants.js b/packages/yoroi-extension/chrome/constants.js index 29cb3295aa..93598c2962 100644 --- a/packages/yoroi-extension/chrome/constants.js +++ b/packages/yoroi-extension/chrome/constants.js @@ -64,4 +64,4 @@ export const injectedScripts = [ 'initialInject.js', ]; -export const speculosEndpoint = 'http://localhost:5000'; +export const speculosEndpoint = 'http://localhost:5001'; diff --git a/packages/yoroi-extension/chrome/manifest.test.js b/packages/yoroi-extension/chrome/manifest.test.js index 1416c71ef2..f19334694b 100644 --- a/packages/yoroi-extension/chrome/manifest.test.js +++ b/packages/yoroi-extension/chrome/manifest.test.js @@ -14,6 +14,7 @@ export default (isDebug: boolean, shouldInjectConnector: boolean): * => buildMan additional: { 'connect-src': [ serverToPermission(Servers.Primary), + serverToPermission(Servers.Testnet), speculosEndpoint, ], }, diff --git a/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js b/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js index 09dbb73e4f..53ba435540 100644 --- a/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js +++ b/packages/yoroi-extension/ledger/utils/speculosHttpTransport.js @@ -4,7 +4,7 @@ import Transport from "@ledgerhq/hw-transport"; import { TransportError } from "@ledgerhq/errors"; import axios from "axios"; -const SPECULOS_ENDPOINT = 'http://localhost:5000/apdu'; +const SPECULOS_ENDPOINT = 'http://localhost:5001/apdu'; /** * HTTP transport implementation */ From 0eff96d63313645bc405d1dfa6e79e48654bc56c Mon Sep 17 00:00:00 2001 From: Denis Nebytov Date: Tue, 11 Mar 2025 10:42:02 +0300 Subject: [PATCH 04/11] added reading ledger screen --- .../helpers/ledgerEmulatorController.js | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/helpers/ledgerEmulatorController.js b/packages/e2e-tests/helpers/ledgerEmulatorController.js index 26272d63ce..7674b91050 100644 --- a/packages/e2e-tests/helpers/ledgerEmulatorController.js +++ b/packages/e2e-tests/helpers/ledgerEmulatorController.js @@ -1,5 +1,7 @@ const SPECULOS_ENDPOINT = 'http://localhost:5001'; +class LedgerEmulatorControllerError extends Error {} + export class LedgerEmulatorController { constructor(logger) { this.logger = logger; @@ -32,9 +34,20 @@ export class LedgerEmulatorController { // todo async readScreen() { - // POST to http://localhost:5000/apdu - // '{"apduHex":"d700000000"}' - // and receive from http://localhost:5000/events?stream=true - // 'data: {"text": "...screen text...", "x": 42, "y": 42, "w": 45, "h": 13, "clear": false}' + try { + const eventsResponse = await fetch( + `${SPECULOS_ENDPOINT}/events?currentscreenonly=true` + ); + if (!eventsResponse.ok) { + throw new LedgerEmulatorController('Not able to receive events for the current screen'); + } + const eventsObj = await eventsResponse.json(); + const eventsText = eventsObj.events.map(evt => evt.text).join(' '); + + return eventsText; + } catch (error) { + console.error(error); + throw new LedgerEmulatorController('Some error happen: ', error); + } } } From 97b4368d2f1c817847f604bc0615db78745c7985 Mon Sep 17 00:00:00 2001 From: Denis Nebytov Date: Tue, 11 Mar 2025 10:42:38 +0300 Subject: [PATCH 05/11] Cheking cardano app state before the test --- .../test/hw/connectLedgerWallet.test.js | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/e2e-tests/test/hw/connectLedgerWallet.test.js b/packages/e2e-tests/test/hw/connectLedgerWallet.test.js index a10289b9d1..b9263df619 100644 --- a/packages/e2e-tests/test/hw/connectLedgerWallet.test.js +++ b/packages/e2e-tests/test/hw/connectLedgerWallet.test.js @@ -43,7 +43,8 @@ describe('Connect Ledger HW wallet', function () { }); it('Ledger initialization', async function () { - //todo ping Speculos + const ledgerState = await ledgerController.readScreen(); + expect(ledgerState, 'Cardano app is not ready').to.equal('Cardano is ready'); }); it('Selecting Connect HW wallet', async function () { @@ -78,16 +79,9 @@ describe('Connect Ledger HW wallet', function () { const txPageIsDisplayed = await transactionsPage.isDisplayed(); expect(txPageIsDisplayed, 'The transactions page is not displayed').to.be.true; const walletInfo = await transactionsPage.getSelectedWalletInfo(); - expect(walletInfo.balance, 'The wallet balance is different').to.equal( - 0, - ); - expect(walletInfo.name, `The wallet name should be YoroSpeculos.`).to.equal( - 'YoroSpeculos', - ); - expect( - walletInfo.plate, - `The wallet plate should be PAXX-9560` - ).to.equal('PAXX-9560'); + expect(walletInfo.balance, 'The wallet balance is different').to.equal(0); + expect(walletInfo.name, `The wallet name should be Speculos.`).to.equal('Speculos'); + expect(walletInfo.plate, `The wallet plate should be PAXX-9560`).to.equal('PAXX-9560'); }); afterEach(function (done) { From ac2c54c0a3be4cd23e07569ad45ced92761bfd51 Mon Sep 17 00:00:00 2001 From: Denis Nebytov Date: Tue, 11 Mar 2025 10:43:04 +0300 Subject: [PATCH 06/11] minor update in enterHWWalletName --- packages/e2e-tests/pages/addNewWallet.page.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/e2e-tests/pages/addNewWallet.page.js b/packages/e2e-tests/pages/addNewWallet.page.js index 7896781ef2..c65c35a897 100644 --- a/packages/e2e-tests/pages/addNewWallet.page.js +++ b/packages/e2e-tests/pages/addNewWallet.page.js @@ -139,8 +139,7 @@ class AddNewWallet extends WalletCommonBase { } async enterHWWalletName(walletName) { await this.customWaitIsPresented(this.hwWalletNameInputLocator, fiveSeconds, quarterSecond); - // the label "Emulator" is used in TrezorEmulatorController.emulatorSetup() - await this.clearInputUpdatingForm(this.hwWalletNameInputLocator, 'Emulator'.length); + await this.clearInputAll(this.hwWalletNameInputLocator); await this.input(this.hwWalletNameInputLocator, walletName); } async saveHWInfo() { From 03648c4699b7ebe21c945c8234b9f8b97079d6cd Mon Sep 17 00:00:00 2001 From: Denis Nebytov Date: Tue, 11 Mar 2025 19:42:38 +0300 Subject: [PATCH 07/11] added ids for HW wallet network selection --- packages/e2e-tests/helpers/constants.js | 5 +++ packages/e2e-tests/pages/addNewWallet.page.js | 41 ++++++++++++++----- .../components/widgets/options/OptionBlock.js | 12 +++++- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/packages/e2e-tests/helpers/constants.js b/packages/e2e-tests/helpers/constants.js index daf725a62b..945694f106 100644 --- a/packages/e2e-tests/helpers/constants.js +++ b/packages/e2e-tests/helpers/constants.js @@ -70,6 +70,11 @@ export const WalletWordsSize = Object.freeze({ Shelley: 15, Daedalus: 24, }); +export const CardanoNetworks = Object.freeze({ + MN: 'mainnet', + PP: 'preprod', + PV: 'preview', +}); export const adaInLovelaces = 1000000; export const projectRootDir = path.resolve(__dirname, '..'); diff --git a/packages/e2e-tests/pages/addNewWallet.page.js b/packages/e2e-tests/pages/addNewWallet.page.js index c65c35a897..956762b5cc 100644 --- a/packages/e2e-tests/pages/addNewWallet.page.js +++ b/packages/e2e-tests/pages/addNewWallet.page.js @@ -1,5 +1,6 @@ import { halfMinute, fiveSeconds, quarterSecond } from '../helpers/timeConstants.js'; import WalletCommonBase from './walletCommonBase.page.js'; +import { CardanoNetworks } from '../helpers/constants.js'; class AddNewWallet extends WalletCommonBase { // locators @@ -15,11 +16,19 @@ class AddNewWallet extends WalletCommonBase { locator: 'connectHardwareWalletButton', method: 'id', }; - // ::start trezor connect section - cardanoNetworkButtonLocator = { - locator: '.PickCurrencyOptionDialog_cardano', - method: 'css', + mainnetNetworkButtonLocator = { + locator: 'connectHWWallet-selectMainnetNetwork-button', + method: 'id', + }; + preprodNetworkButtonLocator = { + locator: 'connectHWWallet-selectPreprodNetwork-button', + method: 'id', }; + previewNetworkButtonLocator = { + locator: 'connectHWWallet-selectPreviewNetwork-button', + method: 'id', + }; + // ::start HW connect section trezorHWButtonLocator = { locator: '.WalletConnectHWOptionDialog_connectTrezor', method: 'css', @@ -52,7 +61,7 @@ class AddNewWallet extends WalletCommonBase { locator: '//input[starts-with(@id, "walletName-")]', method: 'xpath', }; - // ::end trezor connect section + // ::end HW connect section // functions async isDisplayed() { @@ -94,13 +103,25 @@ class AddNewWallet extends WalletCommonBase { await this.click(this.connectHwButtonLocator); }); } - // ::start trezor connect section - async selectCardanoNetwork() { - this.logger.info(`AddNewWallet::selectCardanoNetwork is called`); - await this.waitPresentedAndAct(this.cardanoNetworkButtonLocator, async () => { - await this.click(this.cardanoNetworkButtonLocator); + async selectCardanoNetwork(network = CardanoNetworks.MN) { + this.logger.info(`AddNewWallet::selectCardanoNetwork is called. Network to select: ${network}`); + let networkButtonLocator; + switch (network) { + case CardanoNetworks.PP: + networkButtonLocator = this.preprodNetworkButtonLocator; + break; + case CardanoNetworks.PV: + networkButtonLocator = this.previewNetworkButtonLocator; + break; + default: + networkButtonLocator = this.mainnetNetworkButtonLocator; + break; + } + await this.waitPresentedAndAct(networkButtonLocator, async () => { + await this.click(networkButtonLocator); }); } + // ::start HW connect section async selectTrezorHW() { this.logger.info(`AddNewWallet::selectTrezorHW is called`); await this.waitPresentedAndAct(this.trezorHWButtonLocator, async () => { diff --git a/packages/yoroi-extension/app/components/widgets/options/OptionBlock.js b/packages/yoroi-extension/app/components/widgets/options/OptionBlock.js index 75dc3ce1ca..538ef1fc1f 100644 --- a/packages/yoroi-extension/app/components/widgets/options/OptionBlock.js +++ b/packages/yoroi-extension/app/components/widgets/options/OptionBlock.js @@ -56,12 +56,22 @@ export default class OptionBlock extends Component { const learnMoreButtonClasses = classnames([styles.learnMoreButton, this.state.showLearnMore && styles.arrowUp]); + const getNetworkNameForId = () => { + const nameArr = title.split(' '); + return nameArr.length === 1 ? 'Mainnet' : nameArr[1] + }; + return (
  • {/* Submit button block */} -