From c30c5510804cd2fe29a30bf2d8405a3d63b3bf90 Mon Sep 17 00:00:00 2001 From: bslabiak <112852128+bslabiak@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:19:51 +0200 Subject: [PATCH 1/6] test(extension): disable broken smoke tests (#1079) --- packages/e2e-tests/src/features/e2e/SendNftExtendedE2E.feature | 3 ++- .../e2e-tests/src/features/e2e/SendTransactionDappE2E.feature | 3 ++- .../e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/e2e-tests/src/features/e2e/SendNftExtendedE2E.feature b/packages/e2e-tests/src/features/e2e/SendNftExtendedE2E.feature index 5a05ab44f4..3abefd644c 100644 --- a/packages/e2e-tests/src/features/e2e/SendNftExtendedE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendNftExtendedE2E.feature @@ -6,7 +6,8 @@ Feature: Send NFT - Extended Browser View - E2E And I am on NFTs extended page And I use a single wallet with "Ibilecoin" NFT in extended mode - @LW-2502 @Smoke + @LW-2502 @Smoke @Pending + @issue=LW-10306 Scenario: Extended-view - Send NFT E2E And I'm sending the NFT with name: "Ibilecoin" in extended mode When I enter correct password and confirm the transaction diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature index d54705a362..ba3fceb666 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature @@ -4,7 +4,8 @@ Feature: Send Transactions from Dapp - E2E Background: Given Wallet is synced - @LW-3761 @Testnet @Smoke + @LW-3761 @Testnet @Smoke @Pending + @issue=LW-10306 Scenario: Send ADA from DApp E2E And I save token: "Cardano" balance And I open and authorize test DApp with "Only once" setting diff --git a/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature b/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature index a0b538118a..69d27b6405 100644 --- a/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature +++ b/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature @@ -1,7 +1,8 @@ @Staking-initial-E2E @E2E @Testnet Feature: Delegating funds to new pool E2E - @LW-2685 @Smoke + @LW-2685 @Smoke @Pending + @issue=LW-10306 Scenario: Extended view - Staking - Delegating funds to new pool (if not staked yet) E2E. Given I create new wallet and save wallet information And Wallet is synced From 6aa696470950b45d8f1d9a26b33ecd3853d8da0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:55:56 +0200 Subject: [PATCH 2/6] test(extension): coverage for new stake pool sorting (#1067) * test(extension): automate tests LW-10139 and LW-10141 * test(extension): speed up LW-8499 * test(extension): automate LW-10142 * test(extension): split feature file * test(extension): minor cleanup * test(extension): update after code review --- .../MultidelegationPageAssert.ts | 60 +++++-- .../e2e-tests/src/assert/stakingPageAssert.ts | 17 -- .../multidelegation/MultidelegationPage.ts | 153 +++++++++++++--- .../multidelegation/StakePoolListItem.ts | 7 +- .../src/elements/staking/StakePoolListItem.ts | 103 ----------- .../src/elements/staking/stakingPage.ts | 35 ---- .../src/enums/StakePoolListColumn.ts | 11 ++ ...MultiDelegationPageExtended.part1.feature} | 37 ++-- .../MultiDelegationPageExtended.part2.feature | 51 ++++++ .../src/features/StakingPageExtended.feature | 41 ----- .../StakingSwitchingPoolsExtendedE2E.feature | 4 +- .../e2e/StakingSwitchingPoolsPopupE2E.feature | 4 +- .../pageobject/stakingExtendedPageObject.ts | 60 ------- .../src/steps/multidelegationSteps.ts | 35 +++- packages/e2e-tests/src/steps/stakingSteps.ts | 25 --- packages/e2e-tests/src/types/sortingOrder.ts | 1 + packages/e2e-tests/src/types/staking.ts | 2 +- .../src/utils/stakePoolListContent.ts | 166 +++++++++++------- 18 files changed, 396 insertions(+), 416 deletions(-) delete mode 100644 packages/e2e-tests/src/elements/staking/StakePoolListItem.ts create mode 100644 packages/e2e-tests/src/enums/StakePoolListColumn.ts rename packages/e2e-tests/src/features/{MultiDelegationPageExtended.feature => MultiDelegationPageExtended.part1.feature} (89%) create mode 100644 packages/e2e-tests/src/features/MultiDelegationPageExtended.part2.feature delete mode 100644 packages/e2e-tests/src/features/StakingPageExtended.feature delete mode 100644 packages/e2e-tests/src/pageobject/stakingExtendedPageObject.ts create mode 100644 packages/e2e-tests/src/types/sortingOrder.ts diff --git a/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts b/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts index 3fe3f6ffb3..e38af3f713 100644 --- a/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts +++ b/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts @@ -8,6 +8,10 @@ import { StakePoolListItem } from '../../elements/multidelegation/StakePoolListI import Tooltip from '../../elements/Tooltip'; import testContext from '../../utils/testContext'; import { StakePoolGridCard } from '../../elements/multidelegation/StakePoolGridCard'; +import { StakePoolListColumnName } from '../../types/staking'; +import { SortingOrder } from '../../types/sortingOrder'; +import { mapColumnNameStringToEnum, sortColumnContent } from '../../utils/stakePoolListContent'; +import { StakePoolListColumn } from '../../enums/StakePoolListColumn'; class MultidelegationPageAssert { assertSeeStakingOnPoolsCounter = async (poolsCount: number) => { @@ -188,36 +192,37 @@ class MultidelegationPageAssert { expect(await firstStakePool.ticker.getText()).to.equal(expectedTicker); }; - assertSeeTooltipForColumn = async (columnName: string) => { + assertSeeTooltipForColumn = async (column: StakePoolListColumn) => { + await MultidelegationPage.tooltip.waitForStable(); await MultidelegationPage.tooltip.waitForDisplayed(); let expectedTooltipText; - switch (columnName) { - case 'Ticker': + switch (column) { + case StakePoolListColumn.Ticker: expectedTooltipText = await t('browsePools.tooltips.ticker', 'staking'); break; - case 'Saturation': + case StakePoolListColumn.Saturation: expectedTooltipText = await t('browsePools.tooltips.saturation', 'staking'); break; - case 'ROS': + case StakePoolListColumn.ROS: expectedTooltipText = await t('browsePools.tooltips.ros', 'staking'); break; - case 'Cost': + case StakePoolListColumn.Cost: expectedTooltipText = await t('browsePools.tooltips.cost', 'staking'); break; - case 'Margin': + case StakePoolListColumn.Margin: expectedTooltipText = await t('browsePools.tooltips.margin', 'staking'); break; - case 'Blocks': + case StakePoolListColumn.Blocks: expectedTooltipText = await t('browsePools.tooltips.blocks', 'staking'); break; - case 'Pledge': + case StakePoolListColumn.Pledge: expectedTooltipText = await t('browsePools.tooltips.pledge', 'staking'); break; - case 'Live Stake': + case StakePoolListColumn.LiveStake: expectedTooltipText = await t('browsePools.tooltips.liveStake', 'staking'); break; default: - throw new Error(`Unsupported column name: ${columnName}`); + throw new Error(`Unsupported column name: ${column}`); } expect(await MultidelegationPage.tooltip.getText()).to.equal(expectedTooltipText); }; @@ -300,6 +305,39 @@ class MultidelegationPageAssert { const cardsInARow = Math.floor(rowWidth / cardWidth); expect(cardsInARow).to.equal(expectedCardsCount); }; + + assertSeeColumnSortingIndicator = async (column: StakePoolListColumnName, order: 'ascending' | 'descending') => { + await ( + await MultidelegationPage.getColumnSortingIndicator(mapColumnNameStringToEnum(column), order) + ).waitForDisplayed(); + }; + + assertSeeStakePoolsSorted = async ( + stakePoolsDisplayType: 'list rows' | 'cards', + sortingOption: StakePoolListColumnName, + order: SortingOrder, + poolLimit?: number + ) => { + await MultidelegationPage.waitForPoolsCounterToBeGreaterThanZero(); + poolLimit ??= await MultidelegationPage.getNumberOfPoolsFromCounter(); + if (stakePoolsDisplayType === 'cards') { + // TODO: add code to handle grid cards - LW-10284 + throw new Error('Please add validation for grid cards sorting'); + } else { + const columnContent = await MultidelegationPage.extractColumnContent( + mapColumnNameStringToEnum(sortingOption), + poolLimit + ); + const sortedColumnContent = await sortColumnContent( + columnContent, + mapColumnNameStringToEnum(sortingOption), + order + ); + + expect(columnContent).to.not.be.empty; + expect(columnContent).to.deep.equal(sortedColumnContent); + } + }; } export default new MultidelegationPageAssert(); diff --git a/packages/e2e-tests/src/assert/stakingPageAssert.ts b/packages/e2e-tests/src/assert/stakingPageAssert.ts index d35d8e5776..a296741be9 100644 --- a/packages/e2e-tests/src/assert/stakingPageAssert.ts +++ b/packages/e2e-tests/src/assert/stakingPageAssert.ts @@ -1,13 +1,8 @@ import StakingPage from '../elements/staking/stakingPage'; import { TestnetPatterns } from '../support/patterns'; -import webTester from '../actor/webTester'; import StakingInfoComponent from '../elements/staking/stakingInfoComponent'; import { t } from '../utils/translationService'; -import { StakePoolListItem } from '../elements/staking/StakePoolListItem'; import StakingSuccessDrawer from '../elements/staking/StakingSuccessDrawer'; -import { Logger } from '../support/logger'; -import StakingExtendedPageObject from '../pageobject/stakingExtendedPageObject'; -import { sortColumnContent } from '../utils/stakePoolListContent'; import { expect } from 'chai'; import { StakePool } from '../data/expectedStakePoolsData'; import StakingPasswordDrawer from '../elements/staking/StakingPasswordDrawer'; @@ -114,18 +109,6 @@ class StakingPageAssert { }); }; - assertStakePoolItemsOrder = async (columnName: string, order: string) => { - const stakePoolListItem = new StakePoolListItem(); - await webTester.waitUntilSeeElement(stakePoolListItem.container(), 60_000); - const columnContent: string[] = await StakingExtendedPageObject.extractColumnContent(columnName); - Logger.log(`EXTRACTED DATA: ${columnContent}`); - const sortedColumnContent = await sortColumnContent(columnContent, columnName, order); - Logger.log(`SORTED DATA: ${sortedColumnContent}`); - - expect(columnContent).to.not.be.empty; - expect(columnContent).to.deep.equal(sortedColumnContent); - }; - assertSeeStakingPasswordDrawer = async () => { await StakingPasswordDrawer.title.waitForDisplayed(); expect(await StakingPasswordDrawer.title.getText()).to.equal( diff --git a/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts b/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts index d6311743d7..7c738530d5 100644 --- a/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts +++ b/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts @@ -8,11 +8,11 @@ import StakePoolDetails from '../staking/stakePoolDetails'; import testContext from '../../utils/testContext'; import { isPopupMode } from '../../utils/pageUtils'; import CommonDrawerElements from '../CommonDrawerElements'; -import { StakePoolListColumnType } from '../../types/staking'; import { StakePoolListItem } from './StakePoolListItem'; import { StakePoolGridCard } from './StakePoolGridCard'; import StakePoolDetailsDrawer from './StakePoolDetailsDrawer'; import MoreOptionsComponent from './MoreOptionsComponent'; +import { StakePoolListColumn } from '../../enums/StakePoolListColumn'; class MultidelegationPage { private ACTIVITY_TAB = '[data-testid="activity-tab"]'; @@ -41,6 +41,7 @@ class MultidelegationPage { private COLUMN_HEADER_BLOCKS = '[data-testid="stake-pool-list-header-blocks"]'; private COLUMN_HEADER_PLEDGE = '[data-testid="stake-pool-list-header-pledge"]'; private COLUMN_HEADER_LIVE_STAKE = '[data-testid="stake-pool-list-header-liveStake"]'; + private COLUMN_SORTING_INDICATOR_TEMPLATE = '[data-testid="stake-pool-sort-order-###"]'; private MANAGE_STAKING_BTN_NEXT = '[data-testid="preferences-next-button"]'; private CONFIRMATION_BTN_NEXT = '[data-testid="stake-pool-confirmation-btn"]'; private DELEGATED_POOL_ITEM = '[data-testid="delegated-pool-item"]'; @@ -73,6 +74,7 @@ class MultidelegationPage { private STAKE_POOL_CARD_SKELETON = '[data-testid="stake-pool-card-skeleton"]'; private SELCECTED_STAKE_POOLS_IN_GRID_VIEW = '[data-testid="selected-pools-list"] [data-testid="stake-pool-card"]'; private SELCECTED_STAKE_POOLS_IN_LIST_VIEW = '[data-testid="selected-pools-list"] [data-testid="stake-pool-item"]'; + private POOLS_COUNTER = '[data-testid="pools-counter"]'; get title() { return SectionTitle.sectionTitle; @@ -214,6 +216,10 @@ class MultidelegationPage { return $(this.MANAGE_BTN); } + get poolsCounter() { + return $(this.POOLS_COUNTER); + } + delegatedPoolLogo(index: number): ChainablePromiseElement { return $$(this.DELEGATED_POOL_ITEM)[index].$(this.DELEGATED_POOL_LOGO); } @@ -300,6 +306,43 @@ class MultidelegationPage { )) as WebdriverIO.Element; } + async getColumnSortingIndicator(columnName: StakePoolListColumn, order: 'ascending' | 'descending') { + const orderDirection = order === 'ascending' ? 'asc' : 'desc'; + const orderDirectionSelector = `${this.COLUMN_SORTING_INDICATOR_TEMPLATE.replace('###', orderDirection)}`; + let selector = ''; + + switch (columnName) { + case StakePoolListColumn.Ticker: + selector = `${this.COLUMN_HEADER_TICKER} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.Saturation: + selector = `${this.COLUMN_HEADER_SATURATION} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.ROS: + selector = `${this.COLUMN_HEADER_ROS} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.Cost: + selector = `${this.COLUMN_HEADER_COST} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.Margin: + selector = `${this.COLUMN_HEADER_MARGIN} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.Blocks: + selector = `${this.COLUMN_HEADER_BLOCKS} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.Pledge: + selector = `${this.COLUMN_HEADER_PLEDGE} ${orderDirectionSelector}`; + break; + case StakePoolListColumn.LiveStake: + selector = `${this.COLUMN_HEADER_LIVE_STAKE} ${orderDirectionSelector}`; + break; + default: + throw new Error(`Unsupported column name: ${columnName}`); + } + + return $(selector); + } + async clickAndGetTabStateAttribute(tab: 'Overview' | 'Browse pools') { let tabElement; switch (tab) { @@ -418,68 +461,69 @@ class MultidelegationPage { await poolItem.click(); } - async hoverOverColumnWithName(columnName: StakePoolListColumnType) { + async hoverOverColumn(column: StakePoolListColumn) { let header; - switch (columnName) { - case 'Ticker': + + switch (column) { + case StakePoolListColumn.Ticker: header = await this.columnHeaderTicker; break; - case 'Saturation': + case StakePoolListColumn.Saturation: header = await this.columnHeaderSaturation; break; - case 'ROS': + case StakePoolListColumn.ROS: header = await this.columnHeaderROS; break; - case 'Cost': + case StakePoolListColumn.Cost: header = await this.columnHeaderCost; break; - case 'Margin': + case StakePoolListColumn.Margin: header = await this.columnHeaderMargin; break; - case 'Blocks': + case StakePoolListColumn.Blocks: header = await this.columnHeaderBlocks; break; - case 'Pledge': + case StakePoolListColumn.Pledge: header = await this.columnHeaderPledge; break; - case 'Live Stake': + case StakePoolListColumn.LiveStake: header = await this.columnHeaderLiveStake; break; default: - throw new Error(`Unsupported column name: ${columnName}`); + throw new Error(`Unsupported column name: ${column}`); } // make hovering over ANTD component more stable await header?.$('span span span').moveTo(); } - async clickOnColumnWithName(columnName: StakePoolListColumnType) { - switch (columnName) { - case 'Ticker': + async clickOnColumn(column: StakePoolListColumn) { + switch (column) { + case StakePoolListColumn.Ticker: await this.columnHeaderTicker.click(); break; - case 'Saturation': + case StakePoolListColumn.Saturation: await this.columnHeaderSaturation.click(); break; - case 'ROS': + case StakePoolListColumn.ROS: await this.columnHeaderROS.click(); break; - case 'Cost': + case StakePoolListColumn.Cost: await this.columnHeaderCost.click(); break; - case 'Margin': + case StakePoolListColumn.Margin: await this.columnHeaderMargin.click(); break; - case 'Blocks': + case StakePoolListColumn.Blocks: await this.columnHeaderBlocks.click(); break; - case 'Pledge': + case StakePoolListColumn.Pledge: await this.columnHeaderPledge.click(); break; - case 'Live Stake': + case StakePoolListColumn.LiveStake: await this.columnHeaderLiveStake.click(); break; default: - throw new Error(`Unsupported column name: ${columnName}`); + throw new Error(`Unsupported column name: ${column}`); } } @@ -543,6 +587,69 @@ class MultidelegationPage { const selectedTickers = await this.getTickersOfSelectedPools(viewType); testContext.save('selectedTickers', selectedTickers); } + + async getNumberOfPoolsFromCounter(): Promise { + const poolsCounterText = await this.poolsCounter.getText(); + return Number(Number(poolsCounterText.replace(/.*\(/, '').replace(')', '').replace(',', ''))); + } + + async waitForPoolsCounterToBeGreaterThanZero(): Promise { + await this.poolsCounter.waitForDisplayed(); + await browser.waitUntil(async () => (await this.getNumberOfPoolsFromCounter()) > 0, { + timeoutMsg: 'No stake pools!' + }); + } + + async extractColumnContent(columnName: StakePoolListColumn, poolLimit = 100): Promise { + const columnContent: string[] = []; + + await this.listContainer.waitForStable(); + await browser.pause(500); + + for (let i = 0; i < poolLimit; i++) { + const displayedPoolsCounter = await this.displayedPools.length; + const listItem = new StakePoolListItem(i); + // Load more pools if all visible ones were processed + if (i % (displayedPoolsCounter - 1) === 0) { + await listItem.container.scrollIntoView(); + await this.stakePoolListRowSkeleton.waitForExist({ + reverse: true, + interval: 100, + timeout: 30_000 + }); + } + switch (columnName) { + case StakePoolListColumn.Ticker: + columnContent.push(await listItem.ticker.getText()); + break; + case StakePoolListColumn.Saturation: + columnContent.push(await listItem.saturation.getText()); + break; + case StakePoolListColumn.ROS: + columnContent.push(await listItem.ros.getText()); + break; + case StakePoolListColumn.Cost: + columnContent.push(await listItem.cost.getText()); + break; + case StakePoolListColumn.Margin: + columnContent.push(await listItem.margin.getText()); + break; + case StakePoolListColumn.Blocks: + columnContent.push(await listItem.blocks.getText()); + break; + case StakePoolListColumn.Pledge: + columnContent.push(await listItem.pledge.getText()); + break; + case StakePoolListColumn.LiveStake: + columnContent.push(await listItem.liveStake.getText()); + break; + default: + throw new Error(`Not supported column name: ${columnName}`); + } + } + + return columnContent; + } } export default new MultidelegationPage(); diff --git a/packages/e2e-tests/src/elements/multidelegation/StakePoolListItem.ts b/packages/e2e-tests/src/elements/multidelegation/StakePoolListItem.ts index 3543d58463..bfe82ddb43 100644 --- a/packages/e2e-tests/src/elements/multidelegation/StakePoolListItem.ts +++ b/packages/e2e-tests/src/elements/multidelegation/StakePoolListItem.ts @@ -3,7 +3,6 @@ import { ChainablePromiseElement } from 'webdriverio'; export class StakePoolListItem { private SELECTED_POOLS_LIST = '[data-testid="selected-pools-list"]'; - private AVAILABLE_POOLS_LIST = '[data-testid="stake-pool-list-scroll-wrapper"]'; private LIST_ITEM = '[data-testid="stake-pool-item"]'; private CHECKBOX = '[data-testid="stake-pool-list-checkbox"]'; private TICKER = '[data-testid="stake-pool-list-ticker"]'; @@ -18,9 +17,9 @@ export class StakePoolListItem { protected listItem; constructor(index = 0, isOnSelectedPoolsList = false) { - this.listItem = $(isOnSelectedPoolsList ? this.SELECTED_POOLS_LIST : this.AVAILABLE_POOLS_LIST).$$(this.LIST_ITEM)[ - index - ]; + this.listItem = $( + isOnSelectedPoolsList ? `${this.SELECTED_POOLS_LIST} ${this.LIST_ITEM}` : `[data-item-index="${index}"]` + ); } get container(): ChainablePromiseElement { diff --git a/packages/e2e-tests/src/elements/staking/StakePoolListItem.ts b/packages/e2e-tests/src/elements/staking/StakePoolListItem.ts deleted file mode 100644 index ef31ac4900..0000000000 --- a/packages/e2e-tests/src/elements/staking/StakePoolListItem.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* eslint-disable no-undef */ -import webTester, { LocatorStrategy } from '../../actor/webTester'; -import { WebElement, WebElementFactory as Factory } from './../webElement'; - -export class StakePoolListItem extends WebElement { - protected TABLE_ROW = '//div[@data-testid="stake-pool-table-item"]'; - private LOGO = '//img[@data-testid="stake-pool-list-logo"]'; - private NAME = '//h6[@data-testid="stake-pool-list-name"]'; - private TICKER = '//p[@data-testid="stake-pool-list-ticker"]'; - private ROS = '//p[@data-testid="stake-pool-list-ros"]'; - private COST = '//p[@data-testid="stake-pool-list-cost"]'; - private SATURATION = '//p[@data-testid="stake-pool-list-saturation"]'; - - constructor(index?: number) { - super(); - this.TABLE_ROW = - typeof index === 'undefined' || index.toString() === '' ? this.TABLE_ROW : `(${this.TABLE_ROW})[${index}]`; - } - - container(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}`, 'xpath'); - } - - tableRowWithName(poolName: string): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}[.//h6[contains(text(), '${poolName}')]]`, 'xpath'); - } - - async getRows(): Promise { - return $$(`${this.TABLE_ROW}`); - } - - logo(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}${this.LOGO}`, 'xpath'); - } - - logoWithIndex(index: number): WebElement { - return Factory.fromSelector(`(${this.LOGO})[${index}]`, 'xpath'); - } - - name(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}${this.NAME}`, 'xpath'); - } - - nameWithIndex(index: number): WebElement { - return Factory.fromSelector(`(${this.NAME})[${index}]`, 'xpath'); - } - - ticker(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}${this.TICKER}`, 'xpath'); - } - - tickerWithIndex(index: number): WebElement { - return Factory.fromSelector(`(${this.TICKER})[${index}]`, 'xpath'); - } - - ros(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}${this.ROS}`, 'xpath'); - } - - cost(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}${this.COST}`, 'xpath'); - } - - saturation(): WebElement { - return Factory.fromSelector(`${this.TABLE_ROW}${this.SATURATION}`, 'xpath'); - } - - async getName(): Promise { - return await webTester.getTextValueFromElement(this.name()); - } - - async getTicker(): Promise { - return await webTester.getTextValueFromElement(this.ticker()); - } - - async getRos(): Promise { - return await webTester.getTextValueFromElement(this.ros()); - } - - async getCost(): Promise { - return await webTester.getTextValueFromElement(this.cost()); - } - - async getSaturation(): Promise { - return await webTester.getTextValueFromElement(this.saturation()); - } - - async getNameWithIndex(index: number): Promise { - return await webTester.getTextValueFromElement(this.nameWithIndex(index)); - } - - async getTickerWithIndex(index: number): Promise { - return await webTester.getTextValueFromElement(this.tickerWithIndex(index)); - } - - toJSLocator(): string { - return this.TABLE_ROW; - } - - locatorStrategy(): LocatorStrategy { - return 'xpath'; - } -} diff --git a/packages/e2e-tests/src/elements/staking/stakingPage.ts b/packages/e2e-tests/src/elements/staking/stakingPage.ts index 12f702cd7d..1793d9bcb3 100644 --- a/packages/e2e-tests/src/elements/staking/stakingPage.ts +++ b/packages/e2e-tests/src/elements/staking/stakingPage.ts @@ -2,17 +2,11 @@ import SectionTitle from '../sectionTitle'; class StakingPage { - private SEARCH_ICON = '[data-testid="search-icon"]'; private SEARCH_INPUT = '.ant-select-selection-search input'; - private SEARCH_INPUT_PLACEHOLDER_IN_POPUP = '.ant-select-selection-placeholder'; - private STAKE_POOL_LIST_HEADER_TEMPLATE = '[data-testid="stake-pool-list-header-###COLUMN_NAME###"]'; - private EMPTY_SEARCH_RESULTS_IMAGE = '[data-testid="stake-pool-table-empty-image"]'; - private EMPTY_SEARCH_RESULTS_MESSAGE = '[data-testid="stake-pool-table-empty-message"]'; private SEARCH_LOADER = '[data-testid="search-loader"]'; private STAKE_POOL_LIST_COST = '[data-testid="stake-pool-list-cost"]'; private STATS_TITLE = '[data-testid="stats-title"]'; private STATS_VALUE = '[data-testid="stats-value"]'; - private STAKE_POOL_TABLE_ROW = '[data-testid="stake-pool-table-item"]'; get title() { return SectionTitle.sectionTitle; @@ -22,10 +16,6 @@ class StakingPage { return SectionTitle.sectionCounter; } - get stakingPageSearchIcon() { - return $(this.SEARCH_ICON); - } - get statsTitle() { return $$(this.STATS_TITLE); } @@ -34,14 +24,6 @@ class StakingPage { return $(this.SEARCH_INPUT); } - get searchInputPlaceholderInPopup() { - return $(this.SEARCH_INPUT_PLACEHOLDER_IN_POPUP); - } - - get rows() { - return $$(this.STAKE_POOL_TABLE_ROW); - } - get statsValues() { return $$(this.STATS_VALUE); } @@ -62,27 +44,10 @@ class StakingPage { return $$(this.STAKE_POOL_LIST_COST); } - get statsValue() { - return $$(this.STATS_VALUE); - } - - get emptySearchResultsImage() { - return $(this.EMPTY_SEARCH_RESULTS_IMAGE); - } - - get emptySearchResultsMessage() { - return $(this.EMPTY_SEARCH_RESULTS_MESSAGE); - } - get searchLoader() { return $(this.SEARCH_LOADER); } - stakingPoolListColumnHeader(listHeader: string) { - const headerColumnSelector = this.STAKE_POOL_LIST_HEADER_TEMPLATE.replace('###COLUMN_NAME###', listHeader); - return $(headerColumnSelector); - } - stakingPoolWithName(poolName: string) { return $(`h6=${poolName}`); } diff --git a/packages/e2e-tests/src/enums/StakePoolListColumn.ts b/packages/e2e-tests/src/enums/StakePoolListColumn.ts new file mode 100644 index 0000000000..1c90d58b68 --- /dev/null +++ b/packages/e2e-tests/src/enums/StakePoolListColumn.ts @@ -0,0 +1,11 @@ +/* eslint-disable no-unused-vars */ +export enum StakePoolListColumn { + Ticker = 'Ticker', + Saturation = 'Saturation', + ROS = 'ROS', + Cost = 'Cost', + Margin = 'Margin', + Blocks = 'Blocks', + Pledge = 'Pledge', + LiveStake = 'Live Stake' +} diff --git a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature b/packages/e2e-tests/src/features/MultiDelegationPageExtended.part1.feature similarity index 89% rename from packages/e2e-tests/src/features/MultiDelegationPageExtended.feature rename to packages/e2e-tests/src/features/MultiDelegationPageExtended.part1.feature index 91e855d6e7..335fc12440 100644 --- a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature +++ b/packages/e2e-tests/src/features/MultiDelegationPageExtended.part1.feature @@ -88,22 +88,27 @@ Feature: Staking Page - Extended View Then I see the Network Info component with the expected content @LW-8499 @Testnet @Mainnet - Scenario Outline: Extended View - Staking - Show tooltip for column in browse pools section + Scenario: Extended View - Staking - Show tooltip for columns in browse pools section When I navigate to Staking extended page And I open Browse pools tab And I switch to list view on "Browse pools" tab - When I hover over "" column name in stake pool list - Then tooltip for "" column is displayed - Examples: - | column_name | - | Ticker | - | Saturation | -# | ROS | #TODO: Uncomment when USE_ROS_STAKING_COLUMN=true - | Cost | - | Margin | - | Blocks | - | Pledge | - | Live Stake | + When I hover over "Ticker" column name in stake pool list + Then tooltip for "Ticker" column is displayed + When I hover over "Saturation" column name in stake pool list + Then tooltip for "Saturation" column is displayed + #TODO: Uncomment when USE_ROS_STAKING_COLUMN=true + #When I hover over "ROS" column name in stake pool list + #Then tooltip for "ROS" column is displayed + When I hover over "Cost" column name in stake pool list + Then tooltip for "Cost" column is displayed + When I hover over "Margin" column name in stake pool list + Then tooltip for "Margin" column is displayed + When I hover over "Blocks" column name in stake pool list + Then tooltip for "Blocks" column is displayed + When I hover over "Pledge" column name in stake pool list + Then tooltip for "Pledge" column is displayed + When I hover over "Live Stake" column name in stake pool list + Then tooltip for "Live Stake" column is displayed @LW-8637 @Testnet @Mainnet Scenario: Extended View - Staking password screen details @@ -186,12 +191,6 @@ Feature: Staking Page - Extended View | list | I refresh the page | | list | I open Overview tab | - @LW-10143 @Testnet @Mainnet - Scenario: Extended View - Staking - More options - Sorting options are displayed - When I am on Staking extended page - And I open Browse pools tab - Then "More options" component with stake pool sorting options is displayed - @LW-9996 @Testnet @Mainnet Scenario: Extended View - Grid - display stake pool cards based on browser width When I am on Staking extended page diff --git a/packages/e2e-tests/src/features/MultiDelegationPageExtended.part2.feature b/packages/e2e-tests/src/features/MultiDelegationPageExtended.part2.feature new file mode 100644 index 0000000000..b1975ca611 --- /dev/null +++ b/packages/e2e-tests/src/features/MultiDelegationPageExtended.part2.feature @@ -0,0 +1,51 @@ +@Staking-NonDelegatedFunds-Extended +Feature: Staking Page - Extended View + + Background: + Given Lace is ready for test + + @LW-10143 @Testnet @Mainnet + Scenario: Extended View - Staking - More options - Sorting options are displayed + When I am on Staking extended page + And I open Browse pools tab + Then "More options" component with stake pool sorting options is displayed + + @LW-10139 @LW-10141 @LW-10142 @Testnet @Mainnet + Scenario: Extended View - Staking - List View - Stake pool list sorting by ticker (default) + When I am on Staking extended page + And I open Browse pools tab + And I switch to list view on "Browse pools" tab + Then stake pool list view is displayed + And ascending sorting indicator is displayed for "Ticker" column + And stake pool list rows are sorted by "Ticker" in ascending order + When I click on stake pools table "Ticker" column header + Then descending sorting indicator is displayed for "Ticker" column + And stake pool list rows are sorted by "Ticker" in descending order + When I click on stake pools table "Ticker" column header + Then ascending sorting indicator is displayed for "Ticker" column + And stake pool list rows are sorted by "Ticker" in ascending order + + @LW-10141 @LW-10142 @Testnet @Mainnet + Scenario Outline: Extended View - Staking - List View - sorting by column - + When I am on Staking extended page + And I open Browse pools tab + And I switch to list view on "Browse pools" tab + Then stake pool list view is displayed + When I click on stake pools table "" column header + Then sorting indicator is displayed for "" column + And stake pool list rows are sorted by "" in order + When I click on stake pools table "" column header + Then sorting indicator is displayed for "" column + And stake pool list rows are sorted by "" in order + When I click on stake pools table "" column header + Then sorting indicator is displayed for "" column + And stake pool list rows are sorted by "" in order + Examples: + | column | default_order | modified_order | + | Saturation | descending | ascending | +# | ROS | descending | ascending |# TODO: Uncomment when USE_ROS_STAKING_COLUMN=true + | Cost | ascending | descending | + | Margin | ascending | descending | + | Blocks | descending | ascending | + | Pledge | descending | ascending | + | Live Stake | descending | ascending | diff --git a/packages/e2e-tests/src/features/StakingPageExtended.feature b/packages/e2e-tests/src/features/StakingPageExtended.feature deleted file mode 100644 index 4cd6d3f217..0000000000 --- a/packages/e2e-tests/src/features/StakingPageExtended.feature +++ /dev/null @@ -1,41 +0,0 @@ -@Staking-NonDelegatedFunds-Extended @Pending -Feature: Staking Page - Extended Browser View - - Background: - Given Wallet is synced - - @LW-4024 @Pending @Testnet @Mainnet - @issue=ADP-2344 - Scenario: Extended View - Stake pool list default sorting by ROS - When I navigate to Staking extended page - And I reveal all stake pools - Then the results are in descending order according to "ros" column - - @LW-2706 @Pending @Testnet @Mainnet - @issue=ADP-2344 - Scenario Outline: Extended View - Sort lists ascending - column: - When I navigate to Staking extended page - And I click on the "" column header - And I reveal all stake pools - Then the results are in ascending order according to "" column - Examples: - | column | - | name | - | ros | - | cost | - | saturation | - - @LW-2706 @Pending @Testnet @Mainnet - @issue=ADP-2344 - Scenario Outline: Extended View - Sort lists descending - column - When I navigate to Staking extended page - And I click on the "" column header - And I click on the "" column header - And I reveal all stake pools - Then the results are in descending order according to "" column - Examples: - | column | - | name | - | ros | - | cost | - | saturation | diff --git a/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature b/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature index ba9e1a07ec..8b3c3bb024 100644 --- a/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature +++ b/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature @@ -13,7 +13,7 @@ Feature: Staking Page - Switching pools - Extended Browser View - E2E And I wait for single search result And I click stake pool with name "OtherStakePool" Then I see drawer with "OtherStakePool" stake pool details and a button available for staking - And I save stake pool info + And I save stake pool details When I click "Stake on this pool" button on stake pool details drawer And I click "Fine by me" button on "Switching pool?" modal Then I see drawer with stakepool: "OtherStakePool" confirmation screen in extended mode @@ -37,7 +37,7 @@ Feature: Staking Page - Switching pools - Extended Browser View - E2E And I wait for single search result And I click stake pool with name "-" Then I see drawer with stake pool details without metadata and a button available for staking - And I save stake pool info + And I save stake pool details When I click "Stake on this pool" button on stake pool details drawer And I click "Fine by me" button on "Switching pool?" modal And I click "Next" button on staking confirmation drawer diff --git a/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsPopupE2E.feature b/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsPopupE2E.feature index f61f7b43f9..553979ca9c 100644 --- a/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsPopupE2E.feature +++ b/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsPopupE2E.feature @@ -13,7 +13,7 @@ Feature: Staking Page - Switching pools - Popup View - E2E And I wait for single search result And I click stake pool with name "OtherStakePool" Then I see drawer with "OtherStakePool" stake pool details and a button available for staking - And I save stake pool info + And I save stake pool details When I click "Stake on this pool" button on stake pool details drawer And I click "Fine by me" button on "Switching pool?" modal Then I see drawer with stakepool: "OtherStakePool" confirmation screen in popup mode @@ -36,7 +36,7 @@ Feature: Staking Page - Switching pools - Popup View - E2E And I wait for single search result And I click stake pool with name "-" Then I see drawer with stake pool details without metadata and a button available for staking - When I save stake pool info + When I save stake pool details And I click "Stake on this pool" button on stake pool details drawer And I click "Fine by me" button on "Switching pool?" modal And I click "Next" button on staking confirmation drawer diff --git a/packages/e2e-tests/src/pageobject/stakingExtendedPageObject.ts b/packages/e2e-tests/src/pageobject/stakingExtendedPageObject.ts deleted file mode 100644 index 414ad5568c..0000000000 --- a/packages/e2e-tests/src/pageobject/stakingExtendedPageObject.ts +++ /dev/null @@ -1,60 +0,0 @@ -import webTester from '../actor/webTester'; -import StakingPage from '../elements/staking/stakingPage'; -import { StakePoolListItem } from '../elements/staking/StakePoolListItem'; -import testContext from '../utils/testContext'; -import StakePoolDetails from '../elements/staking/stakePoolDetails'; - -class StakingExtendedPageObject { - async clickStakePoolListHeader(listHeader: string) { - await StakingPage.stakingPoolListColumnHeader(listHeader).scrollIntoView(); - await StakingPage.stakingPoolListColumnHeader(listHeader).click(); - } - - async revealAllStakePools(): Promise { - const stakePoolListItem = new StakePoolListItem(); - await webTester.waitUntilSeeElement(stakePoolListItem.container(), 6000); - - const expectedTotalRows = Number((await StakingPage.counter.getText()).replace(/\D/g, '')); - let displayedRows = (await stakePoolListItem.getRows()).length; - - while (displayedRows < expectedTotalRows) { - await $(new StakePoolListItem(displayedRows).toJSLocator()).scrollIntoView(); - displayedRows = (await stakePoolListItem.getRows()).length; - } - } - - async extractColumnContent(columnName: string): Promise { - const rowsNumber = (await new StakePoolListItem().getRows()).length; - const columnContent: string[] = []; - for (let i = 1; i <= rowsNumber; i++) { - const listItem = new StakePoolListItem(i); - switch (columnName) { - case 'name': - columnContent.push((await listItem.getName()) as string); - break; - case 'ros': - columnContent.push((await listItem.getRos()) as string); - break; - case 'cost': - columnContent.push((await listItem.getCost()) as string); - break; - case 'saturation': - columnContent.push((await listItem.getSaturation()) as string); - break; - } - } - - return columnContent; - } - - saveStakePoolInfo = async () => { - const poolName = (await StakePoolDetails.poolName.getText()) as string; - testContext.save('poolName', poolName); - const poolTicker = (await StakePoolDetails.poolTicker.getText()) as string; - testContext.save('poolTicker', poolTicker); - const poolID = (await StakePoolDetails.poolId.getText()) as string; - testContext.save('poolID', poolID); - }; -} - -export default new StakingExtendedPageObject(); diff --git a/packages/e2e-tests/src/steps/multidelegationSteps.ts b/packages/e2e-tests/src/steps/multidelegationSteps.ts index 8bbe8713cb..8fa11fa1f2 100644 --- a/packages/e2e-tests/src/steps/multidelegationSteps.ts +++ b/packages/e2e-tests/src/steps/multidelegationSteps.ts @@ -27,9 +27,10 @@ import StartStakingPage from '../elements/multidelegation/StartStakingPage'; import PortfolioBar from '../elements/multidelegation/PortfolioBar'; import PortfolioBarAssert from '../assert/multidelegation/PortfolioBarAssert'; import ChangingStakingPreferencesModalAssert from '../assert/multidelegation/ChangingStakingPreferencesModalAssert'; -import { StakePoolListColumnType, StakePoolSortingOptionType } from '../types/staking'; +import { StakePoolListColumnName, StakePoolSortingOptionType } from '../types/staking'; import SwitchingStakePoolModal from '../elements/staking/SwitchingStakePoolModal'; import MoreOptionsComponentAssert from '../assert/multidelegation/MoreOptionsComponentAssert'; +import { mapColumnNameStringToEnum } from '../utils/stakePoolListContent'; const validPassword = 'N_8J@bne87A'; @@ -235,15 +236,15 @@ Then(/^\(if applicable\) first stake pool search result has "([^"]*)" ticker$/, When( /^I hover over "(Ticker|Saturation|ROS|Cost|Margin|Blocks|Pledge|Live Stake)" column name in stake pool list$/, - async (columnName: StakePoolListColumnType) => { - await MultidelegationPage.hoverOverColumnWithName(columnName); + async (columnName: StakePoolListColumnName) => { + await MultidelegationPage.hoverOverColumn(mapColumnNameStringToEnum(columnName)); } ); Then( /^tooltip for "(Ticker|Saturation|ROS|Cost|Margin|Blocks|Pledge|Live Stake)" column is displayed$/, - async (columnName: StakePoolListColumnType) => { - await MultidelegationPageAssert.assertSeeTooltipForColumn(columnName); + async (columnName: StakePoolListColumnName) => { + await MultidelegationPageAssert.assertSeeTooltipForColumn(mapColumnNameStringToEnum(columnName)); } ); @@ -493,8 +494,8 @@ Then(/^I see (\d+) stake pool cards in a row$/, async (cardsCount: number) => { When( /^I click on stake pools table "(Ticker|Saturation|ROS|Cost|Margin|Blocks|Pledge|Live Stake)" column header$/, - async (headerName: StakePoolListColumnType) => { - await MultidelegationPage.clickOnColumnWithName(headerName); + async (headerName: StakePoolListColumnName) => { + await MultidelegationPage.clickOnColumn(mapColumnNameStringToEnum(headerName)); } ); @@ -504,9 +505,29 @@ When( await MultidelegationPage.moreOptionsComponent.selectSortingOption(sortingOption); } ); + Then( /^"More options" component with stake pool (sorting|filtering) options is displayed$/, async (tab: 'sorting' | 'filtering') => { await MoreOptionsComponentAssert.assertSeeMoreOptionsComponent(tab); } ); + +Then( + /^(ascending|descending) sorting indicator is displayed for "(Ticker|Saturation|ROS|Cost|Margin|Blocks|Pledge|Live Stake)" column$/, + async (order: 'ascending' | 'descending', sortingOption: StakePoolListColumnName) => { + await MultidelegationPageAssert.assertSeeColumnSortingIndicator(sortingOption, order); + } +); + +Then( + /^stake pool (list rows|cards) are sorted by "(Ticker|Saturation|ROS|Cost|Margin|Blocks|Pledge|Live Stake)" in (ascending|descending) order$/, + async ( + stakePoolsDisplayType: 'list rows' | 'cards', + sortingOption: StakePoolListColumnName, + order: 'ascending' | 'descending' + ) => { + const poolLimit = 100; // Limit verification to 100 stake pools due to time constraints + await MultidelegationPageAssert.assertSeeStakePoolsSorted(stakePoolsDisplayType, sortingOption, order, poolLimit); + } +); diff --git a/packages/e2e-tests/src/steps/stakingSteps.ts b/packages/e2e-tests/src/steps/stakingSteps.ts index f89e4f0491..ba2382abc7 100644 --- a/packages/e2e-tests/src/steps/stakingSteps.ts +++ b/packages/e2e-tests/src/steps/stakingSteps.ts @@ -1,13 +1,10 @@ import { Then, When } from '@cucumber/cucumber'; import stakingPageAssert from '../assert/stakingPageAssert'; import stakePoolDetailsAssert from '../assert/stakePoolDetailsAssert'; -import stakingExtendedPageObject from '../pageobject/stakingExtendedPageObject'; import drawerCommonExtendedAssert from '../assert/drawerCommonExtendedAssert'; import { getStakePoolById, getStakePoolByName, StakePoolsData } from '../data/expectedStakePoolsData'; import testContext from '../utils/testContext'; import transactionDetailsAssert, { ExpectedActivityDetails } from '../assert/transactionDetailsAssert'; -import { StakePoolListItem } from '../elements/staking/StakePoolListItem'; -import webTester from '../actor/webTester'; import StakingExitModalAssert from '../assert/stakingExitModalAssert'; import extensionUtils from '../utils/utils'; import stakingConfirmationScreenAssert from '../assert/stakingConfirmationScreenAssert'; @@ -125,12 +122,6 @@ Then(/^the stakepool drawer is opened with "([^"]*)" stake pool information$/, a await drawerCommonExtendedAssert.assertSeeDrawerWithTitle(poolName); }); -When(/^I click on the "(.*)" column header$/, async (listHeader: string) => { - const stakePoolListItem = new StakePoolListItem(); - await webTester.waitUntilSeeElement(stakePoolListItem.container(), 60_000); - await stakingExtendedPageObject.clickStakePoolListHeader(listHeader); -}); - // eslint-disable-next-line no-unused-vars,@typescript-eslint/no-unused-vars Then(/^The Tx details are displayed for Staking (with|without) metadata$/, async (_ignored: 'with' | 'without') => { // no need to distinguish between pools with/without metadata @@ -144,22 +135,6 @@ Then(/^The Tx details are displayed for Staking (with|without) metadata$/, async await transactionDetailsAssert.assertSeeActivityDetails(expectedActivityDetails); }); -Then( - /^the results are in (ascending|descending) order according to "([^"]*)" column$/, - async (order: 'ascending' | 'descending', column: string) => { - await stakingPageAssert.assertStakePoolItemsOrder(column, order); - } -); - -When(/^I reveal all stake pools$/, async () => { - await webTester.waitUntilSeeElement(new StakePoolListItem().container(), 60_000); - await stakingExtendedPageObject.revealAllStakePools(); -}); - -When(/^I save stake pool info$/, async () => { - await stakingExtendedPageObject.saveStakePoolInfo(); -}); - Then(/^Staking password screen is displayed$/, async () => { await stakingPageAssert.assertSeeStakingPasswordDrawer(); }); diff --git a/packages/e2e-tests/src/types/sortingOrder.ts b/packages/e2e-tests/src/types/sortingOrder.ts new file mode 100644 index 0000000000..22b91c4cd7 --- /dev/null +++ b/packages/e2e-tests/src/types/sortingOrder.ts @@ -0,0 +1 @@ +export type SortingOrder = 'ascending' | 'descending'; diff --git a/packages/e2e-tests/src/types/staking.ts b/packages/e2e-tests/src/types/staking.ts index be72dc088f..d8edd320b1 100644 --- a/packages/e2e-tests/src/types/staking.ts +++ b/packages/e2e-tests/src/types/staking.ts @@ -1,4 +1,4 @@ -export type StakePoolListColumnType = +export type StakePoolListColumnName = | 'Ticker' | 'Saturation' | 'ROS' diff --git a/packages/e2e-tests/src/utils/stakePoolListContent.ts b/packages/e2e-tests/src/utils/stakePoolListContent.ts index 4151e756ce..24738e188f 100644 --- a/packages/e2e-tests/src/utils/stakePoolListContent.ts +++ b/packages/e2e-tests/src/utils/stakePoolListContent.ts @@ -1,104 +1,138 @@ -import { Asset } from '../data/Asset'; +import type { SortingOrder } from '../types/sortingOrder'; +import type { StakePoolListColumnName } from '../types/staking'; +import { StakePoolListColumn } from '../enums/StakePoolListColumn'; -interface Cost { - percentage: number; - ada: number; +interface AbbreviatedValue { + value: number; + suffix: '-' | 'K' | 'M'; } +const suffixOrderPriority = { + '-': 0, + K: 1, + M: 2 +}; + const emojiRegex = - // eslint-disable-next-line max-len /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g; -const parseCostStringToObject = (cost: string): Cost => { - let parsedItem: Cost = { percentage: 0, ada: 0 }; - if (cost.includes('%') && cost.includes('ADA')) { - const splitItem = cost.split('+'); - parsedItem = { - percentage: Number.parseFloat(splitItem[0].replace(/%/, '')), - ada: Number.parseFloat(splitItem[1].replace(/ADA/, '')) - }; - } +const sortTickerColumnContent = (columnContent: string[], order: SortingOrder): string[] => { + const itemsWithNoTicker = columnContent.filter((item) => item === '-'); + const itemsWithTicker = columnContent.filter((item) => item !== '-'); - if (cost.includes('%') && !cost.includes(Asset.CARDANO.ticker)) { - parsedItem = { - percentage: Number.parseFloat(cost.replace(/%/, '')), - ada: 0 - }; - } + const sortedItems = [...itemsWithTicker].sort((a, b) => { + const nameA = a.replace(emojiRegex, '').replace(' ', '').trim(); + const nameB = b.replace(emojiRegex, '').replace(' ', '').trim(); + return nameA.localeCompare(nameB); + }); - if (!cost.includes('%') && cost.includes(Asset.CARDANO.ticker)) { - parsedItem = { - percentage: 0, - ada: Number.parseFloat(cost.replace(/ADA/, '')) - }; + if (order === 'descending') { + sortedItems.reverse(); } - return parsedItem; + sortedItems.push(...itemsWithNoTicker); + + return sortedItems; }; -const parseCostObjectToString = (cost: Cost): string => { - let parsedItem = `${Number(cost.percentage).toFixed(2)}%`; +const sortBlocksColumnContent = (columnContent: string[], order: SortingOrder): string[] => { + const parsedColumnContent = columnContent.map((item) => Number(item.replace(',', ''))); + const sortedColumnContent = [...parsedColumnContent].sort((a, b) => a - b); - if (cost.ada > 0) { - parsedItem = `${parsedItem} + ${Number(cost.ada)}${Asset.CARDANO.ticker}`; + if (order === 'descending') { + sortedColumnContent.reverse(); } - return parsedItem; + return sortedColumnContent.map((item) => item.toLocaleString()); }; -export const sortNameColumn = (columnContent: string[], order: string): string[] => { - const itemsWithNoName = columnContent.filter((item) => item === '-'); - const itemsWithName = columnContent.filter((item) => item !== '-'); - - const sortedItems = [...itemsWithName].sort((a, b) => { - const nameA = a.replace(emojiRegex, '').replace(' ', '').trim(); - const nameB = b.replace(emojiRegex, '').replace(' ', '').trim(); - return nameA.localeCompare(nameB); - }); - if (order === 'descending') { - sortedItems.reverse(); +const parseValueFromColumnIntoAbbreviatedValueObject = (valueFromColumn: string): AbbreviatedValue => { + if (valueFromColumn.endsWith('K')) { + return { + value: Number(valueFromColumn.slice(0, -1)), + suffix: 'K' + }; + } + if (valueFromColumn.endsWith('M')) { + return { + value: Number(valueFromColumn.slice(0, -1)), + suffix: 'M' + }; } - sortedItems.push(...itemsWithNoName); + return { + value: Number(valueFromColumn), + suffix: '-' + }; +}; - return sortedItems; +const parseAbbreviatedValueObjectIntoString = (abbreviatedValueObject: AbbreviatedValue): string => + `${abbreviatedValueObject.value}${ + ['K', 'M'].includes(abbreviatedValueObject.suffix) ? abbreviatedValueObject.suffix : '' + }`; + +const compareAbbreviatedValues = (abbreviatedValue1: AbbreviatedValue, abbreviatedValue2: AbbreviatedValue): number => { + if (suffixOrderPriority[abbreviatedValue1.suffix] - suffixOrderPriority[abbreviatedValue2.suffix] === 0) { + return abbreviatedValue1.value - abbreviatedValue2.value; + } + return suffixOrderPriority[abbreviatedValue1.suffix] - suffixOrderPriority[abbreviatedValue2.suffix]; }; -export const sortCostColumn = (columnContent: string[], order: string): string[] => { - const parsedColumnContent = columnContent.map((item) => parseCostStringToObject(item)); - const costSorted = [...parsedColumnContent].sort((a, b) => a.ada - b.ada || a.percentage - b.percentage); +const sortColumnWithPercentageValues = (columnContent: string[], order: string): string[] => { + const parsedColumnContent = columnContent.map((item) => Number(item.replace(/%/, '')).toFixed(2)); + const sortedColumnContent = [...parsedColumnContent].sort((a, b) => Number(a) - Number(b)); + if (order === 'descending') { - costSorted.reverse(); + sortedColumnContent.reverse(); } - return costSorted.map((item) => parseCostObjectToString(item)); + + return sortedColumnContent.map((item) => String(`${item}%`)); }; -export const sortColumnWithPercentageValues = (columnContent: string[], order: string): string[] => { - const columnContentWithNumbers = columnContent.map((item) => Number.parseFloat(item.replace(/%/, ''))); - const sortedColumnContentWithNumbers = [...columnContentWithNumbers].sort((a, b) => a - b); +const sortColumnWithAbbreviatedNumbers = (columnContent: string[], order: string): string[] => { + const parsedColumnContent: AbbreviatedValue[] = columnContent.map((item) => + parseValueFromColumnIntoAbbreviatedValueObject(item) + ); + const sortedColumnContent = [...parsedColumnContent].sort((a, b) => compareAbbreviatedValues(a, b)); + if (order === 'descending') { - sortedColumnContentWithNumbers.reverse(); + sortedColumnContent.reverse(); } - return sortedColumnContentWithNumbers.map((item) => String(`${item}%`)); + + return sortedColumnContent.map((item) => parseAbbreviatedValueObjectIntoString(item)); }; export const sortColumnContent = async ( columnContent: string[], - columnName: string, - order: string + sortingOption: StakePoolListColumn, + order: SortingOrder ): Promise => { let sortedColumnContent: string[] = []; - if (columnName === 'name') { - sortedColumnContent = sortNameColumn(columnContent, order); - } - - if (['ros', 'saturation'].includes(columnName)) { - sortedColumnContent = sortColumnWithPercentageValues(columnContent, order); - } - - if (columnName === 'cost') { - sortedColumnContent = sortCostColumn(columnContent, order); + switch (sortingOption) { + case StakePoolListColumn.Ticker: + sortedColumnContent = sortTickerColumnContent(columnContent, order); + break; + case StakePoolListColumn.Saturation: + case StakePoolListColumn.ROS: + case StakePoolListColumn.Margin: + sortedColumnContent = sortColumnWithPercentageValues(columnContent, order); + break; + case StakePoolListColumn.Blocks: + sortedColumnContent = sortBlocksColumnContent(columnContent, order); + break; + case StakePoolListColumn.Cost: + case StakePoolListColumn.Pledge: + case StakePoolListColumn.LiveStake: + sortedColumnContent = sortColumnWithAbbreviatedNumbers(columnContent, order); + break; + default: + throw new Error(`Unsupported sorting option: ${sortingOption}`); } return sortedColumnContent; }; + +export const mapColumnNameStringToEnum = (columnName: StakePoolListColumnName): StakePoolListColumn => + columnName === 'Live Stake' + ? StakePoolListColumn.LiveStake + : StakePoolListColumn[columnName as keyof typeof StakePoolListColumn]; From f32d2a1ee3b28284c10661929693cbf0808a0629 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Fri, 19 Apr 2024 14:09:49 +0100 Subject: [PATCH 3/6] fix(staking): [LW-10282] staking storybook build (#1072) --- .../BrowsePoolsPreferencesCard.stories.tsx | 3 ++- packages/staking/src/features/BrowsePools/constants.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx index 18576f0825..b87bdfd6a1 100644 --- a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx +++ b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx @@ -2,11 +2,12 @@ import { Box, Cell, Flex, Grid, LocalThemeProvider, Section, ThemeColorScheme, V import { action } from '@storybook/addon-actions'; import { useArgs } from '@storybook/preview-api'; import { expect, userEvent, waitFor, within } from '@storybook/test'; -import { DEFAULT_SORT_OPTIONS, StakePoolSortOptions } from 'features/BrowsePools'; import { useCallback, useState } from 'react'; +import type { StakePoolSortOptions } from '../types'; import type { Meta, StoryObj } from '@storybook/react'; import { PoolsFilter, QueryStakePoolsFilters } from '../../store'; +import { DEFAULT_SORT_OPTIONS } from '../constants'; import { BrowsePoolsPreferencesCard } from './BrowsePoolsPreferencesCard'; import { SortAndFilterTab } from './types'; diff --git a/packages/staking/src/features/BrowsePools/constants.ts b/packages/staking/src/features/BrowsePools/constants.ts index fca7546d3b..bae32b93ee 100644 --- a/packages/staking/src/features/BrowsePools/constants.ts +++ b/packages/staking/src/features/BrowsePools/constants.ts @@ -1,4 +1,5 @@ -import { BrowsePoolsView, StakePoolSortOptions, StakingBrowserPreferences } from './types'; +import type { StakePoolSortOptions, StakingBrowserPreferences } from './types'; +import { BrowsePoolsView } from './types'; import { getDefaultSortOrderByField } from './utils'; export const SEARCH_DEBOUNCE_IN_MS = 300; From 2bb331d04496a8b9ccd6475729dd2997a599806e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Fri, 19 Apr 2024 15:18:35 +0200 Subject: [PATCH 4/6] test(extension): update tests for HW wallet (#1078) --- .../src/ui/components/Loader/Loader.tsx | 2 +- ...etSetupConnectHardwareWalletStepRevamp.tsx | 7 ++- .../onboarding/ConnectYourDevicePageAssert.ts | 42 +++++++++++++++++ .../onboardingConnectHWPageAssert.ts | 47 ------------------- .../onboarding/ConnectYourDevicePage.ts | 27 +++++++++++ .../onboarding/connectHardwareWalletPage.ts | 34 -------------- .../features/OnboardingHardwareWallet.feature | 25 +++++----- .../src/features/trezor/Trezor.feature | 11 +++-- .../e2e-tests/src/steps/onboardingSteps.ts | 18 ++++--- 9 files changed, 106 insertions(+), 107 deletions(-) create mode 100644 packages/e2e-tests/src/assert/onboarding/ConnectYourDevicePageAssert.ts delete mode 100644 packages/e2e-tests/src/assert/onboarding/onboardingConnectHWPageAssert.ts create mode 100644 packages/e2e-tests/src/elements/onboarding/ConnectYourDevicePage.ts delete mode 100644 packages/e2e-tests/src/elements/onboarding/connectHardwareWalletPage.ts diff --git a/packages/common/src/ui/components/Loader/Loader.tsx b/packages/common/src/ui/components/Loader/Loader.tsx index 9aa1930eed..036b3e0796 100644 --- a/packages/common/src/ui/components/Loader/Loader.tsx +++ b/packages/common/src/ui/components/Loader/Loader.tsx @@ -8,5 +8,5 @@ export interface LoaderProps { } export const Loader = ({ className }: LoaderProps): React.ReactElement => ( - + ); diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx index 1e645e54f7..c1be02422b 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx @@ -36,7 +36,12 @@ export const WalletSetupConnectHardwareWalletStepRevamp = ({ )} {state === 'error' && ( <> - hardware wallet connection error image + hardware wallet connection error image )} diff --git a/packages/e2e-tests/src/assert/onboarding/ConnectYourDevicePageAssert.ts b/packages/e2e-tests/src/assert/onboarding/ConnectYourDevicePageAssert.ts new file mode 100644 index 0000000000..5e5b761e7f --- /dev/null +++ b/packages/e2e-tests/src/assert/onboarding/ConnectYourDevicePageAssert.ts @@ -0,0 +1,42 @@ +import ConnectYourDevicePage from '../../elements/onboarding/ConnectYourDevicePage'; +import { t } from '../../utils/translationService'; +import { expect } from 'chai'; +import OnboardingCommonAssert from './onboardingCommonAssert'; + +class ConnectYourDevicePageAssert extends OnboardingCommonAssert { + async assertSeeConnectYourDevicePage() { + await this.assertSeeStepTitle(await t('core.walletSetupConnectHardwareWalletStepRevamp.title')); + // TODO: replace subtitle assertions when USE_TREZOR_HW=true + // await this.assertSeeStepSubtitle(await t('core.walletSetupConnectHardwareWalletStepRevamp.subTitle')); + await this.assertSeeStepSubtitle(await t('core.walletSetupConnectHardwareWalletStepRevamp.subTitleLedgerOnly')); + + await ConnectYourDevicePage.loader.waitForDisplayed(); + + await this.assertSeeBackButton(); + await this.assertSeeTryAgainButton(false); + + await this.assertSeeLegalLinks(); + await this.assertSeeHelpAndSupportButton(); + } + + async assertSeeError(expectedErrorMessage: string) { + await ConnectYourDevicePage.errorImage.waitForDisplayed(); + await ConnectYourDevicePage.banner.container.waitForDisplayed(); + expect(await ConnectYourDevicePage.banner.description.getText()).to.equal(expectedErrorMessage); + } + + async assertSeeTryAgainButton(shouldBeVisible: boolean) { + await ConnectYourDevicePage.tryAgainButton.waitForDisplayed({ reverse: !shouldBeVisible }); + if (shouldBeVisible) { + expect(await ConnectYourDevicePage.tryAgainButton.getText()).to.equal( + await t('core.walletSetupConnectHardwareWalletStepRevamp.errorCta') + ); + } + } + + async assertSeeTryAgainButtonEnabled(shouldBeEnabled: boolean) { + await ConnectYourDevicePage.tryAgainButton.waitForEnabled({ reverse: !shouldBeEnabled }); + } +} + +export default new ConnectYourDevicePageAssert(); diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingConnectHWPageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingConnectHWPageAssert.ts deleted file mode 100644 index db1f3b31d6..0000000000 --- a/packages/e2e-tests/src/assert/onboarding/onboardingConnectHWPageAssert.ts +++ /dev/null @@ -1,47 +0,0 @@ -import OnboardingConnectHardwareWalletPage from '../../elements/onboarding/connectHardwareWalletPage'; -import { t } from '../../utils/translationService'; -import { expect } from 'chai'; -import OnboardingCommonAssert from './onboardingCommonAssert'; - -class OnboardingConnectHardwareWalletPageAssert extends OnboardingCommonAssert { - async assertSeeConnectHardwareWalletPageSubTitle() { - await OnboardingConnectHardwareWalletPage.subTitle.waitForDisplayed(); - expect(await OnboardingConnectHardwareWalletPage.subTitle.getText()).to.equal( - await t('core.walletSetupConnectHardwareWalletStep.subTitle') - ); - } - - async assertSeeSupportedDevicesText() { - await OnboardingConnectHardwareWalletPage.supportedDevices.waitForDisplayed(); - expect(await OnboardingConnectHardwareWalletPage.supportedDevices.getText()).to.equal( - await t('core.walletSetupConnectHardwareWalletStep.supportedDevices') - ); - } - - async assertSeeLedgerButtonDisplayed() { - await OnboardingConnectHardwareWalletPage.ledgerButton.waitForDisplayed(); - } - - async assertSeeConnectDeviceText() { - await OnboardingConnectHardwareWalletPage.connectDevice.waitForDisplayed(); - expect(await OnboardingConnectHardwareWalletPage.connectDevice.getText()).to.equal( - await t('core.walletSetupConnectHardwareWalletStep.connectDevice') - ); - } - - async assertSeeConnectHardwareWalletPage() { - await this.assertSeeStepTitle(await t('core.walletSetupConnectHardwareWalletStep.title')); - await this.assertSeeConnectHardwareWalletPageSubTitle(); - await this.assertSeeSupportedDevicesText(); - await this.assertSeeLedgerButtonDisplayed(); - await this.assertSeeConnectDeviceText(); - - await this.assertSeeBackButton(); - await this.assertSeeNextButton(); - - await this.assertSeeLegalLinks(); - await this.assertSeeHelpAndSupportButton(); - } -} - -export default new OnboardingConnectHardwareWalletPageAssert(); diff --git a/packages/e2e-tests/src/elements/onboarding/ConnectYourDevicePage.ts b/packages/e2e-tests/src/elements/onboarding/ConnectYourDevicePage.ts new file mode 100644 index 0000000000..6649de9d58 --- /dev/null +++ b/packages/e2e-tests/src/elements/onboarding/ConnectYourDevicePage.ts @@ -0,0 +1,27 @@ +/* eslint-disable no-undef*/ +import CommonOnboardingElements from './commonOnboardingElements'; +import { ChainablePromiseElement } from 'webdriverio'; +import Banner from '../banner'; + +export class ConnectYourDevicePage extends CommonOnboardingElements { + private LOADER_IMAGE = '[data-testid="loader-image"]'; + private ERROR_IMAGE = '[data-testid="error-image"]'; + + get loader(): ChainablePromiseElement { + return $(this.LOADER_IMAGE); + } + + get errorImage(): ChainablePromiseElement { + return $(this.ERROR_IMAGE); + } + + get tryAgainButton(): ChainablePromiseElement { + return this.nextButton; + } + + get banner(): typeof Banner { + return Banner; + } +} + +export default new ConnectYourDevicePage(); diff --git a/packages/e2e-tests/src/elements/onboarding/connectHardwareWalletPage.ts b/packages/e2e-tests/src/elements/onboarding/connectHardwareWalletPage.ts deleted file mode 100644 index bf702eb087..0000000000 --- a/packages/e2e-tests/src/elements/onboarding/connectHardwareWalletPage.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* eslint-disable no-undef*/ -import CommonOnboardingElements from './commonOnboardingElements'; -import { ChainablePromiseElement } from 'webdriverio'; - -export class OnboardingConnectHardwareWalletPage extends CommonOnboardingElements { - private SUBTITLE_TEXT = '[data-testid="connect-hardware-wallet-subtitle"]'; - private SUPPORTED_DEVICES_TEXT = '[data-testid="connect-hardware-wallet-supported-devices-text"]'; - private LEDGER_BUTTON = '[data-testid="connect-hardware-wallet-button-ledger"]'; - private TREZOR_BUTTON = '[data-testid="connect-hardware-wallet-button-trezor"]'; - - private CONNECT_DEVICE_TEXT = '[data-testid="connect-hardware-wallet-connect-device-text"]'; - - get subTitle(): ChainablePromiseElement { - return $(this.SUBTITLE_TEXT); - } - - get supportedDevices(): ChainablePromiseElement { - return $(this.SUPPORTED_DEVICES_TEXT); - } - - get ledgerButton(): ChainablePromiseElement { - return $(this.LEDGER_BUTTON); - } - - get trezorButton(): ChainablePromiseElement { - return $(this.TREZOR_BUTTON); - } - - get connectDevice(): ChainablePromiseElement { - return $(this.CONNECT_DEVICE_TEXT); - } -} - -export default new OnboardingConnectHardwareWalletPage(); diff --git a/packages/e2e-tests/src/features/OnboardingHardwareWallet.feature b/packages/e2e-tests/src/features/OnboardingHardwareWallet.feature index 4ab90655eb..0a5b85a7cf 100755 --- a/packages/e2e-tests/src/features/OnboardingHardwareWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingHardwareWallet.feature @@ -4,20 +4,12 @@ Feature: Onboarding - Hardware wallet @LW-3367 Scenario: Hardware Wallet - Connect button click When I click "Connect" button on wallet setup page - And I click "OK" button on "Limited support for DApp" modal - Then "Connect Hardware Wallet" page is displayed - - @LW-3368 - Scenario: Hardware wallet - Legal page - next button disabled - When I click "Connect" button on wallet setup page - And I click "OK" button on "Limited support for DApp" modal - And "Next" button is disabled during onboarding process + Then "Connect your device" page is displayed @LW-3374 - Scenario: Hardware wallet - Connect Hardware Wallet - back button click + Scenario: Hardware wallet - Connect your device - back button click Given I click "Connect" button on wallet setup page - And I click "OK" button on "Limited support for DApp" modal - And "Connect Hardware Wallet" page is displayed + And "Connect your device" page is displayed When I click "Back" button during wallet setup Then "Get started" page is displayed @@ -27,10 +19,17 @@ Feature: Onboarding - Hardware wallet When "Get started" page is displayed Then I see current onboarding page in mode And I click "Connect" button on wallet setup page - And I click "OK" button on "Limited support for DApp" modal - And "Connect Hardware Wallet" page is displayed + And "Connect your device" page is displayed Then I see current onboarding page in mode Examples: | mode | | dark | | light | + + @LW-10309 + Scenario: Hardware wallet - Connect your device - "No hardware wallet device was chosen." error + When I click "Connect" button on wallet setup page + # Step below triggers error by closing HID window + And I switch to window with Lace + Then "No hardware wallet device was chosen." error is displayed on "Connect your device" page + And "Try again" button is enabled on "Connect your device" page diff --git a/packages/e2e-tests/src/features/trezor/Trezor.feature b/packages/e2e-tests/src/features/trezor/Trezor.feature index b6d5c27513..66ad86a781 100644 --- a/packages/e2e-tests/src/features/trezor/Trezor.feature +++ b/packages/e2e-tests/src/features/trezor/Trezor.feature @@ -4,11 +4,12 @@ Feature: Trezor Onboarding Scenario: Onboarding Trezor wallet And I connect, unlock and enter correct pin on Trezor emulator Given I click "Connect" button on wallet setup page - And I click "OK" button on "Limited support for DApp" modal - And I am on "Lace terms of use" page and accept terms - And I am on "Help us improve your experience" page + # TODO: remove/replace outdated steps +# And I click "OK" button on "Limited support for DApp" modal +# And I am on "Lace terms of use" page and accept terms +# And I am on "Help us improve your experience" page When I click "Agree" button on Analytics page - And I click Trezor wallet icon +# And I click Trezor wallet icon And I click "Next" button during wallet setup And I select 1 account on Select Account page When I click "Next" button during wallet setup @@ -19,6 +20,6 @@ Feature: Trezor Onboarding And I click "Export" on Trezor Connect page And I confirm exporting public key on Trezor emulator And I switch to window with Lace - Then "All done" page is displayed +# Then "All done" page is displayed When I click "Go to my wallet" button on "All done" page Then I see LW homepage diff --git a/packages/e2e-tests/src/steps/onboardingSteps.ts b/packages/e2e-tests/src/steps/onboardingSteps.ts index 628672b681..6c7ebfd82f 100644 --- a/packages/e2e-tests/src/steps/onboardingSteps.ts +++ b/packages/e2e-tests/src/steps/onboardingSteps.ts @@ -8,7 +8,6 @@ import Modal from '../elements/modal'; import ModalAssert from '../assert/modalAssert'; import OnboardingAnalyticsPage from '../elements/onboarding/analyticsPage'; import OnboardingCommonAssert from '../assert/onboarding/onboardingCommonAssert'; -import OnboardingConnectHWPageAssert from '../assert/onboarding/onboardingConnectHWPageAssert'; import OnboardingMainPage from '../elements/onboarding/mainPage'; import OnboardingMainPageAssert from '../assert/onboarding/onboardingMainPageAssert'; import OnboardingWalletSetupPage from '../elements/onboarding/walletSetupPage'; @@ -17,7 +16,6 @@ import TokensPageAssert from '../assert/tokensPageAssert'; import TopNavigationAssert from '../assert/topNavigationAssert'; import testContext from '../utils/testContext'; import CommonAssert from '../assert/commonAssert'; -import OnboardingConnectHardwareWalletPage from '../elements/onboarding/connectHardwareWalletPage'; import SelectAccountPage from '../elements/onboarding/selectAccountPage'; import { browser } from '@wdio/globals'; import type { RecoveryPhrase } from '../types/onboarding'; @@ -31,6 +29,7 @@ import { getWalletsFromRepository } from '../fixture/walletRepositoryInitializer import OnboardingWalletSetupPageAssert from '../assert/onboarding/onboardingWalletSetupPageAssert'; import OnboardingAnalyticsBannerAssert from '../assert/onboarding/onboardingAnalyticsBannerAssert'; import { shuffle } from '../utils/arrayUtils'; +import ConnectYourDevicePageAssert from '../assert/onboarding/ConnectYourDevicePageAssert'; const mnemonicWords: string[] = getTestWallet(TestWalletName.TestAutomationWallet).mnemonic ?? []; const invalidMnemonicWords: string[] = getTestWallet(TestWalletName.InvalidMnemonic).mnemonic ?? []; @@ -160,12 +159,19 @@ Then(/^I select (12|15|24) word passphrase length$/, async (length: RecoveryPhra await RecoveryPhrasePage.selectMnemonicLength(length); }); -Then(/^"Connect Hardware Wallet" page is displayed$/, async () => { - await OnboardingConnectHWPageAssert.assertSeeConnectHardwareWalletPage(); +Then(/^"Connect your device" page is displayed$/, async () => { + await ConnectYourDevicePageAssert.assertSeeConnectYourDevicePage(); }); -Then(/^I click Trezor wallet icon$/, async () => { - await OnboardingConnectHardwareWalletPage.trezorButton.click(); +Then(/^"No hardware wallet device was chosen." error is displayed on "Connect your device" page$/, async () => { + await ConnectYourDevicePageAssert.assertSeeError( + await t('core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.devicePickerRejected') + ); +}); + +When(/^"Try again" button is enabled on "Connect your device" page$/, async () => { + await ConnectYourDevicePageAssert.assertSeeTryAgainButton(true); + await ConnectYourDevicePageAssert.assertSeeTryAgainButtonEnabled(true); }); Then(/^"Restoring a multi-address wallet\?" modal is displayed$/, async () => { From 3e70afd25dabbc77d1130625f92b608da0fc17a2 Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Fri, 19 Apr 2024 15:51:53 +0200 Subject: [PATCH 5/6] chore(deps): update axios and cardano sdk packages (#1069) * chore: update cardano js sdk packages * chore: update axios v0.28.0 --- apps/browser-extension-wallet/package.json | 12 +- .../ConfirmDRepRetirementContainer.test.tsx | 4 +- .../__tests__/hooks.test.tsx | 4 +- .../components/confirm-transaction/hooks.ts | 2 +- .../src/hooks/useWalletState.ts | 5 + packages/cardano/package.json | 10 +- packages/core/package.json | 2 +- packages/staking/package.json | 8 +- yarn.lock | 150 ++++++++++-------- 9 files changed, 114 insertions(+), 83 deletions(-) diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 4935b1ce56..d9fafa0543 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -39,14 +39,14 @@ }, "dependencies": { "@ant-design/icons": "^4.7.0", - "@cardano-sdk/cardano-services-client": "0.18.0", + "@cardano-sdk/cardano-services-client": "0.19.0", "@cardano-sdk/core": "0.30.0", "@cardano-sdk/dapp-connector": "0.12.14", - "@cardano-sdk/input-selection": "0.12.26", - "@cardano-sdk/tx-construction": "0.18.2", + "@cardano-sdk/input-selection": "0.12.27", + "@cardano-sdk/tx-construction": "0.18.3", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.36.0", - "@cardano-sdk/web-extension": "0.26.2", + "@cardano-sdk/wallet": "0.37.0", + "@cardano-sdk/web-extension": "0.27.0", "@emurgo/cip14-js": "~3.0.1", "@koralabs/handles-public-api-interfaces": "^1.6.6", "@lace/cardano": "0.1.0", @@ -59,7 +59,7 @@ "@vespaiach/axios-fetch-adapter": "^0.3.0", "antd": "^4.24.10", "are-you-es5": "^2.1.2", - "axios": "0.21.4", + "axios": "0.28.0", "bignumber.js": "9.0.1", "bip39": "^3.0.4", "blake2b-no-wasm": "2.1.4", diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx index bf927ebba8..ac55acc547 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmDRepRetirementContainer.test.tsx @@ -33,7 +33,9 @@ const hash = Crypto.Hash28ByteBase16(Buffer.from('dRepCredentialHashdRepCreden') const getPubDRepKey = async () => await hash; const inMemoryWallet = { - getPubDRepKey, + governance: { + getPubDRepKey + }, assetInfo$, balance: { utxo: { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx index 56f0beb427..0ce143d7e1 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/hooks.test.tsx @@ -307,7 +307,9 @@ describe('Testing hooks', () => { mockUseWalletStore.mockReset(); mockUseWalletStore.mockReturnValue({ inMemoryWallet: { - getPubDRepKey: jest.fn(async () => await ed25519PublicKeyHexMock) + governance: { + getPubDRepKey: jest.fn(async () => await ed25519PublicKeyHexMock) + } } }); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts index 99785804cc..7e40ce0653 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/hooks.ts @@ -205,7 +205,7 @@ export const useGetOwnPubDRepKeyHash = (): UseGetOwnPubDRepKeyHash => { useEffect(() => { if (!inMemoryWallet) return; const get = async () => { - const ownPubDRepKey = await inMemoryWallet.getPubDRepKey(); + const ownPubDRepKey = await inMemoryWallet.governance.getPubDRepKey(); const ownDRepKeyHash = await pubDRepKeyToHash(ownPubDRepKey); setOwnPubDRepKeyHash(ownDRepKeyHash); diff --git a/apps/browser-extension-wallet/src/hooks/useWalletState.ts b/apps/browser-extension-wallet/src/hooks/useWalletState.ts index e4a6aaac5d..ea9f95dd58 100644 --- a/apps/browser-extension-wallet/src/hooks/useWalletState.ts +++ b/apps/browser-extension-wallet/src/hooks/useWalletState.ts @@ -52,6 +52,7 @@ const combineObservable = ({ wallet }: Wallet.CardanoWallet): Observable Date: Fri, 19 Apr 2024 16:21:26 +0200 Subject: [PATCH 6/6] fix(extension): LW-10206 fix trezor security vulnerabilities (#1023) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(extension): fix trezor security vulnerabilities * fix(extension): add missing types --------- Co-authored-by: Szymon Masłowski --- .../src/lib/scripts/trezor/trezor-content-script.ts | 2 ++ .../src/lib/scripts/trezor/trezor-usb-permissions.ts | 11 +++++------ .../src/lib/scripts/trezor/types.ts | 4 ++++ 3 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 apps/browser-extension-wallet/src/lib/scripts/trezor/types.ts diff --git a/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-content-script.ts b/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-content-script.ts index 523adc068f..54ce65a6cf 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-content-script.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-content-script.ts @@ -1,4 +1,5 @@ import { runtime } from 'webextension-polyfill'; +import { AllowedOrigins } from './types'; // Communicate from background script to popup let port = runtime.connect({ name: 'trezor-connect' }); @@ -12,6 +13,7 @@ port.onDisconnect.addListener(() => { // communicate from popup to background script window.addEventListener('message', (event) => { + if (event.origin !== AllowedOrigins.TREZOR_CONNECT) throw new Error('Origin not allowed'); if (port && event.source === window && event.data) { port.postMessage({ data: event.data }); } diff --git a/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-usb-permissions.ts b/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-usb-permissions.ts index 4fbfff55ee..06423a09fa 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-usb-permissions.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/trezor/trezor-usb-permissions.ts @@ -1,8 +1,5 @@ import { runtime, tabs } from 'webextension-polyfill'; - -// Handling messages from usb permissions iframe - -const url = 'https://connect.trezor.io/8/'; +import { AllowedOrigins } from './types'; /* Handling messages from usb permissions iframe */ const switchToPopupTab = async (event?: BeforeUnloadEvent) => { @@ -21,13 +18,15 @@ const switchToPopupTab = async (event?: BeforeUnloadEvent) => { // find tab by popup pattern and switch to it const currentTabs = await tabs.query({ - url: `${url}popup.html` + url: `${AllowedOrigins.TREZOR_CONNECT_POPUP_BASE_URL}/popup.html` }); if (currentTabs.length < 0) return; tabs.update(currentTabs[0].id, { active: true }); }; window.addEventListener('message', async (event) => { + if (event.origin !== AllowedOrigins.TREZOR_CONNECT) throw new Error('Origin not allowed'); + if (event.data === 'usb-permissions-init') { const iframe = document.querySelector('#trezor-usb-permissions'); if (!iframe || !(iframe instanceof HTMLIFrameElement)) { @@ -55,7 +54,7 @@ window.addEventListener('load', () => { instance.style.border = '0px'; instance.style.width = '100%'; instance.style.height = '100%'; - instance.setAttribute('src', `${url}extension-permissions.html`); + instance.setAttribute('src', `${AllowedOrigins.TREZOR_CONNECT_POPUP_BASE_URL}/extension-permissions.html`); instance.setAttribute('allow', 'usb'); if (document.body) { diff --git a/apps/browser-extension-wallet/src/lib/scripts/trezor/types.ts b/apps/browser-extension-wallet/src/lib/scripts/trezor/types.ts new file mode 100644 index 0000000000..e4690c2263 --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/trezor/types.ts @@ -0,0 +1,4 @@ +export enum AllowedOrigins { + TREZOR_CONNECT = 'https://connect.trezor.io', + TREZOR_CONNECT_POPUP_BASE_URL = 'https://connect.trezor.io/8' +}