diff --git a/e2e/src/features/change-node.feature b/e2e/src/features/change-node.feature index b0d0f449a..2dfd6782a 100644 --- a/e2e/src/features/change-node.feature +++ b/e2e/src/features/change-node.feature @@ -1,5 +1,5 @@ Feature: Change node - @dev + Scenario: As a user, i'd like to change node Given I have imported an existing account And I am on the Header page diff --git a/e2e/src/features/create-new-wallet.feature b/e2e/src/features/create-new-wallet.feature index e64696666..5b5eeb13a 100644 --- a/e2e/src/features/create-new-wallet.feature +++ b/e2e/src/features/create-new-wallet.feature @@ -21,5 +21,11 @@ Feature: Create a new wallet And I press Accept Terms Checkbox on the Register Form page And I press Create Button on the Register Form page + And I am on the OnRumpModal page + And I press Close Button on the On-ramp Modal page + + And I am on the NewsletterModal page + And I press Close Button on the Newsletter Modal page + Then I am on the Home page diff --git a/e2e/src/features/delegate.feature b/e2e/src/features/delegate.feature index 7685c702d..7a5c5a495 100644 --- a/e2e/src/features/delegate.feature +++ b/e2e/src/features/delegate.feature @@ -1,5 +1,5 @@ Feature: Delegate - @dev +@dev Scenario: As a user, i'd like to re-delegate TEZ to a baker Given I have imported an existing account And I press Asset Item Apy Button on the Assets page diff --git a/e2e/src/features/import-account-by-private-key.feature b/e2e/src/features/import-account-by-private-key.feature index e5dff3b2f..6e196e6d7 100644 --- a/e2e/src/features/import-account-by-private-key.feature +++ b/e2e/src/features/import-account-by-private-key.feature @@ -1,4 +1,5 @@ Feature: Import Account by Private Key + Scenario: As a user, I'd like to import account by private key Given I have imported an existing account diff --git a/e2e/src/features/import-existing-wallet.feature b/e2e/src/features/import-existing-wallet.feature index 419a82e89..32c9d9715 100644 --- a/e2e/src/features/import-existing-wallet.feature +++ b/e2e/src/features/import-existing-wallet.feature @@ -15,5 +15,8 @@ Feature: Import existing wallet And I press Accept Terms Checkbox on the Register Form page And I press Import Button on the Register Form page + And I am on the NewsletterModal page + And I press Close Button on the Newsletter Modal page + Then I am on the Home page diff --git a/e2e/src/features/send.feature b/e2e/src/features/send.feature index d97278005..ed9444417 100644 --- a/e2e/src/features/send.feature +++ b/e2e/src/features/send.feature @@ -1,5 +1,5 @@ Feature: Send - @dev +@dev Scenario: As a user, I'd like to send my funds to another account Given I have imported an existing account And I press Send Button on the Home page @@ -7,7 +7,7 @@ Feature: Send And I am on the Send page # Send TEZ And I enter watchOnlyPublicKey into Recipient Input on the Send Form page - And I enter low_amount into Amount Input on the Send Form page + And I enter amount_0_0001 into Amount Input on the Send Form page And I press Send Button on the Send Form page And I am on the InternalConfirmation page @@ -20,9 +20,9 @@ Feature: Send # Send uUSD And I press Asset Drop-down on the Send Form page And I enter uUSD into Asset Drop-down Search Input on the Send Form page - And I select UUSD token in the token drop-down list on the Send page + And I select uUSD token in the token drop-down list on the Send page And I enter watchOnlyPublicKey into Recipient Input on the Send Form page - And I enter low_amount into Amount Input on the Send Form page + And I enter amount_0_0001 into Amount Input on the Send Form page And I press Send Button on the Send Form page And I am on the InternalConfirmation page @@ -36,9 +36,9 @@ Feature: Send And I press Asset Drop-down on the Send Form page And I clear Asset Drop-down Search Input value on the Send Form page And I enter kUSD into Asset Drop-down Search Input on the Send Form page - And I select KUSD token in the token drop-down list on the Send page + And I select kUSD token in the token drop-down list on the Send page And I enter watchOnlyPublicKey into Recipient Input on the Send Form page - And I enter low_amount into Amount Input on the Send Form page + And I enter amount_0_0001 into Amount Input on the Send Form page And I press Send Button on the Send Form page And I am on the InternalConfirmation page @@ -54,7 +54,7 @@ Feature: Send And I enter OBJKTCOM into Asset Drop-down Search Input on the Send Form page And I select OBJKTCOM token in the token drop-down list on the Send page And I enter watchOnlyPublicKey into Recipient Input on the Send Form page - And I enter high_amount into Amount Input on the Send Form page + And I enter amount_1 into Amount Input on the Send Form page And I press Send Button on the Send Form page And I am on the InternalConfirmation page diff --git a/e2e/src/features/swap.feature b/e2e/src/features/swap.feature new file mode 100644 index 000000000..4830ff1dc --- /dev/null +++ b/e2e/src/features/swap.feature @@ -0,0 +1,147 @@ +Feature: Swap + @dev + Scenario: As a user, i'd like to swap assets + Given I have imported an existing account + And I press Swap Button on the Home page + + And I am on the Swap page + # 1.Swap TEZ -> kUSD (TEZ -> FA2) + And I enter amount_0_005 into Asset Input on the Swap Form (From) Input page + And I press Asset Drop-down Button on the Swap Form (To) Input page + And I enter uUSD into Search Input on the Swap Form (To) Input page + And I select uUSD token in the token drop-down list on the Swap page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + And I am on the Swap page + # 2.Swap TEZ -> kUSD (TEZ -> FA1.2) + And I press Asset Drop-down Button on the Swap Form (To) Input page + And I clear Search Input value on the Swap Form (To) Input page + And I enter kUSD into Search Input on the Swap Form (To) Input page + And I select kUSD token in the token drop-down list on the Swap page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + And I am on the Swap page + # 3.Swap kUSD -> TEZ (FA1.2 -> TEZ) + And I press Swap Places Button on the Swap Form page + And I enter amount_0_005 into Asset Input on the Swap Form (From) Input page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 4.Swap uUSD -> TEZ (FA2 -> TEZ) + And I press Asset Drop-down Button on the Swap Form (From) Input page + And I enter uUSD into Search Input on the Swap Form (From) Input page + And I select uUSD token in the token drop-down list on the Swap page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 5.Swap uUSD -> WTZ (FA2 -> FA1.2) + And I press Asset Drop-down Button on the Swap Form (To) Input page + And I enter WTZ into Search Input on the Swap Form (To) Input page + And I select WTZ token in the token drop-down list on the Swap page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 6.Swap WTZ -> uUSD (FA1.2 -> FA2) + And I press Swap Places Button on the Swap Form page + And I enter amount_0_005 into Asset Input on the Swap Form (From) Input page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 7.Swap WTZ -> kUSD (FA1.2 -> FA1.2) + And I press Asset Drop-down Button on the Swap Form (To) Input page + And I enter kUSD into Search Input on the Swap Form (To) Input page + And I select kUSD token in the token drop-down list on the Swap page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 8.Swap kUSD -> WTZ (FA1.2 -> FA1.2) + And I press Swap Places Button on the Swap Form page + And I enter amount_0_005 into Asset Input on the Swap Form (From) Input page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 9.Swap wUSDT -> uUSD (FA2 -> FA2) + And I press Asset Drop-down Button on the Swap Form (From) Input page + And I enter wUSDT into Search Input on the Swap Form (From) Input page + And I select wUSDT token in the token drop-down list on the Swap page + And I clear Asset Input value on the Swap Form (From) Input page + And I enter amount_0_03 into Asset Input on the Swap Form (From) Input page + And I press Asset Drop-down Button on the Swap Form (To) Input page + And I enter uUSD into Search Input on the Swap Form (To) Input page + And I select uUSD token in the token drop-down list on the Swap page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + + And I am on the Swap page + # 10.Swap wUSDT -> uUSD (FA2 -> FA2) + And I press Swap Places Button on the Swap Form page + And I enter amount_0_03 into Asset Input on the Swap Form (From) Input page + And I click on animated Swap button on the Swap page + + And I am on the InternalConfirmation page + And I press Confirm Button on the Internal Confirmation page + + And I am on the OperationStatusAlert page + And I'm waiting for 'success ✓' operation status + + Then I am on the Swap page diff --git a/e2e/src/features/unlock-screen.feature b/e2e/src/features/unlock-screen.feature index fc72d9aec..e83a82c98 100644 --- a/e2e/src/features/unlock-screen.feature +++ b/e2e/src/features/unlock-screen.feature @@ -1,4 +1,5 @@ Feature: Unlock Screen + Scenario: As a user, I'd like to unlock my wallet Given I have imported an existing account And I press Account Icon on the Header page diff --git a/e2e/src/hooks/before.hook.ts b/e2e/src/hooks/before.hook.ts index 304b5f58a..f0fa67712 100644 --- a/e2e/src/hooks/before.hook.ts +++ b/e2e/src/hooks/before.hook.ts @@ -14,6 +14,10 @@ Before({ timeout: MEDIUM_TIMEOUT }, async () => { const page = await retry(async () => { const page = await BrowserContext.browser.newPage(); + await page.setViewport({ + height: 800, + width: 800 + }); try { const response = await page.goto(url); if (response == null || !response.ok) throw new Error('Failed to open page'); diff --git a/e2e/src/page-objects/index.ts b/e2e/src/page-objects/index.ts index 653dcc947..7ca24711f 100644 --- a/e2e/src/page-objects/index.ts +++ b/e2e/src/page-objects/index.ts @@ -1,4 +1,8 @@ +import { SwapPage } from 'e2e/src/page-objects/pages/swap.page'; + import { NetworksDropDown } from 'e2e/src/page-objects/pages/drop-down-lists/networks.drop-down'; +import { NewsletterModalPage } from 'e2e/src/page-objects/pages/newsletter-modal.page'; +import { OnRumModalPage } from 'e2e/src/page-objects/pages/on-rum-modal.page'; import { OperationStatusAlert } from './pages/alerts/operation-status.alert'; import { CreateOrRestoreAnAccountPage } from './pages/create-or-restore-an-account.page'; @@ -45,5 +49,8 @@ export const Pages = { InternalConfirmation: new InternalConfirmationPage(), OperationStatusAlert: new OperationStatusAlert(), Send: new SendPage(), - NetworksDropDown: new NetworksDropDown() + NetworksDropDown: new NetworksDropDown(), + OnRumpModal: new OnRumModalPage(), + NewsletterModal: new NewsletterModalPage(), + Swap: new SwapPage() }; diff --git a/e2e/src/page-objects/pages/internal-confirmation.page.ts b/e2e/src/page-objects/pages/internal-confirmation.page.ts index bb3416a2b..36af6b4b5 100644 --- a/e2e/src/page-objects/pages/internal-confirmation.page.ts +++ b/e2e/src/page-objects/pages/internal-confirmation.page.ts @@ -1,6 +1,8 @@ +import { InternalConfirmationSelectors } from 'src/app/templates/InternalConfirmation.selectors'; +import { OperationsBannerSelectors } from 'src/app/templates/OperationsBanner/OperationsBanner.selectors'; + import { LONG_TIMEOUT } from 'e2e/src/utils/timing.utils'; -import { InternalConfirmationSelectors } from '../../../../src/app/templates/InternalConfirmation.selectors'; import { Page } from '../../classes/page.class'; import { createPageElement } from '../../utils/search.utils'; @@ -11,12 +13,23 @@ export class InternalConfirmationPage extends Page { rawTab = createPageElement(InternalConfirmationSelectors.rawTab); previewTab = createPageElement(InternalConfirmationSelectors.previewTab); retryButton = createPageElement(InternalConfirmationSelectors.retryButton); + errorText = createPageElement(OperationsBannerSelectors.errorText); + errorDropDownButton = createPageElement(OperationsBannerSelectors.errorDropDownButton); + errorValue = createPageElement(OperationsBannerSelectors.errorValue); async isVisible() { - await this.confirmButton.waitForDisplayed(LONG_TIMEOUT); - await this.declineButton.waitForDisplayed(); - await this.bytesTab.waitForDisplayed(); - await this.rawTab.waitForDisplayed(); - await this.previewTab.waitForDisplayed(); + try { + await this.errorText.waitForDisplayed(5000); + await this.errorDropDownButton.click(); + + const errorLog = await this.errorValue.getText(); + console.log('Confirmation page includes error logs:', errorLog); + } catch (error) { + await this.confirmButton.waitForDisplayed(LONG_TIMEOUT); + await this.declineButton.waitForDisplayed(); + await this.bytesTab.waitForDisplayed(); + await this.rawTab.waitForDisplayed(); + await this.previewTab.waitForDisplayed(); + } } } diff --git a/e2e/src/page-objects/pages/newsletter-modal.page.ts b/e2e/src/page-objects/pages/newsletter-modal.page.ts new file mode 100644 index 000000000..73d78f4d2 --- /dev/null +++ b/e2e/src/page-objects/pages/newsletter-modal.page.ts @@ -0,0 +1,16 @@ +import { NewsletterOverlaySelectors } from 'src/app/layouts/PageLayout/NewsletterOverlay/NewsletterOverlay.selectors'; + +import { Page } from '../../classes/page.class'; +import { createPageElement } from '../../utils/search.utils'; + +export class NewsletterModalPage extends Page { + closeButton = createPageElement(NewsletterOverlaySelectors.closeButton); + emailInput = createPageElement(NewsletterOverlaySelectors.emailInput); + subscribeButton = createPageElement(NewsletterOverlaySelectors.subscribeButton); + + async isVisible() { + await this.closeButton.waitForDisplayed(); + await this.emailInput.waitForDisplayed(); + await this.subscribeButton.waitForDisplayed(); + } +} diff --git a/e2e/src/page-objects/pages/on-rum-modal.page.ts b/e2e/src/page-objects/pages/on-rum-modal.page.ts new file mode 100644 index 000000000..afdff1600 --- /dev/null +++ b/e2e/src/page-objects/pages/on-rum-modal.page.ts @@ -0,0 +1,20 @@ +import { OnRampOverlaySelectors } from 'src/app/layouts/PageLayout/OnRampOverlay/OnRampOverlay.selectors'; + +import { Page } from '../../classes/page.class'; +import { createPageElement } from '../../utils/search.utils'; + +export class OnRumModalPage extends Page { + closeButton = createPageElement(OnRampOverlaySelectors.closeButton); + fiftyDollarButton = createPageElement(OnRampOverlaySelectors.fiftyDollarButton); + oneHundredDollarButton = createPageElement(OnRampOverlaySelectors.oneHundredDollarButton); + twoHundredDollarButton = createPageElement(OnRampOverlaySelectors.twoHundredDollarButton); + customAmountButton = createPageElement(OnRampOverlaySelectors.customAmountButton); + + async isVisible() { + await this.closeButton.waitForDisplayed(); + await this.fiftyDollarButton.waitForDisplayed(); + await this.oneHundredDollarButton.waitForDisplayed(); + await this.twoHundredDollarButton.waitForDisplayed(); + await this.customAmountButton.waitForDisplayed(); + } +} diff --git a/e2e/src/page-objects/pages/swap.page.ts b/e2e/src/page-objects/pages/swap.page.ts new file mode 100644 index 000000000..aba9165f2 --- /dev/null +++ b/e2e/src/page-objects/pages/swap.page.ts @@ -0,0 +1,47 @@ +import retry from 'async-retry'; +import { + SwapFormFromInputSelectors, + SwapFormSelectors, + SwapFormToInputSelectors +} from 'src/app/templates/SwapForm/SwapForm.selectors'; +import { AssetsMenuSelectors } from 'src/app/templates/SwapForm/SwapFormInput/AssetsMenu/selectors'; + +import { sleep } from 'e2e/src/utils/timing.utils'; + +import { Page } from '../../classes/page.class'; +import { createPageElement, findElement } from '../../utils/search.utils'; + +export class SwapPage extends Page { + swapPlacesButton = createPageElement(SwapFormSelectors.swapPlacesButton); + swapButton = createPageElement(SwapFormSelectors.swapButton); + + assetInputFrom = createPageElement(SwapFormFromInputSelectors.assetInput); + assetItemFrom = createPageElement(SwapFormFromInputSelectors.assetDropDownButton); + searchInputFrom = createPageElement(SwapFormFromInputSelectors.searchInput); + + assetInputTo = createPageElement(SwapFormToInputSelectors.assetInput); + assetItemTo = createPageElement(SwapFormToInputSelectors.assetDropDownButton); + searchInputTo = createPageElement(SwapFormToInputSelectors.searchInput); + + async isVisible() { + await this.swapPlacesButton.waitForDisplayed(); + await this.swapButton.waitForDisplayed(); + await this.assetInputFrom.waitForDisplayed(); + await this.assetItemFrom.waitForDisplayed(); + await this.assetInputTo.waitForDisplayed(); + await this.assetItemTo.waitForDisplayed(); + } + + async selectAsset(slug: string) { + // Retrying here, because sometimes element reference is lost between + // finding it and clicking. Common for drop-down menus. + await retry( + async () => { + const tokenItemElem = await findElement(AssetsMenuSelectors.assetsMenuAssetItem, { slug }); + await sleep(1000); + await tokenItemElem.click(); + }, + { retries: 10 } + ); + } +} diff --git a/e2e/src/step-definitions/common.steps.ts b/e2e/src/step-definitions/common.steps.ts index 135a3f72e..131f03599 100644 --- a/e2e/src/step-definitions/common.steps.ts +++ b/e2e/src/step-definitions/common.steps.ts @@ -18,6 +18,7 @@ Given( /I clear (.*) value on the (.*) page/, { timeout: MEDIUM_TIMEOUT }, async (elementName: string, pageName: string) => { + await createPageElement(`${pageName}/${elementName}`).click(); await createPageElement(`${pageName}/${elementName}`).clearInput(); } ); @@ -47,5 +48,8 @@ Given(/I have imported an existing account/, { timeout: LONG_TIMEOUT }, async () await Pages.SetWallet.acceptTerms.click(); await Pages.SetWallet.importButton.click(); + await Pages.NewsletterModal.isVisible(); + await Pages.NewsletterModal.closeButton.click(); + await Pages.Home.isVisible(); }); diff --git a/e2e/src/step-definitions/swap.steps.ts b/e2e/src/step-definitions/swap.steps.ts new file mode 100644 index 000000000..7f3bae077 --- /dev/null +++ b/e2e/src/step-definitions/swap.steps.ts @@ -0,0 +1,34 @@ +import { Given } from '@cucumber/cucumber'; +import retry from 'async-retry'; +import { InternalConfirmationSelectors } from 'src/app/templates/InternalConfirmation.selectors'; +import { SwapFormSelectors } from 'src/app/templates/SwapForm/SwapForm.selectors'; + +import { Pages } from 'e2e/src/page-objects'; +import { iSelectTokenSlugs } from 'e2e/src/utils/input-data.utils'; +import { createPageElement } from 'e2e/src/utils/search.utils'; +import { MEDIUM_TIMEOUT, VERY_LONG_TIMEOUT } from 'e2e/src/utils/timing.utils'; + +import { BrowserContext } from '../classes/browser-context.class'; + +Given( + /I select (.*) token in the token drop-down list on the Swap page/, + { timeout: VERY_LONG_TIMEOUT }, + async (key: keyof typeof iSelectTokenSlugs) => { + const assetsSlug = iSelectTokenSlugs[key]; + + await Pages.Swap.selectAsset(assetsSlug); + } +); + +Given(/I click on animated Swap button on the Swap page/, { timeout: MEDIUM_TIMEOUT }, async () => { + const elem = createPageElement(SwapFormSelectors.swapButton, undefined, { loading: '' }); + const confirmButton = createPageElement(InternalConfirmationSelectors.confirmButton); + + await retry( + async () => { + await elem.click(); + await confirmButton.waitForDisplayed(5000); + }, + { retries: 3 } + ); +}); diff --git a/e2e/src/utils/input-data.utils.ts b/e2e/src/utils/input-data.utils.ts index 7cbe3662a..1bf768a43 100644 --- a/e2e/src/utils/input-data.utils.ts +++ b/e2e/src/utils/input-data.utils.ts @@ -13,18 +13,23 @@ export const iEnterValues = { bakerAddress: '', // For transactions - low_amount: '0.0001', - medium_amount: '0.001', - high_amount: '1', - kUSD: 'Kolibri', - uUSD: 'youves uUSD', + amount_0_0001: '0.0001', + amount_0_005: '0.005', + amount_0_03: '0.03', + amount_1: '1', + kUSD: 'kUSD', + uUSD: 'uUSD', + WTZ: 'WTZ', + wUSDT: 'wUSDT', OBJKTCOM: 'Temple NFT' }; export type IEnterValuesKey = keyof typeof iEnterValues; export const iSelectTokenSlugs = { - KUSD: 'KT1K9gCRgaLRFKTErYt1wVxA3Frb9FjasjTV_0', - UUSD: 'KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW_0', + kUSD: 'KT1K9gCRgaLRFKTErYt1wVxA3Frb9FjasjTV_0', + uUSD: 'KT1XRPEPXbZK25r3Htzp2o1x7xdMMmfocKNW_0', + WTZ: 'KT1PnUZCp3u2KzWr93pn4DD7HAJnm3rWVrgn_0', + wUSDT: 'KT18fp5rcTW7mbWDmzFwjLDUhs5MeJmagDSZ_18', OBJKTCOM: 'KT1DGbb333QNo3e2cpN3YGL5aRwWzkADcPA3_2' // 'Temple NFT' }; diff --git a/e2e/src/utils/search.utils.ts b/e2e/src/utils/search.utils.ts index ce5408283..9916f6802 100644 --- a/e2e/src/utils/search.utils.ts +++ b/e2e/src/utils/search.utils.ts @@ -9,23 +9,20 @@ const buildTestIDSelector = (testID: string) => `[data-testid="${testID}"]`; type OtherSelectors = Record; -const buildSelector = (testID: string, otherSelectors?: OtherSelectors) => { - const pairs = Object.entries({ ...otherSelectors, testid: testID }).map( - ([key, val]) => `data-${key}="${val}"` as const - ); - return `[${pairs.join('][')}]`; -}; - export const findElement = async (testID: string, otherSelectors?: OtherSelectors, timeout = MEDIUM_TIMEOUT) => { const selector = buildSelector(testID, otherSelectors); - const element = await BrowserContext.page.waitForSelector(selector, { visible: true, timeout }); + return await findElementBySelectors(selector, timeout); +}; + +export const findElementBySelectors = async (selectors: string, timeout = MEDIUM_TIMEOUT) => { + const element = await BrowserContext.page.waitForSelector(selectors, { visible: true, timeout }); if (isDefined(element)) { return element; } - throw new Error(`"${testID}" not found`); + throw new Error(`${selectors} not found`); }; export const findElements = async (testID: string) => { @@ -41,10 +38,13 @@ export const findElements = async (testID: string) => { }; class PageElement { - constructor(public testID: string, public otherSelectors?: OtherSelectors) {} + constructor(public testID: string, public otherSelectors?: OtherSelectors, public notSelectors?: OtherSelectors) {} findElement(timeout?: number) { - return findElement(this.testID, this.otherSelectors, timeout); + let selectors = buildSelector(this.testID, this.otherSelectors); + if (this.notSelectors) selectors += buildNotSelector(this.notSelectors); + + return findElementBySelectors(selectors, timeout); } waitForDisplayed(timeout?: number) { return this.findElement(timeout); @@ -58,8 +58,10 @@ class PageElement { await element.type(text); } async clearInput() { + await BrowserContext.page.keyboard.press('End'); await BrowserContext.page.keyboard.down('Shift'); await BrowserContext.page.keyboard.press('Home'); + await BrowserContext.page.keyboard.up('Shift'); await BrowserContext.page.keyboard.press('Backspace'); } async getText() { @@ -87,8 +89,8 @@ class PageElement { } } -export const createPageElement = (testID: string, otherSelectors?: OtherSelectors) => - new PageElement(testID, otherSelectors); +export const createPageElement = (testID: string, otherSelectors?: OtherSelectors, notSelectors?: OtherSelectors) => + new PageElement(testID, otherSelectors, notSelectors); export const getElementText = (element: ElementHandle) => element.evaluate(innerElement => { @@ -102,3 +104,19 @@ export const getElementText = (element: ElementHandle) => throw new Error('Element text not found'); }); + +const buildSelectorPairs = (selectors: OtherSelectors) => { + return Object.entries(selectors).map(([key, val]) => + val ? (`data-${key}="${val}"` as const) : (`data-${key}` as const) + ); +}; + +const buildSelector = (testID: string, otherSelectors?: OtherSelectors) => { + const pairs = buildSelectorPairs({ ...otherSelectors, testid: testID }); + return `[${pairs.join('][')}]`; +}; + +const buildNotSelector = (notSelectors: OtherSelectors) => { + const pairs = buildSelectorPairs(notSelectors); + return `:not([${pairs.join(']):not([')}])`; +}; diff --git a/e2e/src/utils/timing.utils.ts b/e2e/src/utils/timing.utils.ts index 6a4210989..f776e05ce 100644 --- a/e2e/src/utils/timing.utils.ts +++ b/e2e/src/utils/timing.utils.ts @@ -1,3 +1,5 @@ +export const VERY_LONG_TIMEOUT = 120_000; + export const LONG_TIMEOUT = 60_000; export const MEDIUM_TIMEOUT = 30_000; @@ -8,3 +10,7 @@ export const RETRY_OPTIONS = { minTimeout: 300, maxRetryTime: 15_000 }; + +export function sleep(ms: number) { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/src/app/atoms/FormSubmitButton.tsx b/src/app/atoms/FormSubmitButton.tsx index 7d6ab63f7..cac5da7cb 100644 --- a/src/app/atoms/FormSubmitButton.tsx +++ b/src/app/atoms/FormSubmitButton.tsx @@ -5,6 +5,7 @@ import classNames from 'clsx'; import Spinner from 'app/atoms/Spinner/Spinner'; import { T } from 'lib/i18n'; +import { setAnotherSelector } from '../../lib/analytics'; import { ButtonProps, Button } from './Button'; interface FormSubmitButtonProps extends ButtonProps { @@ -53,7 +54,13 @@ export const FormSubmitButton: FC = ({ if (searchingRoute) { return ( - diff --git a/src/app/templates/ExpensesView/ExpensesView.tsx b/src/app/templates/ExpensesView/ExpensesView.tsx index b8b2595d1..9157df3fb 100644 --- a/src/app/templates/ExpensesView/ExpensesView.tsx +++ b/src/app/templates/ExpensesView/ExpensesView.tsx @@ -18,7 +18,9 @@ import { useAssetMetadata, getAssetSymbol } from 'lib/metadata'; import { RawOperationAssetExpense, RawOperationExpenses, useGasToken } from 'lib/temple/front'; import { mutezToTz, tzToMutez } from 'lib/temple/helpers'; -import OperationsBanner from '../OperationsBanner'; +import { setTestID } from '../../../lib/analytics'; +import OperationsBanner from '../OperationsBanner/OperationsBanner'; +import { OperationsBannerSelectors } from '../OperationsBanner/OperationsBanner.selectors'; import styles from './ExpensesView.module.css'; type OperationAssetExpense = Omit & { @@ -247,7 +249,7 @@ const ExpensesView: FC = ({ {error && (
- + diff --git a/src/app/templates/InternalConfirmation.tsx b/src/app/templates/InternalConfirmation.tsx index 073a4adcd..6a2aa3a74 100644 --- a/src/app/templates/InternalConfirmation.tsx +++ b/src/app/templates/InternalConfirmation.tsx @@ -17,7 +17,7 @@ import { setOnRampPossibilityAction } from 'app/store/settings/actions'; import AccountBanner from 'app/templates/AccountBanner'; import ExpensesView, { ModifyFeeAndLimit } from 'app/templates/ExpensesView/ExpensesView'; import NetworkBanner from 'app/templates/NetworkBanner'; -import OperationsBanner from 'app/templates/OperationsBanner'; +import OperationsBanner from 'app/templates/OperationsBanner/OperationsBanner'; import RawPayloadView from 'app/templates/RawPayloadView'; import ViewsSwitcher from 'app/templates/ViewsSwitcher/ViewsSwitcher'; import { ViewsSwitcherItemProps } from 'app/templates/ViewsSwitcher/ViewsSwitcherItem'; diff --git a/src/app/templates/OperationView.tsx b/src/app/templates/OperationView.tsx index e4ea978a2..b0c90eed4 100644 --- a/src/app/templates/OperationView.tsx +++ b/src/app/templates/OperationView.tsx @@ -6,7 +6,7 @@ import { ReactComponent as CodeAltIcon } from 'app/icons/code-alt.svg'; import { ReactComponent as EyeIcon } from 'app/icons/eye.svg'; import { ReactComponent as HashIcon } from 'app/icons/hash.svg'; import ExpensesView, { ModifyFeeAndLimit } from 'app/templates/ExpensesView/ExpensesView'; -import OperationsBanner from 'app/templates/OperationsBanner'; +import OperationsBanner from 'app/templates/OperationsBanner/OperationsBanner'; import RawPayloadView from 'app/templates/RawPayloadView'; import ViewsSwitcher from 'app/templates/ViewsSwitcher/ViewsSwitcher'; import { TEZ_TOKEN_SLUG, toTokenSlug } from 'lib/assets'; diff --git a/src/app/templates/OperationsBanner/OperationsBanner.selectors.ts b/src/app/templates/OperationsBanner/OperationsBanner.selectors.ts new file mode 100644 index 000000000..0b1623222 --- /dev/null +++ b/src/app/templates/OperationsBanner/OperationsBanner.selectors.ts @@ -0,0 +1,5 @@ +export enum OperationsBannerSelectors { + errorText = 'Operations Banner/Error Text', + errorDropDownButton = 'Operations Banner/Error drop-down Button', + errorValue = 'Operations Banner/Error Value' +} diff --git a/src/app/templates/OperationsBanner.tsx b/src/app/templates/OperationsBanner/OperationsBanner.tsx similarity index 96% rename from src/app/templates/OperationsBanner.tsx rename to src/app/templates/OperationsBanner/OperationsBanner.tsx index e4bdb62b4..c48303a2b 100644 --- a/src/app/templates/OperationsBanner.tsx +++ b/src/app/templates/OperationsBanner/OperationsBanner.tsx @@ -4,9 +4,12 @@ import classNames from 'clsx'; import ReactJson from 'react-json-view'; import { ReactComponent as CopyIcon } from 'app/icons/copy.svg'; +import { setTestID } from 'lib/analytics'; import { T } from 'lib/i18n'; import useCopyToClipboard from 'lib/ui/useCopyToClipboard'; +import { OperationsBannerSelectors } from './OperationsBanner.selectors'; + type ContentsItem = { kind: string; source: string; @@ -59,6 +62,7 @@ const OperationsBanner = memo( height: '10rem', ...jsonViewStyle }} + {...setTestID(OperationsBannerSelectors.errorValue)} > {typeof opParams === 'string' ? (
diff --git a/src/app/templates/SwapForm/SwapForm.selectors.ts b/src/app/templates/SwapForm/SwapForm.selectors.ts index 49823df1e..44f634624 100644 --- a/src/app/templates/SwapForm/SwapForm.selectors.ts +++ b/src/app/templates/SwapForm/SwapForm.selectors.ts @@ -7,12 +7,12 @@ export enum SwapFormFromInputSelectors { dropdown = 'Swap Form (From) Input/Asset Drop-down', assetInput = 'Swap Form (From) Input/Asset Input', searchInput = 'Swap Form (From) Input/Search Input', - assetItem = 'Swap Form (From) Input/Asset Item' + assetDropDownButton = 'Swap Form (From) Input/Asset Drop-down Button' } export enum SwapFormToInputSelectors { dropdown = 'Swap Form (To) Input/Asset Drop-down', assetInput = 'Swap Form (To) Input/Asset Input', searchInput = 'Swap Form (To) Input/Search Input', - assetItem = 'Swap Form (To) Input/Asset Item' + assetDropDownButton = 'Swap Form (To) Input/Asset Drop-down Button' } diff --git a/src/app/templates/SwapForm/SwapForm.tsx b/src/app/templates/SwapForm/SwapForm.tsx index 552f88df8..b181afe4a 100644 --- a/src/app/templates/SwapForm/SwapForm.tsx +++ b/src/app/templates/SwapForm/SwapForm.tsx @@ -403,7 +403,7 @@ export const SwapForm: FC = () => { dropdown: SwapFormFromInputSelectors.dropdown, input: SwapFormFromInputSelectors.assetInput, searchInput: SwapFormFromInputSelectors.searchInput, - assetSelector: SwapFormFromInputSelectors.assetItem + assetDropDownButton: SwapFormFromInputSelectors.assetDropDownButton }} /> @@ -426,7 +426,7 @@ export const SwapForm: FC = () => { dropdown: SwapFormToInputSelectors.dropdown, input: SwapFormToInputSelectors.assetInput, searchInput: SwapFormToInputSelectors.searchInput, - assetSelector: SwapFormToInputSelectors.assetItem + assetDropDownButton: SwapFormToInputSelectors.assetDropDownButton }} /> diff --git a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx index b6cac776e..8d5b010c2 100644 --- a/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/AssetsMenu/AssetOption.tsx @@ -5,7 +5,7 @@ import { ListRowProps } from 'react-virtualized'; import { AssetIcon } from 'app/templates/AssetIcon'; import { AssetItemContent } from 'app/templates/AssetItemContent'; -import { setTestID } from 'lib/analytics'; +import { setAnotherSelector, setTestID } from 'lib/analytics'; import { useAssetMetadata } from 'lib/metadata'; import { isTruthy } from 'lib/utils'; @@ -34,6 +34,7 @@ export const AssetOption: FC = ({ assetSlug, selected, style, onClick }) )} onClick={handleClick} {...setTestID(AssetsMenuSelectors.assetsMenuAssetItem)} + {...setAnotherSelector('slug', assetSlug)} > diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts index f03439853..dccf6a8be 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInput.props.ts @@ -19,5 +19,5 @@ export interface SwapFormTestIDs { dropdown: string; input?: string; searchInput?: string; - assetSelector?: string; + assetDropDownButton?: string; } diff --git a/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx b/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx index 7808f94a6..6103673cc 100644 --- a/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx +++ b/src/app/templates/SwapForm/SwapFormInput/SwapFormInputHeader/SwapFormInputHeader.tsx @@ -148,7 +148,7 @@ export const SwapFormInputHeader = forwardRef(
{selectedAssetSlug ? ( <>