diff --git a/packages/nodes/src/ethereum.node.service.ts b/packages/nodes/src/ethereum.node.service.ts index 27433a95..006f6d2e 100644 --- a/packages/nodes/src/ethereum.node.service.ts +++ b/packages/nodes/src/ethereum.node.service.ts @@ -1,4 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; +import { Logger } from '@nestjs/common'; import * as ganache from 'ganache'; import { Server } from 'ganache'; import { @@ -26,18 +27,50 @@ export class EthereumNodeService { accounts: Account[]; } | undefined; + private readonly logger = new Logger(EthereumNodeService.name); constructor(@Inject(OPTIONS) private options: EthereumNodeServiceOptions) {} async startNode() { if (this.state !== undefined) return; + + if (this.options.accounts) { + for (const account of this.options.accounts) { + account.balance = utils.hexValue( + utils.parseEther(account.balance.toString()), + ); + } + } + this.logger.debug('Starting a fork node...'); const node = ganache.server({ chainId: this.options.chainId || 0x1, fork: { url: this.options.rpcUrl }, logging: { quiet: true }, miner: { blockTime: 2 }, - wallet: { defaultBalance: this.options.defaultBalance || 1000 }, + wallet: { + /** + * If you would like to use specific account, you can pass it for `account` option. + * + * Example: + * + * ```ts + * new EthereumNodeService( + * chainId: this.widgetConfig.chainId, + * rpcUrl: WIDGET_CONFIG.STAND_CONFIG.rpcUrl, + * defaultBalance: 100, + * accounts: [{ + * account: '', + * balance: 50 + * }] + * + * ) + * ``` + */ + accounts: this.options.accounts, + defaultBalance: this.options.defaultBalance || 1000, + }, }); + await node.listen(this.options.port || 7545); const nodeUrl = `http://127.0.0.1:${this.options.port || 7545}`; const initialAccounts = await node.provider.getInitialAccounts(); diff --git a/packages/nodes/src/node.constants.ts b/packages/nodes/src/node.constants.ts index 4a9bc506..e4987e0a 100644 --- a/packages/nodes/src/node.constants.ts +++ b/packages/nodes/src/node.constants.ts @@ -7,6 +7,12 @@ export type EthereumNodeServiceOptions = { port?: number; chainId?: number; defaultBalance?: number; + accounts?: OptionsAccount[]; +}; + +type OptionsAccount = { + secretKey: string; + balance: number | string; }; export type Account = { diff --git a/packages/wallets/src/metamask/metamask.page.ts b/packages/wallets/src/metamask/metamask.page.ts index 63d8c66d..f8c8b4a4 100644 --- a/packages/wallets/src/metamask/metamask.page.ts +++ b/packages/wallets/src/metamask/metamask.page.ts @@ -251,13 +251,40 @@ export class MetamaskPage implements WalletPage { }); } - async changeWalletAddress(addressName: string) { - await test.step('Change wallet address', async () => { + async changeWalletAccountByAddress(address: string) { + await test.step('Change wallet account by address', async () => { await this.navigate(); await this.header.accountMenuButton.click(); - await this.accountMenu.clickToAddress(addressName); + await this.accountMenu.clickToAddress(address); + }); + } + + async isWalletAddressExist(address: string) { + return await test.step(`Checking to the wallet address ${address} is exist`, async () => { + await this.navigate(); + await this.header.accountMenuButton.click(); + const listOfAddress = await this.accountMenu.getListOfAddress(); + + const addressStart = address.slice(0, 7).toLowerCase(); + const addressEnd = address.slice(-5).toLowerCase(); + + const isExist = listOfAddress.some( + (listAddress) => + listAddress.toLowerCase().startsWith(addressStart) && + listAddress.toLowerCase().endsWith(addressEnd), + ); + await this.page.close(); + return isExist; + }); + } + + async changeWalletAccountByName(accountName: string) { + await test.step('Change wallet account', async () => { + await this.navigate(); + await this.header.accountMenuButton.click(); + await this.accountMenu.clickToAccount(accountName); const accountNumber = - this.header.accountMenuButton.getByText(addressName); + this.header.accountMenuButton.getByText(accountName); await accountNumber.waitFor({ state: 'visible', timeout: 2000 }); await this.page.waitForTimeout(2000); await this.page.close(); diff --git a/packages/wallets/src/metamask/pages/elements/accountMenu.element.ts b/packages/wallets/src/metamask/pages/elements/accountMenu.element.ts index f410efb1..9dba5201 100644 --- a/packages/wallets/src/metamask/pages/elements/accountMenu.element.ts +++ b/packages/wallets/src/metamask/pages/elements/accountMenu.element.ts @@ -7,6 +7,7 @@ export class AccountMenu { importAccountButton: Locator; privateKeyInput: Locator; importAccountConfirmButton: Locator; + accountListAddress: Locator; constructor(page: Page) { this.page = page; @@ -21,11 +22,33 @@ export class AccountMenu { this.importAccountConfirmButton = this.page.getByTestId( 'import-account-confirm-button', ); + this.accountListAddress = this.page.getByTestId('account-list-address'); } - async clickToAddress(addressName: string) { - await test.step(`Click to "${addressName}" account`, async () => { - await this.accountListModal.getByText(addressName).click(); + async clickToAccount(accountName: string) { + await test.step(`Click to "${accountName}" account`, async () => { + await this.accountListModal.getByText(accountName).click(); + }); + } + + async clickToAddress(address: string) { + await test.step(`Click to account by "${address}"`, async () => { + const addressStart = address.slice(0, 7); + const addressEnd = address.slice(-5); + + await this.accountListModal + .getByText(`${addressStart}...${addressEnd}`) + .click(); + }); + } + + async getListOfAddress() { + return test.step('Get all exists accounts', async () => { + const listOfAddressText = []; + for (const address of await this.accountListAddress.all()) { + listOfAddressText.push(await address.textContent()); + } + return listOfAddressText; }); } diff --git a/packages/wallets/src/metamask/pages/walletOperations.page.ts b/packages/wallets/src/metamask/pages/walletOperations.page.ts index e37641bb..68fd9613 100644 --- a/packages/wallets/src/metamask/pages/walletOperations.page.ts +++ b/packages/wallets/src/metamask/pages/walletOperations.page.ts @@ -48,21 +48,23 @@ export class WalletOperationPage { } async cancelAllTxInQueue() { - //Is there is any tx in queue. - try { - await this.cancelButton.waitFor({ - state: 'visible', - timeout: 1000, - }); - } catch (er) { - return; - } + test.step('Cancel all tx in queue', async () => { + //Is there is any tx in queue. + try { + await this.cancelButton.waitFor({ + state: 'visible', + timeout: 1000, + }); + } catch (er) { + return; + } - if (await this.cancelAllTxsButton.isVisible()) { - await this.cancelAllTxsButton.click(); - } else { - await this.cancelButton.click(); - } + if (await this.cancelAllTxsButton.isVisible()) { + await this.cancelAllTxsButton.click(); + } else { + await this.cancelButton.click(); + } + }); } async cancelTransaction() { diff --git a/packages/wallets/src/wallet.page.ts b/packages/wallets/src/wallet.page.ts index 0b74165f..e8ca5a59 100644 --- a/packages/wallets/src/wallet.page.ts +++ b/packages/wallets/src/wallet.page.ts @@ -41,5 +41,7 @@ export interface WalletPage { changeNetwork?(networkName: string): Promise; - changeWalletAddress?(addressName: string): Promise; + changeWalletAccountByName?(accountName: string): Promise; + changeWalletAccountByAddress?(address: string): Promise; + isWalletAddressExist?(address: string): Promise; }