From fd56e43864cc2b3f82993fa4da8ce81da5f90dac Mon Sep 17 00:00:00 2001 From: MariuSZ <112616777+MariuszSzpyt@users.noreply.github.com> Date: Fri, 22 Mar 2024 10:23:24 +0100 Subject: [PATCH] Feat/mgx 843 non blocking tx UI (#392) --- .../microapps-ui-main.accounts.test.ts | 23 ++- .../microapps-ui-main.liq.pools.test.ts | 28 ++- .../microapps-ui-main.swap.test.ts | 27 +++ .../microapps-ui-main.withdraw.test.ts | 19 +- .../microapps-pages/LiqPoolDetails.ts | 12 ++ utils/frontend/microapps-pages/LiqPools.ts | 6 + .../microapps-pages/NotificationToast.ts | 182 ++++++++++++++++++ utils/frontend/microapps-utils/Handlers.ts | 39 ++-- 8 files changed, 295 insertions(+), 41 deletions(-) create mode 100644 utils/frontend/microapps-pages/NotificationToast.ts diff --git a/test/microapps-ui-xcm/microapps-ui-main.accounts.test.ts b/test/microapps-ui-xcm/microapps-ui-main.accounts.test.ts index f73bb01bb..8655540d4 100644 --- a/test/microapps-ui-xcm/microapps-ui-main.accounts.test.ts +++ b/test/microapps-ui-xcm/microapps-ui-main.accounts.test.ts @@ -41,6 +41,10 @@ import { KSM_FULL_NAME, MGX_ASSET_NAME, } from "../../utils/frontend/microapps-pages/UiConstant"; +import { + NotificationToast, + ToastType, +} from "../../utils/frontend/microapps-pages/NotificationToast"; jest.spyOn(console, "log").mockImplementation(jest.fn()); jest.setTimeout(1500000); @@ -150,7 +154,7 @@ describe.each` await sidebar.clickNavLiqPools(); const poolsList = new LiqPools(driver); - const isPoolsListDisplayed = await poolsList.isDisplayed(); + let isPoolsListDisplayed = await poolsList.isDisplayed(); expect(isPoolsListDisplayed).toBeTruthy(); const isMgxKsmPoolVisible = await poolsList.isPoolItemDisplayed( @@ -198,8 +202,21 @@ describe.each` 2, ); - const poolShareAfter = await poolDetails.getMyPositionAmount(); - expect(poolShareAfter).toBeGreaterThan(poolShareBefore); + await poolDetails.waitForContinueState(false, 5000); + const toast = new NotificationToast(driver); + await toast.waitForToastDisappear( + ToastType.Success, + TransactionType.AddLiquidity, + ); + await poolDetails.clickBackButton(); + isPoolsListDisplayed = await poolsList.isDisplayed(); + expect(isPoolsListDisplayed).toBeTruthy(); + await poolsList.clickPoolItem( + "-" + MGX_ASSET_NAME + "-" + KSM_ASSET_NAME, + ); + + const my_new_pool_share = await poolDetails.getMyPositionAmount(); + expect(my_new_pool_share).toBeGreaterThan(poolShareBefore); }); test("Deposit tokens by account type " + accType, async () => { diff --git a/test/microapps-ui-xcm/microapps-ui-main.liq.pools.test.ts b/test/microapps-ui-xcm/microapps-ui-main.liq.pools.test.ts index 2fe19e3e7..662666b50 100644 --- a/test/microapps-ui-xcm/microapps-ui-main.liq.pools.test.ts +++ b/test/microapps-ui-xcm/microapps-ui-main.liq.pools.test.ts @@ -33,6 +33,10 @@ import { LiqPools } from "../../utils/frontend/microapps-pages/LiqPools"; import { Sidebar } from "../../utils/frontend/microapps-pages/Sidebar"; import { LiqPoolDetils } from "../../utils/frontend/microapps-pages/LiqPoolDetails"; import { TransactionType } from "../../utils/frontend/microapps-pages/NotificationModal"; +import { + NotificationToast, + ToastType, +} from "../../utils/frontend/microapps-pages/NotificationToast"; jest.setTimeout(FIVE_MIN); jest.spyOn(console, "log").mockImplementation(jest.fn()); @@ -234,7 +238,7 @@ describe("Miocroapps UI liq pools tests", () => { await sidebar.clickNavLiqPools(); const poolsList = new LiqPools(driver); - const isPoolsListDisplayed = await poolsList.isDisplayed(); + let isPoolsListDisplayed = await poolsList.isDisplayed(); expect(isPoolsListDisplayed).toBeTruthy(); const isMgxKsmPoolVisible = await poolsList.isPoolItemDisplayed( @@ -279,6 +283,16 @@ describe("Miocroapps UI liq pools tests", () => { TransactionType.AddLiquidity, 2, ); + await poolDetails.waitForContinueState(false, 5000); + const toast = new NotificationToast(driver); + await toast.waitForToastDisappear( + ToastType.Success, + TransactionType.AddLiquidity, + ); + await poolDetails.clickBackButton(); + isPoolsListDisplayed = await poolsList.isDisplayed(); + expect(isPoolsListDisplayed).toBeTruthy(); + await poolsList.clickPoolItem("-" + MGX_ASSET_NAME + "-" + KSM_ASSET_NAME); const my_new_pool_share = await poolDetails.getMyPositionAmount(); expect(my_new_pool_share).toBeGreaterThan(my_pool_share); @@ -290,7 +304,7 @@ describe("Miocroapps UI liq pools tests", () => { await sidebar.clickNavLiqPools(); const poolsList = new LiqPools(driver); - const isPoolsListDisplayed = await poolsList.isDisplayed(); + let isPoolsListDisplayed = await poolsList.isDisplayed(); expect(isPoolsListDisplayed).toBeTruthy(); await poolsList.clickAllPoolsTab(); await poolsList.clickCreateLiquidity(); @@ -305,6 +319,7 @@ describe("Miocroapps UI liq pools tests", () => { const isFeeDisplayed = await poolsList.isFeeDisplayed(); expect(isFeeDisplayed).toBeTruthy(); + await poolsList.waitForContinueState(true, 5000); await poolsList.createLiqPoolsubmit(); await waitForMicroappsActionNotification( driver, @@ -313,12 +328,19 @@ describe("Miocroapps UI liq pools tests", () => { TransactionType.CreatePool, 2, ); + await poolsList.clickCancelButton(); + isPoolsListDisplayed = await poolsList.isDisplayed(); + expect(isPoolsListDisplayed).toBeTruthy(); + await poolsList.clickPoolItem("-" + ZLK_ASSET_NAME + "-" + TUR_ASSET_NAME); const poolDetails = new LiqPoolDetils(driver); const isPoolDetailsVisible = await poolDetails.isDisplayed( - TUR_ASSET_NAME + " / " + ZLK_ASSET_NAME, + ZLK_ASSET_NAME + " / " + TUR_ASSET_NAME, ); expect(isPoolDetailsVisible).toBeTruthy(); + const isMyPositionAmountDisplayed = + await poolDetails.isMyPositionAmountDisplayed(); + expect(isMyPositionAmountDisplayed).toBeTruthy(); const my_pool_share = await poolDetails.getMyPositionAmount(); expect(my_pool_share).toBeGreaterThan(0); }); diff --git a/test/microapps-ui-xcm/microapps-ui-main.swap.test.ts b/test/microapps-ui-xcm/microapps-ui-main.swap.test.ts index 341d8f93f..816bd141a 100644 --- a/test/microapps-ui-xcm/microapps-ui-main.swap.test.ts +++ b/test/microapps-ui-xcm/microapps-ui-main.swap.test.ts @@ -20,6 +20,7 @@ import { connectWallet, setupPage, setupPageWithState, + waitForMicroappsActionNotification, } from "../../utils/frontend/microapps-utils/Handlers"; import { Swap } from "../../utils/frontend/microapps-pages/Swap"; import { ApiContext } from "../../utils/Framework/XcmHelper"; @@ -29,6 +30,7 @@ import { AssetId } from "../../utils/ChainSpecs"; import XcmNetworks from "../../utils/Framework/XcmNetworks"; import { devTestingPairs } from "../../utils/setup"; import StashServiceMockSingleton from "../../utils/stashServiceMockSingleton"; +import { TransactionType } from "../../utils/frontend/microapps-pages/NotificationToast"; jest.setTimeout(FIVE_MIN); jest.spyOn(console, "log").mockImplementation(jest.fn()); @@ -259,6 +261,31 @@ describe("Miocroapps UI swap tests", () => { expect(payTokeAmountAfterSwitch).toEqual(getTokenAmount); }); + it("Swap is possible with enough tokens", async () => { + await setupPageWithState(driver, acc_name); + const swap = new Swap(driver); + const isSwapFrameDisplayed = await swap.isDisplayed(); + expect(isSwapFrameDisplayed).toBeTruthy(); + await swap.pickPayToken(MGX_ASSET_NAME); + await swap.pickGetToken(KSM_FULL_NAME); + await swap.setPayTokenAmount("500"); + const getTokenAmount = await swap.fetchGetAssetAmount(); + expect(parseFloat(getTokenAmount)).toBeGreaterThan(0); + + await swap.waitForSwapButtonEnabled(); + const isSwapEnabled = await swap.isSwapButtonEnabled(); + expect(isSwapEnabled).toBeTruthy(); + + await swap.clickSwapButton(); + await waitForMicroappsActionNotification( + driver, + mangata, + kusama, + TransactionType.Swap, + 2, + ); + }); + afterEach(async () => { const session = await driver.getSession(); await addExtraLogs( diff --git a/test/microapps-ui-xcm/microapps-ui-main.withdraw.test.ts b/test/microapps-ui-xcm/microapps-ui-main.withdraw.test.ts index 131ca5f88..b00f19cef 100644 --- a/test/microapps-ui-xcm/microapps-ui-main.withdraw.test.ts +++ b/test/microapps-ui-xcm/microapps-ui-main.withdraw.test.ts @@ -31,12 +31,8 @@ import { devTestingPairs } from "../../utils/setup"; import { AssetId } from "../../utils/ChainSpecs"; import { BN_THOUSAND } from "@mangata-finance/sdk"; import StashServiceMockSingleton from "../../utils/stashServiceMockSingleton"; -import { - ModalType, - NotificationModal, - TransactionType, -} from "../../utils/frontend/microapps-pages/NotificationModal"; import { WithdrawModal } from "../../utils/frontend/microapps-pages/WithdrawModal"; +import { TransactionType } from "../../utils/frontend/microapps-pages/NotificationToast"; jest.spyOn(console, "log").mockImplementation(jest.fn()); @@ -157,23 +153,12 @@ describe("Microapps UI withdraw modal tests", () => { await withdrawModal.clickContinue(); - const modal = new NotificationModal(driver); - await modal.waitForModalState( - ModalType.Confirm, - TransactionType.Withdraw, - 3000, - ); - const isModalWaitingForSignVisible = await modal.isModalVisible( - ModalType.Confirm, - TransactionType.Withdraw, - ); - expect(isModalWaitingForSignVisible).toBeTruthy(); await waitForMicroappsActionNotification( driver, mangata, kusama, TransactionType.Withdraw, - 4, + 3, ); }); diff --git a/utils/frontend/microapps-pages/LiqPoolDetails.ts b/utils/frontend/microapps-pages/LiqPoolDetails.ts index 891906231..6062c96f0 100644 --- a/utils/frontend/microapps-pages/LiqPoolDetails.ts +++ b/utils/frontend/microapps-pages/LiqPoolDetails.ts @@ -24,6 +24,7 @@ const DIV_POOL_VOLUME = "volume"; const DIV_POOL_REWARDS = "monthly-rewards"; const BUTTON_ADD_LIQ = "add-liquidity"; const BUTTON_SUBMIT_LIQ = "submit-button"; +const BUTTON_BACK = "back-button"; const MY_POOL_POSTION_AMNT = "my-pool-position-amount"; const ADD_LIQ_POOL_WIDGET = "provide-liquidity-widget"; const DIV_FIRST_TOKEN_CONTAINER = "firstToken-container"; @@ -54,6 +55,11 @@ export class LiqPoolDetils { await clickElement(this.driver, itemXpath); } + async clickBackButton() { + const itemXpath = buildDataTestIdXpath(BUTTON_BACK); + await clickElement(this.driver, itemXpath); + } + async clickSubmitLiquidity() { const itemXpath = buildDataTestIdXpath(BUTTON_SUBMIT_LIQ); await clickElement(this.driver, itemXpath); @@ -205,6 +211,12 @@ export class LiqPoolDetils { return displayed; } + async isMyPositionAmountDisplayed() { + const itemXpath = buildDataTestIdXpath(MY_POOL_POSTION_AMNT); + const displayed = await isDisplayed(this.driver, itemXpath); + return displayed; + } + async getMyPositionAmount() { const itemXpath = buildDataTestIdXpath(MY_POOL_POSTION_AMNT); const text = await getText(this.driver, itemXpath); diff --git a/utils/frontend/microapps-pages/LiqPools.ts b/utils/frontend/microapps-pages/LiqPools.ts index cd966d9a4..62461af2d 100644 --- a/utils/frontend/microapps-pages/LiqPools.ts +++ b/utils/frontend/microapps-pages/LiqPools.ts @@ -32,6 +32,7 @@ const POOL_SHARE = "poolShare"; const EST_REWARDS = "est-rewards"; const FEE = "fee"; const BTN_SUBMIT = "submit-button"; +const BTN_CANCEL = "cancel-button"; export class LiqPools { driver: WebDriver; @@ -204,6 +205,11 @@ export class LiqPools { ); } + async clickCancelButton() { + const itemXpath = buildDataTestIdXpath(BTN_CANCEL); + await clickElement(this.driver, itemXpath); + } + async getPoolsList() { const classNameXpath = await buildClassXpath("focus:outline-0 group"); await waitForElementVisible(this.driver, classNameXpath, 5000); diff --git a/utils/frontend/microapps-pages/NotificationToast.ts b/utils/frontend/microapps-pages/NotificationToast.ts new file mode 100644 index 000000000..c824f5619 --- /dev/null +++ b/utils/frontend/microapps-pages/NotificationToast.ts @@ -0,0 +1,182 @@ +import { By, WebDriver } from "selenium-webdriver"; +import { FIVE_MIN } from "../../Constants"; +import { waitForNBlocks } from "../../utils"; +import { + buildDataTestIdXpath, + buildXpathByElementText, + clickElement, + getText, + waitForElement, + waitForElementToDissapear, + waitForElementVisible, +} from "../utils/Helper"; + +//SELECTORS +const TOAST_BASE = "tx-notification"; +const TOAST_CONFIRM_TRADE = "-confirming"; +const TOAST_PROGRESS_TRADE = "-pending"; +//transaction-toast-swap-pending +const TOAST_SUCCESS_TRADE = "-success"; +const TOAST_ERROR = "-error"; +const TOAST_DONE_BTN = "button"; +const TOAST_TEXT = "-text"; + +export enum ToastType { + Confirm, + Progress, + Success, + Error, + Rejected, +} + +export enum TransactionType { + Deposit, + Swap, + Withdraw, + AddLiquidity, + RemoveLiquidity, + ActivateLiquidity, + DeactivateLiquidity, + CreatePool, + Claim, + Stake, +} + +export class NotificationToast { + driver: WebDriver; + + toastStage: Record = { + [ToastType.Confirm]: TOAST_CONFIRM_TRADE, + [ToastType.Progress]: TOAST_PROGRESS_TRADE, + [ToastType.Success]: TOAST_SUCCESS_TRADE, + [ToastType.Error]: TOAST_ERROR, + [ToastType.Rejected]: TOAST_ERROR, + }; + + toastTransaction: Record = { + [TransactionType.Deposit]: "-deposit", + [TransactionType.Withdraw]: "-withdraw", + [TransactionType.AddLiquidity]: "-addLiquidity", + [TransactionType.Swap]: "-swap", + [TransactionType.ActivateLiquidity]: "-activateLP", + [TransactionType.DeactivateLiquidity]: "-deactivateLP", + [TransactionType.RemoveLiquidity]: "-removeLiquidity", + [TransactionType.Claim]: "-claim", + [TransactionType.CreatePool]: "-createPool", + [TransactionType.Stake]: "-stake", + }; + + constructor(driver: WebDriver) { + this.driver = driver; + } + private gettoastXpath(type: ToastType, transaction: TransactionType) { + return buildDataTestIdXpath( + TOAST_BASE + this.toastTransaction[transaction] + this.toastStage[type], + ); + } + private gettoastTextXpath(type: ToastType) { + return buildDataTestIdXpath(this.toastStage[type] + TOAST_TEXT); + } + + public async istoastVisible(type: ToastType, transaction: TransactionType) { + const toastXpath = this.gettoastXpath(type, transaction); + try { + return await ( + await this.driver.findElement(By.xpath(toastXpath)) + ).isDisplayed(); + } catch (e) { + return false; + } + } + + public async gettoastText(type: ToastType, transaction: TransactionType) { + const toastXpath = this.gettoastXpath(type, transaction); + return await getText(this.driver, toastXpath); + } + + public async clickInDone() { + await clickElement( + this.driver, + buildXpathByElementText(TOAST_DONE_BTN, "Confirm"), + ); + } + + public async dismiss() { + await clickElement( + this.driver, + buildXpathByElementText(TOAST_DONE_BTN, "close"), + ); + } + + public async waitForToastDisappear( + toastState: ToastType, + transaction: TransactionType, + ) { + await waitForElementToDissapear( + this.driver, + this.gettoastXpath(toastState, transaction), + ); + } + + public async waitForToast( + toastState: ToastType, + transaction: TransactionType, + ) { + await waitForElement( + this.driver, + this.gettoastXpath(toastState, transaction), + ); + await waitForNBlocks(2); + } + + public async waitForToastState( + toastState: ToastType, + transaction: TransactionType, + timeout = FIVE_MIN, + ) { + await waitForElementVisible( + this.driver, + this.gettoastXpath(toastState, transaction), + timeout, + ); + } + + async gettoastErrorInfo(toastState: ToastType) { + const locator = this.gettoastTextXpath(toastState); + const text = await getText(this.driver, locator); + const [headerText, txInfo] = text.split("\n"); + + return { + //Confirm Trade in Polkadot extension\nSwapping 0.001 MGA for 0.000976 mETH' + header: headerText, + txInfo: txInfo, + fromAmount: "", + ToAmount: "", + fromAsset: "", + toAsset: "", + }; + } + async gettoastInfo(toastState: ToastType) { + const locator = this.gettoastTextXpath(toastState); + const text = await getText(this.driver, locator); + const [headerText, txInfo] = text.split("\n"); + const regexfindValues = /([0-9]?.[0-9]+)\s/g; + const amounts = txInfo.match(regexfindValues)!; + const regexfindAssetNames = /[0-9]?.[0-9]+\s(?[a-zA-Z]+)/gm; + const iterator = txInfo.matchAll(regexfindAssetNames); + const assetNames: string[] = []; + for (const match of iterator) { + assetNames.push(match.groups!.AssetName); + } + + return { + //Confirm Trade in Polkadot extension\nSwapping 0.001 MGA for 0.000976 mETH' + header: headerText, + txInfo: txInfo, + fromAmount: amounts[0].trim(), + ToAmount: amounts[1].trim(), + fromAsset: assetNames[0].trim(), + toAsset: assetNames[1].trim(), + }; + } +} diff --git a/utils/frontend/microapps-utils/Handlers.ts b/utils/frontend/microapps-utils/Handlers.ts index f7c4b04c7..9f1cbf3e8 100644 --- a/utils/frontend/microapps-utils/Handlers.ts +++ b/utils/frontend/microapps-utils/Handlers.ts @@ -1,17 +1,17 @@ import { WebDriver } from "selenium-webdriver"; import { ApiContext } from "../../Framework/XcmHelper"; import { Main } from "../microapps-pages/Main"; -import { - ModalType, - NotificationModal, - TransactionType, -} from "../microapps-pages/NotificationModal"; import { WalletConnectModal } from "../microapps-pages/WalletConnectModal"; import { WalletWrapper } from "../microapps-pages/WalletWrapper"; import { Polkadot } from "../pages/Polkadot"; import { acceptPermissionsWalletExtensionInNewWindow } from "../utils/Helper"; import { BN_TEN } from "@mangata-finance/sdk"; import { BN } from "@polkadot/util"; +import { + NotificationToast, + ToastType, + TransactionType, +} from "../microapps-pages/NotificationToast"; export async function connectWallet( driver: WebDriver, @@ -72,25 +72,28 @@ export async function waitForMicroappsActionNotification( transaction: TransactionType, numOfBLocks = 1, ) { - const modal = new NotificationModal(driver); - await modal.waitForModalState(ModalType.Confirm, transaction, 3000); - const isModalWaitingForSignVisible = await modal.isModalVisible( - ModalType.Confirm, + const toast = new NotificationToast(driver); + await toast.waitForToastState(ToastType.Confirm, transaction, 3000); + const isModalWaitingForSignVisible = await toast.istoastVisible( + ToastType.Confirm, transaction, ); expect(isModalWaitingForSignVisible).toBeTruthy(); await Polkadot.signTransaction(driver); - for (let i = 0; i < numOfBLocks; i++) { + let i = 1; + do { await chainOne.chain.newBlock(); await chainTwo.chain.newBlock(); - } - await modal.waitForModalState(ModalType.Success, transaction); - const isModalSuccessVisible = await modal.isModalVisible( - ModalType.Success, - transaction, - ); - expect(isModalSuccessVisible).toBeTruthy(); - await modal.clickInDone(); + i++; + } while (i < numOfBLocks); + + // pushing last block instructions for concurrent run along with toast wait + // this is required to make sure driver will in time check the toast + const promises = []; + promises.push(chainOne.chain.newBlock()); + promises.push(chainTwo.chain.newBlock()); + promises.push(toast.waitForToastState(ToastType.Success, transaction)); + await Promise.all(promises); } export async function addLiqTokenMicroapps(