diff --git a/tests/e2e/features/transactions/deposit/deposit-with-blockchain.feature b/tests/e2e/features/transactions/deposit/deposit-with-blockchain.feature index e4618d9a..0714593a 100644 --- a/tests/e2e/features/transactions/deposit/deposit-with-blockchain.feature +++ b/tests/e2e/features/transactions/deposit/deposit-with-blockchain.feature @@ -16,3 +16,16 @@ Feature: Deposit When I choose "ETH" as token and insert "0.0000000001" as amount When I "confirm" transaction after clicking "Add funds to zkSync Era Testnet" button Then Message "Transaction submitted" should be visible + + # It takes ~2m40.775s + @id1295 @resetAllowance + Scenario: Make a deposit with approving an allowance + Given I reset allowance + Given I go to the main page + Given I go to "Deposit" transaction section + When I click by "text" with "Your account" value + Then Element with "testId" "fee-amount" should be "visible" + When I choose "DAI" as token and insert "0.0000000001" as amount + Then I "handle" transaction after clicking "Approve allowance" button + When I "confirm" transaction after clicking "Add funds to zkSync Era Testnet" button + Then Message "Transaction submitted" should be visible diff --git a/tests/e2e/src/helpers/helper.ts b/tests/e2e/src/helpers/helper.ts index 66ee8fc9..d7c3246b 100644 --- a/tests/e2e/src/helpers/helper.ts +++ b/tests/e2e/src/helpers/helper.ts @@ -24,7 +24,7 @@ let authorizedTag: boolean; let unauthorizedTag: boolean; let emptyWalletTag: boolean; let incognitoTag: boolean; -let transactionsTag: boolean; +export let transactionsTag: boolean; let noBlockChain: boolean; let wallet_1: string[]; let wallet_2: string[]; @@ -88,6 +88,19 @@ export class Helper { return result; } + async checkSelectorHidden(selector: string, waitTime = 10000): Promise { + result = true; + try { + await this.world.page?.waitForSelector(selector, { + state: "hidden", + timeout: waitTime, + }); + } catch { + result = false; + } + return result; + } + async checkElementClickable(element: any, waitTime = 10000): Promise { result = true; try { diff --git a/tests/e2e/src/pages/main.page.ts b/tests/e2e/src/pages/main.page.ts index c161068b..ab972855 100644 --- a/tests/e2e/src/pages/main.page.ts +++ b/tests/e2e/src/pages/main.page.ts @@ -93,6 +93,14 @@ export class MainPage extends BasePage { return "//button[text()='Confirm']"; } + async commonButtonByItsName(value: string) { + return `//button[contains(., '${value}')]`; + } + + async buttonOfModalCard(buttonText: string) { + return `${this.modalCard}//button[text()='${buttonText}']`; + } + async selectTransaction(transactionType: string) { try { let route: string; @@ -125,14 +133,15 @@ export class MainPage extends BasePage { async makeTransaction(actionType: string, transactionType: string) { metamaskPage = await new MetamaskPage(this.world); - result = await this.getTransactionSelector(transactionType); - - await metamaskPage.operateTransaction(result); + const selector = await this.getTransactionSelector(transactionType); + await metamaskPage.callTransactionInterface(); + await metamaskPage.operateTransaction(selector, "confirm"); + await metamaskPage.approveAllovance(selector); } async getTransactionSelector(transactionType: string) { - result = transactionType; - return result; + const selector = `//*[contains(text(),'${transactionType}')]`; + return selector; } async monitorBalance(walletAddress: string, layer: string) { diff --git a/tests/e2e/src/pages/metamask.page.ts b/tests/e2e/src/pages/metamask.page.ts index 941cc795..aac094f8 100644 --- a/tests/e2e/src/pages/metamask.page.ts +++ b/tests/e2e/src/pages/metamask.page.ts @@ -4,7 +4,7 @@ import { setTimeout } from "timers/promises"; import { BasePage } from "./base.page"; import { MainPage } from "./main.page"; import { Extension } from "../data/data"; -import { depositTag, Helper } from "../helpers/helper"; +import { depositTag, Helper, transactionsTag } from "../helpers/helper"; import { config, wallet } from "../support/config"; import type { ICustomWorld } from "../support/custom-world"; @@ -13,6 +13,8 @@ let page: any; let element: any; let metamaskHomeUrl: string; let metamaskWelcomeUrl: string; +export let address: string; +let selector: string; let testId: any; let logoutTrigger: any = undefined; @@ -49,6 +51,14 @@ export class MetamaskPage extends BasePage { return "//*[@data-testid='advanced-setting-reset-account']//button[1]"; // //button[contains(text(),'Reset')]| } + get nextButton() { + return "//button[contains(text(), 'Next')]"; + } + + get switchNetworkButton() { + return "//button[contains(text(), 'Switch network')]"; + } + get extensionDetailsBtn() { return "id=detailsButton"; } @@ -58,7 +68,7 @@ export class MetamaskPage extends BasePage { } get confirmTransaction() { - return "//*[@data-testid='page-container-footer-next']"; + return `//*[@data-testid='page-container-footer-next'] | //button[contains(text(), 'Confirm')]`; } get declineBtn() { @@ -109,6 +119,10 @@ export class MetamaskPage extends BasePage { return `@value='${value}'`; } + get copyWalletAddress() { + return "//button[@class='selected-account__clickable']"; + } + //metamask home page get headerIcon() { return "(//*[contains(@class,'app-header')]//div[contains(@class,'identicon')])[1]"; @@ -154,6 +168,17 @@ export class MetamaskPage extends BasePage { } } + async extractCurrentWalletAddress() { + const helper = await new Helper(this.world); + const currentURL = page.url(); + await page.goto(metamaskWelcomeUrl); + await page.reload(); + await page.locator("//button[@data-testid='popover-close']").click(); + await page.locator(this.copyWalletAddress).click(); + address = await helper.getClipboardValue(); + await page.goto(currentURL); + } + async authorizeInMetamaskExtension(secretPhrase: Array, password: string) { const helper = await new Helper(this.world); const wallet_password = await helper.decrypt(wallet.password); @@ -174,20 +199,49 @@ export class MetamaskPage extends BasePage { } } logoutTrigger = false; + if (transactionsTag) { + await this.extractCurrentWalletAddress(); + } } - async operateTransaction(triggeredElement: string) { - //change network - try { + async callTransactionInterface() { + const helper = await new Helper(this.world); + const mainPage = await new MainPage(this.world); + selector = await mainPage.commonButtonByItsName("Change wallet network"); + const networkChangeRequest = await helper.checkElementVisible(selector); + if (networkChangeRequest) { await this.switchNetwork(); - } finally { - await setTimeout(config.minimalTimeout.timeout); - await this.click(this.continueBtn); + } + await setTimeout(config.defaultTimeout.timeout); + selector = await mainPage.commonButtonByItsName("Continue"); + const continueBtn = await helper.checkElementVisible(selector); + const modalCard = await helper.checkElementVisible(mainPage.modalCard); + if (continueBtn && !modalCard) { + await this.click(selector); + } + } - const popUpContext = await this.catchPopUpByClick(`//span[contains(text(),'${triggeredElement}')]`); - await setTimeout(config.minimalTimeout.timeout); - await popUpContext?.setViewportSize(config.popUpWindowSize); + async approveAllovance(selector: string) { + const mainPage = await new MainPage(this.world); + selector = `${mainPage.modalCard}${selector}`; + if (selector.includes("Approve allowance")) { + selector = `${mainPage.modalCard}//*[contains(text(), 'Allowance approved')]`; + await this.world.page?.waitForSelector(selector, config.extraTimeout); + await this.click(await mainPage.buttonOfModalCard("Continue")); + } + } + + async operateTransaction(selector: string, argument: string) { + const popUpContext = await this.catchPopUpByClick(selector); + await setTimeout(config.minimalTimeout.timeout); + await popUpContext?.setViewportSize(config.popUpWindowSize); + if (argument == "confirm") { + await popUpContext?.click(this.confirmTransaction); + } else if (argument == "next and confirm") { + await popUpContext?.click(this.nextButton); await popUpContext?.click(this.confirmTransaction); + } else if (argument == "networkSwitch") { + await popUpContext?.click(this.switchNetworkButton); } } diff --git a/tests/e2e/src/pages/revoke.page.ts b/tests/e2e/src/pages/revoke.page.ts new file mode 100644 index 00000000..013f0ff6 --- /dev/null +++ b/tests/e2e/src/pages/revoke.page.ts @@ -0,0 +1,103 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { setTimeout } from "timers/promises"; + +import { BasePage } from "./base.page"; +import { address, MetamaskPage } from "./metamask.page"; +import { Helper } from "../helpers/helper"; +import { config } from "../support/config"; + +import type { ICustomWorld } from "../support/custom-world"; + +let metamaskPage: any; +let result: any; +let selector: string; +let argument: string; + +export class RevokePage extends BasePage { + constructor(world: ICustomWorld) { + super(world); + } + + // selectors for revoke + get networkSelectorsListForRevoke() { + return "//*[@id='react-select-address-chain-select-live-region']"; + } + + get menuWalletButton() { + return "//button/div[contains(text(), '0x')]"; + } + + get metamaskButton() { + return `//button[contains(text(),'MetaMask')]`; + } + + get revokeURL() { + return "https://revoke.cash/"; + } + + async revokeButton(value: string) { + return `//button[contains(text(), '${value}')]`; + } + + async login() { + const basePage = new BasePage(this.world); + metamaskPage = new MetamaskPage(this.world); + const revokeUrl = this.revokeURL; + selector = this.metamaskButton; + argument = "next and confirm"; + await basePage.goTo(revokeUrl); + await basePage.clickByText("Connect Wallet"); + await metamaskPage.operateTransaction(selector, argument); + } + + async logout() { + const basePage = new BasePage(this.world); + await this.clickByMenuWalletButton(); + await basePage.clickByText("Disconnect"); + } + + async checkNetworkForRevoke(network: string) { + const helper = await new Helper(this.world); + const selector = `${this.networkSelectorsListForRevoke}/..//img[@alt='${network}']`; + result = await helper.checkElementVisible(selector); + return result; + } + + async clickByMenuWalletButton() { + selector = this.menuWalletButton; + await this.click(selector); + } + + async revokeAllowance() { + metamaskPage = await new MetamaskPage(this.world); + const helper = await new Helper(this.world); + const networkChainId = "?chainId=5"; // Goerli + const revokeGoerliUrl = "https://revoke.cash/address/" + address + networkChainId; + argument = "networkSwitch"; + const networkForRevokeIsSelected = await this.checkNetworkForRevoke("Goerli"); + if (!networkForRevokeIsSelected) { + await this.goTo(revokeGoerliUrl); + } + await setTimeout(config.defaultTimeout.timeout); + selector = await this.revokeButton("Switch Network"); + const switchNetworkIsVisible = await helper.checkElementVisible(selector); + if (switchNetworkIsVisible) { + await metamaskPage.operateTransaction(selector, argument); + } + + await setTimeout(config.defaultTimeout.timeout); + argument = "confirm"; + selector = await this.revokeButton("Revoke"); + const revokeButtonIsVisible = await helper.checkElementVisible(selector); + if (revokeButtonIsVisible) { + await metamaskPage.operateTransaction(selector, argument); + await this.revokeAllowance(); + } + + selector = await this.revokeButton("Revoking"); + const revokingButtonIsVisible = await helper.checkElementVisible(selector); + if (revokingButtonIsVisible) { + await helper.checkSelectorHidden(selector, config.increasedTimeout.timeout); + } + } +} diff --git a/tests/e2e/src/steps/portal.steps.ts b/tests/e2e/src/steps/portal.steps.ts index 3e2f39ec..098446a2 100644 --- a/tests/e2e/src/steps/portal.steps.ts +++ b/tests/e2e/src/steps/portal.steps.ts @@ -9,6 +9,7 @@ import { ContactsPage } from "../pages/contacts.page"; import { LoginPage } from "../pages/login.page"; import { MainPage } from "../pages/main.page"; import { MetamaskPage } from "../pages/metamask.page"; +import { RevokePage } from "../pages/revoke.page"; import { config } from "../support/config"; import type { ICustomWorld } from "../support/custom-world"; @@ -16,6 +17,7 @@ import type { ICustomWorld } from "../support/custom-world"; let basePage: BasePage; let mainPage: MainPage; let loginPage: LoginPage; +let revokePage: RevokePage; let metamaskPage: MetamaskPage; let contactsPage: ContactsPage; let helper: Helper; @@ -94,7 +96,7 @@ When("Circle timer for fee updating should be visible", config.stepTimeout, asyn When( "I {string} transaction after clicking {string} button", - config.stepTimeout, + config.stepExtraTimeout, async function (this: ICustomWorld, actionType: string, transactionBtn: string) { mainPage = new MainPage(this); await mainPage.makeTransaction(actionType, transactionBtn); @@ -212,6 +214,11 @@ Given("I click on the {string} contact button", async function (this: ICustomWor await contactsPage.pressRemoveBtnModal(removeButtonName); }); +Given("I go to the main page", config.stepTimeout, async function (this: ICustomWorld) { + await this.page?.waitForLoadState("load", config.defaultTimeout); + await this.page?.goto(config.BASE_URL + config.DAPP_NETWORK); +}); + Given("I am on the Main page", async function (this: ICustomWorld) { const basePage = new BasePage(this); element = await basePage.returnElementByType("text", "Assets"); @@ -362,3 +369,10 @@ Then( await expect(element).toBeVisible(); } ); + +Given("I reset allowance", config.stepExtraTimeout, async function (this: ICustomWorld) { + revokePage = new RevokePage(this); + await revokePage.login(); + await revokePage.revokeAllowance(); + await revokePage.logout(); +}); diff --git a/tests/e2e/src/support/config.ts b/tests/e2e/src/support/config.ts index 19c126b7..0589c694 100644 --- a/tests/e2e/src/support/config.ts +++ b/tests/e2e/src/support/config.ts @@ -40,7 +40,9 @@ export const config = { defaultTimeout: { timeout: 6 * 1000 }, minimalTimeout: { timeout: 1 * 1000 }, increasedTimeout: { timeout: 15 * 1000 }, + extraTimeout: { timeout: 30 * 1000 }, stepTimeout: { timeout: 60 * 1000 }, + stepExtraTimeout: { timeout: 180 * 1000 }, feeLimitations: true, feeBoundaryLevel: 0.2, // in ETH networkL1: "goerli",