From d05f524a7a03014f082fb6d33886b44dbb4a71dc Mon Sep 17 00:00:00 2001 From: vetalcore Date: Tue, 26 Mar 2024 10:30:23 +0200 Subject: [PATCH 01/74] =?UTF-8?q?fix(extension):=20load=20more=20stake=20p?= =?UTF-8?q?ool=20grid=20items=20if=20all=20stake=20pools=20ar=E2=80=A6=20(?= =?UTF-8?q?#983)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(extension): load more stake pool grid items if all stake pools are unselected * fix(extension): resolve sdet comments --- .../BrowsePools/StakePoolsGrid/Grid.tsx | 29 ++++++++++++++++++- .../StakePoolsGrid/StakePoolsGrid.tsx | 1 + 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/packages/staking/src/features/BrowsePools/StakePoolsGrid/Grid.tsx b/packages/staking/src/features/BrowsePools/StakePoolsGrid/Grid.tsx index 9e7b4a1c4..3440af41b 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsGrid/Grid.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsGrid/Grid.tsx @@ -1,7 +1,9 @@ /* eslint-disable react/no-multi-comp */ import { Flex, useVisibleItemsCount } from '@lace/ui'; -import { useEffect, useLayoutEffect, useRef } from 'react'; +import debounce from 'lodash/debounce'; +import { RefObject, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { ListRange, VirtuosoGrid, VirtuosoGridProps } from 'react-virtuoso'; +import useResizeObserver from 'use-resize-observer'; import * as cx from './StakePoolsGrid.css'; export type GridProps = VirtuosoGridProps & { @@ -11,8 +13,11 @@ export type GridProps = VirtuosoGridProps & { rowHeight: number; numberOfItemsPerRow?: number; scrollableTargetId?: string; + parentRef: RefObject; }; +const DEFAULT_DEBOUNCE = 200; + export const Grid = | undefined>({ itemContent, items, @@ -20,9 +25,11 @@ export const Grid = | undefined>({ rowHeight, numberOfItemsPerRow, scrollableTargetId = '', + parentRef, }: GridProps) => { const tableReference = useRef(null); const scrollableTargetReference = useRef['customScrollParent']>(); + const [overscan, setOverscan] = useState({ main: 0, reverse: 0 }); useLayoutEffect(() => { if (scrollableTargetId) { @@ -41,10 +48,30 @@ export const Grid = | undefined>({ } }, [initialRowsCount, loadMoreData, numberOfItemsPerRow, rowHeight]); + const updateGridOverscan = useCallback(() => { + if (!tableReference?.current || !parentRef?.current) return; + // VirtuosoGrid won't render more items in case it's top position (visible items count) changes, force it manually via overscan value + const tableVsParentHeightDiff = + parentRef.current.getBoundingClientRect().height - tableReference.current.getBoundingClientRect().height; + setOverscan({ main: tableVsParentHeightDiff, reverse: 0 }); + }, [parentRef]); + + useLayoutEffect(() => { + updateGridOverscan(); + }, [updateGridOverscan]); + + const onResize = useMemo( + () => debounce(updateGridOverscan, DEFAULT_DEBOUNCE, { leading: true }), + [updateGridOverscan] + ); + + useResizeObserver({ onResize, ref: parentRef }); + return ( {items.length > 0 && scrollableTargetReference.current && ( + overscan={overscan} listClassName={cx.grid} data={items} customScrollParent={scrollableTargetReference.current} diff --git a/packages/staking/src/features/BrowsePools/StakePoolsGrid/StakePoolsGrid.tsx b/packages/staking/src/features/BrowsePools/StakePoolsGrid/StakePoolsGrid.tsx index f15cb9f76..446d3a7eb 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsGrid/StakePoolsGrid.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsGrid/StakePoolsGrid.tsx @@ -104,6 +104,7 @@ export const StakePoolsGrid = ({ ) : ( + parentRef={ref} rowHeight={STAKE_POOL_CARD_HEIGHT} numberOfItemsPerRow={numberOfItemsPerRow} scrollableTargetId={scrollableTargetId} From 66850b373a203142f3b10859431b574edd19e269 Mon Sep 17 00:00:00 2001 From: Tom Mayel <143514860+tommayeliog@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:47:49 +0000 Subject: [PATCH 02/74] fix(extension,staking): fix right side bottom overlapping [LW-10067] (#984) * fix(extension,staking): fix right side bottom overlapping --- .../features/staking/components/StakingSkeleton.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx index 0c385155e..2b41c48b3 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx @@ -71,7 +71,7 @@ export const StakingSkeleton = ({ children }: PropsWithChildren): React. ]; const sidePanel = ( - + From 007b6fe680975c9ef038585c48816e34a45b909d Mon Sep 17 00:00:00 2001 From: Shawn Date: Wed, 27 Mar 2024 08:01:19 +0000 Subject: [PATCH 03/74] feat: forgot password analytics (#973) --- .../AnalyticsProvider/analyticsTracker/events.ts | 13 ++++++------- .../AnalyticsProvider/analyticsTracker/types.ts | 9 +++------ .../wallet-setup/components/WalletSetupWizard.tsx | 12 +++++++++--- packages/common/src/analytics/types.ts | 14 +++++++------- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts index 1f0a21cb2..56053b8ce 100644 --- a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts +++ b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts @@ -11,8 +11,8 @@ export const postHogOnboardingActions: PostHogOnboardingActionsType = { create: { SETUP_OPTION_CLICK: PostHogAction.OnboardingCreateClick, SAVE_RECOVERY_PHRASE_NEXT_CLICK: PostHogAction.OnboardingCreateSaveRecoveryPhraseNextClick, + ENTER_RECOVERY_PHRASE_NEXT_CLICK: PostHogAction.OnboardingCreateEnterRecoveryPhraseNextClick, ENTER_WALLET: PostHogAction.OnboardingCreateEnterWalletClick, - WALLET_NAME_PASSWORD_NEXT_CLICK: PostHogAction.OnboardingCreateWalletNamePasswordNextClick, RECOVERY_PHRASE_INTRO_WATCH_VIDEO_CLICK: PostHogAction.OnboardingCreateSaveRecoveryPhraseIntroPlayVideoClick, RECOVERY_PHRASE_INTRO_VIDEO_GOTIT_CLICK: PostHogAction.OnboardingCreateKeepWalletSecureGotItClick, RECOVERY_PHRASE_COPY_TO_CLIPBOARD_CLICK: PostHogAction.OnboardingCreateSaveRecoveryPhraseCopyToClipboardClick, @@ -21,7 +21,7 @@ export const postHogOnboardingActions: PostHogOnboardingActionsType = { restore: { SETUP_OPTION_CLICK: PostHogAction.OnboardingRestoreClick, ENTER_WALLET: PostHogAction.OnboardingRestoreEnterWalletClick, - WALLET_NAME_PASSWORD_NEXT_CLICK: PostHogAction.OnboardingRestoreWalletNamePasswordNextClick, + ENTER_RECOVERY_PHRASE_NEXT_CLICK: PostHogAction.OnboardingRestoreEnterRecoveryPhraseNextClick, RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK: PostHogAction.OnboardingRestoreEnterRecoveryPhrasePasteFromClipboardClick }, @@ -34,10 +34,9 @@ export const postHogOnboardingActions: PostHogOnboardingActionsType = { }, // eslint-disable-next-line camelcase forgot_password: { - WALLET_PASSWORD_NEXT_CLICK: PostHogAction.UnlockWalletForgotPasswordNextClick, - RECOVERY_PASSPHRASE_LENGTH_NEXT_CLICK: PostHogAction.UnlockWalletForgotPasswordRecoveryPhraseLengthNextClick, - ENTER_PASSPHRASE_01_NEXT_CLICK: PostHogAction.UnlockWalletForgotPasswordEnterPassphrase01NextClick, - ENTER_PASSPHRASE_09_NEXT_CLICK: PostHogAction.UnlockWalletForgotPasswordEnterPassphrase09NextClick, - ENTER_PASSPHRASE_17_NEXT_CLICK: PostHogAction.UnlockWalletForgotPasswordEnterPassphrase17NextClick + ENTER_RECOVERY_PHRASE_NEXT_CLICK: PostHogAction.UnlockWalletForgotPasswordRecoveryPhraseNextClick, + ENTER_WALLET: PostHogAction.UnlockWalletForgotPasswordEnterWalletClick, + RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK: + PostHogAction.UnlockWalletForgotPasswordRecoveryPhrasePasteFromClipboardClick } }; diff --git a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts index 270222256..1e9f72dc9 100644 --- a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts +++ b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts @@ -45,15 +45,11 @@ export type PostHogActionsKeys = | 'LEARN_MORE_CLICK' | 'ANALYTICS_REJECT_CLICK' | 'WALLET_NAME_NEXT_CLICK' - | 'WALLET_PASSWORD_NEXT_CLICK' | 'SAVE_RECOVERY_PHRASE_NEXT_CLICK' + | 'ENTER_RECOVERY_PHRASE_NEXT_CLICK' | 'ENTER_WALLET' | 'GOT_IT_CLICK' | 'PIN_EXTENSION_CLICK' - | 'ENTER_PASSPHRASE_01_NEXT_CLICK' - | 'ENTER_PASSPHRASE_09_NEXT_CLICK' - | 'ENTER_PASSPHRASE_17_NEXT_CLICK' - | 'RECOVERY_PASSPHRASE_LENGTH_NEXT_CLICK' | 'CONNECT_HW_NEXT_CLICK' | 'SETUP_HW_WALLET_NEXT_CLICK' | 'DONE_GO_TO_WALLET' @@ -61,7 +57,8 @@ export type PostHogActionsKeys = | 'RECOVERY_PHRASE_INTRO_WATCH_VIDEO_CLICK' | 'RECOVERY_PHRASE_INTRO_VIDEO_GOTIT_CLICK' | 'RECOVERY_PHRASE_COPY_TO_CLIPBOARD_CLICK' - | 'RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK'; + | 'RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK' + | 'RECOVERY_PASSPHRASE_VERIFICATION_NEXT_CLICK'; export type PostHogOnboardingActionsValueType = Partial>; export type PostHogOnboardingActionsType = Partial>; export type PostHogPersonProperties = { diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx index 11822d7e2..cc1d61aa1 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx @@ -213,9 +213,15 @@ export const WalletSetupWizard = ({ ); const handleSubmit = async (result: { password: string; walletName: string }) => { + sendAnalytics(postHogOnboardingActions[setupType]?.ENTER_WALLET); await handleCompleteCreation(result.walletName, result.password); }; + const handleMnemonicVerification = () => { + sendAnalytics(postHogOnboardingActions[setupType]?.ENTER_RECOVERY_PHRASE_NEXT_CLICK); + moveForward(); + }; + const renderedMnemonicStep = () => { if ([SetupType.RESTORE, SetupType.FORGOT_PASSWORD].includes(setupType)) { const isMnemonicSubmitEnabled = util.validateMnemonic(util.joinMnemonicWords(mnemonic)); @@ -224,14 +230,14 @@ export const WalletSetupWizard = ({ mnemonic={mnemonic} onChange={setMnemonic} onCancel={setupType !== SetupType.FORGOT_PASSWORD && moveBack} - onSubmit={moveForward} + onSubmit={handleMnemonicVerification} isSubmitEnabled={isMnemonicSubmitEnabled} translations={walletSetupMnemonicStepTranslations} suggestionList={wordList} defaultMnemonicLength={DEFAULT_MNEMONIC_LENGTH} onSetMnemonicLength={(value: number) => setMnemonicLength(value)} onPasteFromClipboard={() => - sendAnalytics(postHogOnboardingActions.restore.RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK) + sendAnalytics(postHogOnboardingActions[setupType]?.RECOVERY_PHRASE_PASTE_FROM_CLIPBOARD_CLICK) } /> ); @@ -259,7 +265,7 @@ export const WalletSetupWizard = ({ onStepNext={(mnemonicStage) => { mnemonicStage === 'writedown' ? sendAnalytics(postHogOnboardingActions.create.SAVE_RECOVERY_PHRASE_NEXT_CLICK) - : sendAnalytics(postHogOnboardingActions.create.ENTER_WALLET); + : sendAnalytics(postHogOnboardingActions.create.ENTER_RECOVERY_PHRASE_NEXT_CLICK); }} translations={walletSetupMnemonicStepTranslations} suggestionList={wordList} diff --git a/packages/common/src/analytics/types.ts b/packages/common/src/analytics/types.ts index 6305bc283..e903e7d1b 100644 --- a/packages/common/src/analytics/types.ts +++ b/packages/common/src/analytics/types.ts @@ -17,19 +17,19 @@ export enum PostHogAction { OnboardingHWDoneGoToWallet = 'onboarding | hardware wallet | all done | go to my wallet | click', // Restore wallet OnboardingRestoreClick = 'onboarding | restore wallet revamp | restore | click', - OnboardingRestoreWalletNamePasswordNextClick = "onboarding | restore wallet revamp | let's set up your new wallet | next | click", + OnboardingRestoreEnterRecoveryPhraseNextClick = 'onboarding | restore wallet revamp | enter your recovery phrase | next | click', OnboardingRestoreEnterRecoveryPhrasePasteFromClipboardClick = 'onboarding | restore wallet revamp | enter your recovery phrase | paste from clipboard | click', OnboardingRestoreHdWallet = 'onboarding | restore wallet | hd wallet', - OnboardingRestoreEnterWalletClick = 'onboarding | restore wallet revamp | enter your recovery phrase | enter wallet | click', + OnboardingRestoreEnterWalletClick = "onboarding | restore wallet revamp | let's set up your new wallet | enter wallet | click", // Create new wallet OnboardingCreateClick = 'onboarding | new wallet revamp | create | click', - OnboardingCreateWalletNamePasswordNextClick = "onboarding | new wallet revamp | let's set up your new wallet | next | click", OnboardingCreateSaveRecoveryPhraseNextClick = 'onboarding | new wallet revamp | save your recovery phrase | next | click', + OnboardingCreateEnterRecoveryPhraseNextClick = 'onboarding | new wallet revamp | enter your recovery phrase | next | click', OnboardingCreateSaveRecoveryPhraseIntroPlayVideoClick = 'onboarding | new wallet revamp | save your recovery phrase | watch video | click', OnboardingCreateSaveRecoveryPhraseCopyToClipboardClick = 'onboarding | new wallet revamp | save your recovery phrase | copy to clipboard | click', OnboardingCreateKeepWalletSecureGotItClick = 'onboarding | new wallet revamp | keeping your wallet secure | got it | click', OnboardingCreateEnterRecoveryPhrasePasteFromClipboardClick = 'onboarding | new wallet revamp | enter your recovery phrase | paste from clipboard | click', - OnboardingCreateEnterWalletClick = 'onboarding | new wallet revamp | enter your recovery phrase | enter wallet | click', + OnboardingCreateEnterWalletClick = "onboarding | new wallet revamp | let's set up your new wallet | enter wallet | click", // Multi wallet MultiWalletSwitchWallet = 'multiwallet | switch wallet | click', MultiWalletSwitchAccount = 'multiwallet | switch account | click', @@ -255,9 +255,9 @@ export enum PostHogAction { UnlockWalletForgotPasswordCancelClick = 'unlock wallet | forgot password? | cancel | click', UnlockWalletForgotPasswordNextClick = 'unlock wallet | forgot password? | set up your password | next | click', UnlockWalletForgotPasswordRecoveryPhraseLengthNextClick = 'unlock wallet | forgot password? | recovery phrase length | next | click', - UnlockWalletForgotPasswordEnterPassphrase01NextClick = 'unlock wallet | forgot password? | enter passphrase #01 | next | click', - UnlockWalletForgotPasswordEnterPassphrase09NextClick = 'unlock wallet | forgot password? | enter passphrase #09 | next | click', - UnlockWalletForgotPasswordEnterPassphrase17NextClick = 'unlock wallet | forgot password? | enter passphrase #17 | next | click' + UnlockWalletForgotPasswordRecoveryPhraseNextClick = 'unlock wallet | forgot password? | enter your recovery phrase | next | click', + UnlockWalletForgotPasswordEnterWalletClick = 'unlock wallet | forgot password? | set up your password | enter wallet | click', + UnlockWalletForgotPasswordRecoveryPhrasePasteFromClipboardClick = 'unlock wallet | forgot password? | enter your recovery phrase | paste from clipboard | click' } // eslint-disable-next-line @typescript-eslint/no-explicit-any From e8a9096cf2da863f8e75f0933ea9c709589ac0eb Mon Sep 17 00:00:00 2001 From: Vanessa Rodriguez Cristobal Date: Wed, 27 Mar 2024 09:18:33 +0000 Subject: [PATCH 04/74] feat: transaction representation refactor (#883) - transaction includes transaction fees - it displays returned deposit and deposits - it shows from address sections wiht tokens and nfts - it shows to address section with tokens and nfts - it displays tokens and nfts in the summary - it summarises the balance in coins of a transactions - it displays the dapp name in the origin tab --- .../components/Layout/MainLayout.module.scss | 1 + .../src/features/dapp/components/Layout.tsx | 2 +- .../ConfirmTransaction.module.scss | 17 +- .../ConfirmTransaction.tsx | 6 +- .../DappTransactionContainer.tsx | 384 ++++++++---------- .../__tests__/ConfirmTransaction.test.tsx | 1 - .../DappTransactionContainer.test.tsx | 220 ++++++---- .../src/hooks/index.ts | 1 + .../src/hooks/useChainHistoryProvider.ts | 17 + .../src/lib/translations/en.json | 11 +- .../src/utils/utxo-chain-history-resolver.ts | 23 ++ packages/common/src/ui/lib/add-ellipsis.ts | 7 + packages/common/src/ui/styles/theme.scss | 1 + .../common/src/ui/styles/themes/_light.scss | 1 + packages/core/.storybook/__mocks__/cardano.ts | 6 +- .../components/ActivityDetail/Collateral.tsx | 6 + .../ActivityDetail/TransactionDetails.tsx | 7 +- .../ActivityDetail/TransactionFee.tsx | 27 +- .../DappAddressSections.module.scss | 34 ++ .../DappAddressSections.tsx | 219 ++++++++++ .../DappTransaction.module.scss | 43 +- .../DappTransaction.stories.tsx | 56 ++- .../DappTransaction/DappTransaction.tsx | 218 +++++++--- .../DappTxAsset/DappTxAsset.module.scss | 41 -- .../DappTxAsset/DappTxAsset.tsx | 29 -- .../DappTransaction/DappTxAsset/index.ts | 1 - .../DappTxHeader/DappTxHeader.module.scss | 27 -- .../DappTxHeader/DappTxHeader.tsx | 20 - .../DappTxOutput/DappTxOutput.module.scss | 67 --- .../DappTxOutput/DappTxOutput.tsx | 42 -- .../DappTransactionHeader.module.scss | 14 + .../DappTransactionHeader.tsx | 43 ++ .../components/DappTransactionHeader/index.ts | 1 + packages/core/src/ui/lib/translations/en.json | 10 + .../src/assert/dAppConnectorAssert.ts | 72 +--- .../e2e-tests/src/assert/tokensPageAssert.ts | 12 +- .../dappConnector/confirmTransactionPage.ts | 111 +++-- .../newTransaction/transactionSummaryPage.ts | 3 +- .../analytics/AnalyticsSendExtended.feature | 2 +- .../e2e/SendTransactionDappE2E.feature | 6 +- .../src/pageobject/dAppConnectorPageObject.ts | 3 +- .../e2e-tests/src/steps/dAppConnectorSteps.ts | 18 +- packages/icons/raw/ada.component.svg | 11 + packages/icons/src/AdaComponent.tsx | 28 ++ .../src/assets/images/dark-mode-fallback.png | Bin 0 -> 3271 bytes .../src/assets/images/light-mode-fallback.png | Bin 0 -> 3107 bytes packages/ui/src/assets/images/token-1.png | Bin 0 -> 1554 bytes packages/ui/src/assets/images/token-2.png | Bin 0 -> 1208 bytes packages/ui/src/assets/images/token-3.png | Bin 0 -> 2033 bytes .../dapp-transaction-assets.component.tsx | 96 +++++ .../dapp-transaction-summary.component.tsx | 56 +++ .../dapp-transaction-summary.css.ts | 102 +++++ .../dapp-transaction-summary.stories.tsx | 131 ++++++ .../dapp-transaction-type.component.tsx | 57 +++ .../dapp-transaction-summary/index.ts | 3 + .../src/design-system/grid/grid.component.tsx | 4 +- .../ui/src/design-system/grid/grid.css.ts | 17 + packages/ui/src/design-system/index.ts | 4 + .../user-profile.component.tsx | 3 + .../profile-picture/user-profile.css.ts | 4 + .../design-system/summary-expander/index.ts | 1 + .../transaction-summary-amount.component.tsx | 99 +++-- .../transaction-summary.css.ts | 18 +- .../transaction-summary.stories.tsx | 11 +- packages/ui/src/design-tokens/colors.data.ts | 6 + .../src/design-tokens/theme/dark-theme.css.ts | 9 + .../theme/hooks/use-theme-variant.tsx | 11 + .../design-tokens/theme/light-theme.css.ts | 9 + 68 files changed, 1692 insertions(+), 818 deletions(-) create mode 100644 apps/browser-extension-wallet/src/hooks/useChainHistoryProvider.ts create mode 100644 apps/browser-extension-wallet/src/utils/utxo-chain-history-resolver.ts create mode 100644 packages/core/src/ui/components/DappAddressSections/DappAddressSections.module.scss create mode 100644 packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss delete mode 100644 packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx create mode 100644 packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.module.scss create mode 100644 packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx create mode 100644 packages/core/src/ui/components/DappTransactionHeader/index.ts create mode 100644 packages/icons/raw/ada.component.svg create mode 100644 packages/icons/src/AdaComponent.tsx create mode 100644 packages/ui/src/assets/images/dark-mode-fallback.png create mode 100644 packages/ui/src/assets/images/light-mode-fallback.png create mode 100644 packages/ui/src/assets/images/token-1.png create mode 100644 packages/ui/src/assets/images/token-2.png create mode 100644 packages/ui/src/assets/images/token-3.png create mode 100644 packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-assets.component.tsx create mode 100644 packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx create mode 100644 packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.css.ts create mode 100644 packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.stories.tsx create mode 100644 packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-type.component.tsx create mode 100644 packages/ui/src/design-system/dapp-transaction-summary/index.ts create mode 100644 packages/ui/src/design-system/summary-expander/index.ts create mode 100644 packages/ui/src/design-tokens/theme/hooks/use-theme-variant.tsx diff --git a/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss b/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss index 1d6d8f2d1..984549d0d 100644 --- a/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss +++ b/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss @@ -21,6 +21,7 @@ display: flex; min-height: 0; width: 100%; + flex-direction: column; > * { width: 100%; } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/Layout.tsx b/apps/browser-extension-wallet/src/features/dapp/components/Layout.tsx index 78b3e6b73..c89c6c1d8 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/Layout.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/Layout.tsx @@ -3,7 +3,7 @@ import React from 'react'; import styles from './Layout.module.scss'; type layoutProps = { - title: string | React.ReactElement; + title?: string | React.ReactElement; children?: React.ReactElement | React.ReactNode; layoutClassname?: string; pageClassname?: string; diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss index 3376b96ac..a884c336c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss @@ -3,23 +3,26 @@ .spaceBetween { justify-content: space-between; - padding-top: size_unit(2); + padding-top: size_unit(0); } -.layoutError { - padding: 0; +.transactionContainer { + display: flex; + flex-direction: column; + flex: 1; + justify-content: space-between; } .actions { @extend %flex-column; - background-color: var(--bg-color-body); - gap: size_unit(1); + height: size_unit(17.12); + justify-content: space-between; padding: size_unit(2) size_unit(3) size_unit(2) size_unit(3); - border-top: 2px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); - margin: size_unit(4) size_unit(-3) size_unit(-2) size_unit(-3); + border-top: 1px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); position: sticky; bottom: 0; z-index: 10; + background-color: var(--light-mode-body, var(--dark-mode-bg-black)); .actionBtn { width: 100%; } diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx index a49bcd69a..7d3041c83 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.tsx @@ -88,11 +88,7 @@ export const ConfirmTransaction = (): React.ReactElement => { useOnBeforeUnload(disallowSignTx); return ( - + {req && txType ? ( setConfirmTransactionError(true)} /> ) : ( diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx index a6fd22419..73a523c67 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx @@ -1,227 +1,185 @@ -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { useObservable } from '@lace/common'; -import { useWalletStore } from '@stores'; -import { Skeleton } from 'antd'; import { DappTransaction } from '@lace/core'; -import { TokenInfo, getAssetsInformation } from '@src/utils/get-assets-information'; -import { useAddressBookContext, withAddressBookContext } from '@src/features/address-book/context'; -import { AddressListType } from '@src/views/browser-view/features/activity'; -import { useCurrencyStore } from '@providers/currency'; -import { useFetchCoinPrice } from '@hooks'; -import { useViewsFlowContext } from '@providers'; +import { Flex } from '@lace/ui'; +import { useViewsFlowContext } from '@providers/ViewFlowProvider'; + import { Wallet } from '@lace/cardano'; +import { withAddressBookContext } from '@src/features/address-book/context'; +import { useWalletStore } from '@stores'; +import { exposeApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension'; +import { DAPP_CHANNELS } from '@src/utils/constants'; +import { runtime } from 'webextension-polyfill'; +import { useFetchCoinPrice, useChainHistoryProvider } from '@hooks'; import { - AssetsMintedInspection, createTxInspector, - assetsMintedInspector, - assetsBurnedInspector, - MintedAsset + TransactionSummaryInspection, + transactionSummaryInspector, + Cardano, + TokenTransferValue, + tokenTransferInspector } from '@cardano-sdk/core'; +import { createWalletAssetProvider } from '@cardano-sdk/wallet'; +import { Skeleton } from 'antd'; +import type { UserPromptService } from '@lib/scripts/background/services'; +import { of, take } from 'rxjs'; + +import { useCurrencyStore, useAppSettingsContext } from '@providers'; +import { logger, signingCoordinator } from '@lib/wallet-api-ui'; import { useComputeTxCollateral } from '@hooks/useComputeTxCollateral'; +import { utxoAndBackendChainHistoryResolver } from '@src/utils/utxo-chain-history-resolver'; -interface Props { +interface DappTransactionContainerProps { errorMessage?: string; } -const convertMetadataArrayToObj = (arr: unknown[]): Record => { - const result: Record = {}; - for (const item of arr) { - if (typeof item === 'object' && !Array.isArray(item) && item !== null) { - Object.assign(result, item); - } - } - return result; -}; - -// eslint-disable-next-line complexity, sonarjs/cognitive-complexity -const getAssetNameFromMintMetadata = (asset: MintedAsset, metadata: Wallet.Cardano.TxMetadata): string | undefined => { - if (!asset || !metadata) return; - const decodedAssetName = Buffer.from(asset.assetName, 'hex').toString(); - - // Tries to find the asset name in the tx metadata under label 721 or 20 - for (const [key, value] of metadata.entries()) { - // eslint-disable-next-line no-magic-numbers - if (key !== BigInt(721) && key !== BigInt(20)) return; - const cip25Metadata = Wallet.cardanoMetadatumToObj(value); - if (!Array.isArray(cip25Metadata)) return; - - // cip25Metadata should be an array containing all policies for the minted assets in the tx - const policyLevelMetadata = convertMetadataArrayToObj(cip25Metadata)[asset.policyId]; - if (!Array.isArray(policyLevelMetadata)) return; - - // policyLevelMetadata should be an array of objects with the minted assets names as key - // e.g. "policyId" = [{ "AssetName1": { ...metadataAsset1 } }, { "AssetName2": { ...metadataAsset2 } }]; - const assetProperties = convertMetadataArrayToObj(policyLevelMetadata)?.[decodedAssetName]; - if (!Array.isArray(assetProperties)) return; - - // assetProperties[decodedAssetName] should be an array of objects with the properties as keys - // e.g. [{ "name": "Asset Name" }, { "description": "An asset" }, ...] - const assetMetadataName = convertMetadataArrayToObj(assetProperties)?.name; - // eslint-disable-next-line consistent-return - return typeof assetMetadataName === 'string' ? assetMetadataName : undefined; - } -}; - -// eslint-disable-next-line complexity, sonarjs/cognitive-complexity -export const DappTransactionContainer = withAddressBookContext(({ errorMessage }: Props): React.ReactElement => { - const { - walletInfo, - inMemoryWallet, - blockchainProvider: { assetProvider }, - walletUI: { cardanoCoin }, - walletState - } = useWalletStore(); - const { - signTxRequest: { request }, - dappInfo - } = useViewsFlowContext(); - const currencyStore = useCurrencyStore(); - const coinPrice = useFetchCoinPrice(); - const { list: addressList } = useAddressBookContext() as { list: AddressListType[] }; - const tx = useMemo(() => request?.transaction.toCore(), [request?.transaction]); - const assets = useObservable(inMemoryWallet.assetInfo$); - const [assetsInfo, setAssetsInfo] = useState(); - - const txCollateral = useComputeTxCollateral(walletState, tx); - - const assetIds = useMemo(() => { - if (!tx) return []; - const uniqueAssetIds = new Set(); - // Merge all assets (TokenMaps) from the tx outputs and mint - const assetMaps = tx.body?.outputs?.map((output) => output.value.assets) ?? []; - if (tx.body?.mint?.size > 0) assetMaps.push(tx.body.mint); - - // Extract all unique asset ids from the array of TokenMaps - for (const asset of assetMaps) { - if (asset) { - for (const id of asset.keys()) { - !uniqueAssetIds.has(id) && uniqueAssetIds.add(id); - } - } - } - return [...uniqueAssetIds.values()]; - }, [tx]); - - useEffect(() => { - if (assetIds?.length > 0) { - getAssetsInformation(assetIds, assets, { - assetProvider, - extraData: { nftMetadata: true, tokenMetadata: true } - }) - .then((result) => setAssetsInfo(result)) - .catch((error) => { - console.error(error); - }); - } - }, [assetIds, assetProvider, assets]); - - const createMintedList = useCallback( - (mintedAssets: AssetsMintedInspection) => { - if (!assetsInfo) return []; - return mintedAssets.map((asset) => { - const assetId = Wallet.Cardano.AssetId.fromParts(asset.policyId, asset.assetName); - const assetInfo = assets.get(assetId) || assetsInfo?.get(assetId); - // If it's a new asset or the name is being updated we should be getting it from the tx metadata - const metadataName = getAssetNameFromMintMetadata(asset, tx?.auxiliaryData?.blob); - return { - name: assetInfo?.name.toString() || asset.fingerprint || assetId, - ticker: - metadataName ?? - assetInfo?.nftMetadata?.name ?? - assetInfo?.tokenMetadata?.ticker ?? - assetInfo?.tokenMetadata?.name ?? - asset.fingerprint.toString(), - amount: Wallet.util.calculateAssetBalance(asset.quantity, assetInfo) - }; +export const DappTransactionContainer = withAddressBookContext( + ({ errorMessage }: DappTransactionContainerProps): React.ReactElement => { + const { + signTxRequest: { request: req, set: setSignTxRequest }, + dappInfo + } = useViewsFlowContext(); + + const { + walletInfo, + inMemoryWallet, + blockchainProvider: { assetProvider }, + walletUI: { cardanoCoin }, + walletState + } = useWalletStore(); + + const { fiatCurrency } = useCurrencyStore(); + const { priceResult } = useFetchCoinPrice(); + + const [{ chainName }] = useAppSettingsContext(); + + const [fromAddressTokens, setFromAddressTokens] = useState< + Map | undefined + >(); + const [toAddressTokens, setToAddressTokens] = useState< + Map | undefined + >(); + const [transactionInspectionDetails, setTransactionInspectionDetails] = useState< + TransactionSummaryInspection | undefined + >(); + + const chainHistoryProvider = useChainHistoryProvider({ chainName }); + + const txInputResolver = useMemo( + () => + utxoAndBackendChainHistoryResolver({ + utxo: inMemoryWallet.utxo, + transactions: inMemoryWallet.transactions, + chainHistoryProvider + }), + [inMemoryWallet, chainHistoryProvider] + ); + + const tx = useMemo(() => req?.transaction.toCore(), [req?.transaction]); + const txCollateral = useComputeTxCollateral(walletState, tx); + + useEffect(() => { + const subscription = signingCoordinator.transactionWitnessRequest$.pipe(take(1)).subscribe(async (r) => { + setSignTxRequest(r); }); - }, - [assets, assetsInfo, tx] - ); - - const createAssetList = useCallback( - (txAssets: Wallet.Cardano.TokenMap) => { - if (!assetsInfo) return []; - const assetList: Wallet.Cip30SignTxAssetItem[] = []; - txAssets.forEach(async (value, key) => { - const walletAsset = assets.get(key) || assetsInfo?.get(key); - assetList.push({ - name: walletAsset?.name.toString() || key.toString(), - ticker: walletAsset?.tokenMetadata?.ticker || walletAsset?.nftMetadata?.name, - amount: Wallet.util.calculateAssetBalance(value, walletAsset) - }); - }); - return assetList; - }, - [assets, assetsInfo] - ); - - const addressToNameMap = useMemo( - () => new Map(addressList?.map((item: AddressListType) => [item.address, item.name])), - [addressList] - ); - - const [txSummary, setTxSummary] = useState(); - - useEffect(() => { - if (!tx) { - setTxSummary(void 0); - return; - } - const getTxSummary = async () => { - const inspector = createTxInspector({ - minted: assetsMintedInspector, - burned: assetsBurnedInspector - }); - - const { minted, burned } = await inspector(tx as Wallet.Cardano.HydratedTx); - const isMintTransaction = minted.length > 0 || burned.length > 0; - - const txType = isMintTransaction ? Wallet.Cip30TxType.Mint : Wallet.Cip30TxType.Send; - const externalOutputs = tx.body.outputs.filter((output) => { - if (txType === 'Send') { - return walletInfo.addresses.every((addr) => output.address !== addr.address); - } - return true; - }); + const api = exposeApi>( + { + api$: of({ + async readyToSignTx(): Promise { + return Promise.resolve(true); + } + }), + baseChannel: DAPP_CHANNELS.userPrompt, + properties: { readyToSignTx: RemoteApiPropertyType.MethodReturningPromise } + }, + { logger: console, runtime } + ); + + return () => { + subscription.unsubscribe(); + api.shutdown(); + }; + }, [setSignTxRequest]); + + const userAddresses = useMemo(() => walletInfo.addresses.map((v) => v.address), [walletInfo.addresses]); + const userRewardAccounts = useObservable(inMemoryWallet.delegation.rewardAccounts$); + const rewardAccountsAddresses = useMemo(() => userRewardAccounts?.map((key) => key.address), [userRewardAccounts]); + const protocolParameters = useObservable(inMemoryWallet?.protocolParameters$); + + useEffect(() => { + if (!req || !protocolParameters) { + setTransactionInspectionDetails(void 0); + return; + } + const getTxSummary = async () => { + const inspector = createTxInspector({ + tokenTransfer: tokenTransferInspector({ + inputResolver: txInputResolver, + fromAddressAssetProvider: createWalletAssetProvider({ + assetProvider, + assetInfo$: inMemoryWallet.assetInfo$, + logger + }), + toAddressAssetProvider: createWalletAssetProvider({ + assetProvider, + assetInfo$: inMemoryWallet.assetInfo$, + tx, + logger + }) + }), + summary: transactionSummaryInspector({ + addresses: userAddresses, + rewardAccounts: rewardAccountsAddresses, + inputResolver: txInputResolver, + protocolParameters, + assetProvider: createWalletAssetProvider({ + assetProvider, + assetInfo$: inMemoryWallet.assetInfo$, + tx, + logger + }) + }) + }); - const txSummaryOutputs: Wallet.Cip30SignTxSummary['outputs'] = externalOutputs.reduce((acc, txOut) => { - // Don't show withdrawl tx's etc - if (txOut.address.toString() === walletInfo.addresses[0].address.toString()) return acc; - - return [ - ...acc, - { - coins: Wallet.util.lovelacesToAdaString(txOut.value.coins.toString()), - recipient: addressToNameMap?.get(txOut.address.toString()) || txOut.address.toString(), - ...(txOut.value.assets?.size > 0 && { assets: createAssetList(txOut.value.assets) }) - } - ]; - }, []); - - // eslint-disable-next-line consistent-return - setTxSummary({ - fee: Wallet.util.lovelacesToAdaString(tx.body.fee.toString()), - outputs: txSummaryOutputs, - type: txType, - mintedAssets: createMintedList(minted), - burnedAssets: createMintedList(burned), - collateral: txCollateral ? Wallet.util.lovelacesToAdaString(txCollateral.toString()) : undefined - }); - }; - getTxSummary(); - }, [tx, walletInfo.addresses, createAssetList, createMintedList, addressToNameMap, setTxSummary, txCollateral]); - - return tx && txSummary ? ( - - ) : ( - - ); -}); + const { summary, tokenTransfer } = await inspector(tx as Wallet.Cardano.HydratedTx); + + const { toAddress, fromAddress } = tokenTransfer; + setToAddressTokens(toAddress); + setFromAddressTokens(fromAddress); + setTransactionInspectionDetails(summary); + }; + getTxSummary(); + }, [ + req, + walletInfo.addresses, + userAddresses, + rewardAccountsAddresses, + txInputResolver, + protocolParameters, + assetProvider, + inMemoryWallet.assetInfo$, + tx + ]); + + return ( + + {req && transactionInspectionDetails && dappInfo ? ( + + ) : ( + + )} + + ); + } +); diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx index b7560a74d..c2847651c 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/ConfirmTransaction.test.tsx @@ -184,7 +184,6 @@ describe('Testing ConfirmTransaction component', () => { })); }); - expect(queryByTestId(testIds.layoutTitle)).toHaveTextContent(txType); expect(queryByTestId('ConfirmTransactionContent')).toBeInTheDocument(); expect(mockConfirmTransactionContent).toHaveBeenLastCalledWith( { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx index 0922f14c4..98afa976f 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx @@ -19,6 +19,11 @@ const mockWithAddressBookContext = jest.fn((children) => children); const mockUseCurrencyStore = jest.fn().mockReturnValue({ fiatCurrency: { code: 'usd', symbol: '$' } }); const mockUseFetchCoinPrice = jest.fn().mockReturnValue({ priceResult: { cardano: { price: 2 }, tokens: new Map() } }); const mockUseComputeTxCollateral = jest.fn().mockReturnValue(BigInt(1_000_000)); +const mockUseChainHistoryProvider = jest.fn().mockReturnValue({ + transactionsByAddress: jest.fn().mockResolvedValue([]), + transactionsByHashes: jest.fn().mockResolvedValue([]), + blocksByHashes: jest.fn().mockResolvedValue([]) +}); import * as React from 'react'; import { cleanup, render } from '@testing-library/react'; import { DappTransactionContainer } from '../DappTransactionContainer'; @@ -35,7 +40,8 @@ import { cardanoCoin } from '@src/utils/constants'; const { Cardano, Crypto } = Wallet; const assetProvider = { - getAssets: jest.fn(() => ['assets']) + getAssets: jest.fn(() => ['assets']), + getAsset: jest.fn(() => 'asset') }; const walletInfo = { name: 'wall', @@ -44,6 +50,19 @@ const walletInfo = { const mockedAssetsInfo = new Map([['id', 'data']]); const assetInfo$ = new BehaviorSubject(mockedAssetsInfo); const available$ = new BehaviorSubject([]); +const signed$ = new BehaviorSubject([]); +const rewardAccounts$ = new BehaviorSubject([ + { + // eslint-disable-next-line unicorn/consistent-destructuring + address: Wallet.Cardano.RewardAccount('stake_test1uqrw9tjymlm8wrwq7jk68n6v7fs9qz8z0tkdkve26dylmfc2ux2hj'), + credentialStatus: 'REGISTERED', + rewardBalance: 1 + } +]); +const protocolParameters$ = new BehaviorSubject({ + stakeKeyDeposit: 1, + poolDeposit: 1 +}); const inMemoryWallet = { assetInfo$, @@ -51,7 +70,19 @@ const inMemoryWallet = { utxo: { available$ } - } + }, + utxo: { + available$ + }, + transactions: { + outgoing: { + signed$ + } + }, + delegation: { + rewardAccounts$ + }, + protocolParameters$ }; jest.mock('@src/stores', () => ({ @@ -79,6 +110,11 @@ jest.mock('@providers/currency', (): typeof CurrencyProvider => ({ useCurrencyStore: mockUseCurrencyStore })); +jest.mock('@hooks/useChainHistoryProvider', (): typeof CurrencyProvider => ({ + ...jest.requireActual('@hooks/useChainHistoryProvider'), + useChainHistoryProvider: mockUseChainHistoryProvider +})); + jest.mock('@lace/core', () => { const original = jest.requireActual('@lace/core'); return { @@ -142,8 +178,8 @@ const request = { } as any } as TransactionWitnessRequest; -jest.mock('@providers', () => ({ - ...jest.requireActual('@providers'), +jest.mock('@providers/ViewFlowProvider', () => ({ + ...jest.requireActual('@providers/ViewFlowProvider'), useViewsFlowContext: mockUseViewsFlowContext })); @@ -166,14 +202,14 @@ describe('Testing DappTransactionContainer component', () => { mockSkeleton.mockImplementation(() => ); mockUseViewsFlowContext.mockReset(); mockUseViewsFlowContext.mockImplementation(() => ({ - signTxRequest: { request }, + signTxRequest: { request, set: jest.fn() }, dappInfo })); }); afterEach(() => { jest.resetModules(); - jest.resetAllMocks(); + jest.clearAllMocks(); cleanup(); }); @@ -183,83 +219,120 @@ describe('Testing DappTransactionContainer component', () => { const errorMessage = 'errorMessage'; const props = { errorMessage }; - const txSummary = { - burnedAssets: [], - collateral: '1.00', - fee: '0.17', - mintedAssets: [ + const toAddress = new Map([ + [ + // eslint-disable-next-line unicorn/consistent-destructuring + Wallet.Cardano.PaymentAddress( + 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' + ), { - amount: '3', - name: 'asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh', - ticker: 'asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh' + assets: new Map([ + [ + '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + { + amount: BigInt(9), + assetInfo: { + assetId: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + fingerprint: 'asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh', + name: '54534c41', + nftMetadata: null, + policyId: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba82', + quantity: BigInt(3), + supply: BigInt(3) + } + } + ], + [ + '6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7', + { + amount: BigInt(4), + assetInfo: { + assetId: '6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7', + fingerprint: 'asset1cvmyrfrc7lpht2hcjwr9lulzyyjv27uxh3kcz0', + name: '', + policyId: '6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7', + quantity: BigInt(0), + supply: BigInt(0) + } + } + ] + ]), + coins: BigInt(9_000_000) } ], - outputs: [ - { - coins: '5.00', - recipient: - 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' - }, - { - assets: [ - { - amount: '3', - name: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', - ticker: undefined - }, - { - amount: '4', - name: '6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7', - ticker: undefined - } - ], - coins: '2.00', - recipient: - 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' - }, + [ + // eslint-disable-next-line unicorn/consistent-destructuring + Wallet.Cardano.PaymentAddress( + 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' + ), { - assets: [ - { - amount: '6', - name: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', - ticker: undefined - } - ], - coins: '2.00', - recipient: - 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' - }, - { - assets: [ - { - amount: '1', - name: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', - ticker: undefined - } - ], - coins: '2.00', - recipient: - 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' + assets: new Map([ + [ + '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + { + amount: BigInt(1), + assetInfo: { + assetId: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', + fingerprint: 'asset1rqluyux4nxv6kjashz626c8usp8g88unmqwnyh', + name: '54534c41', + nftMetadata: null, + policyId: '659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba82', + quantity: BigInt(3), + supply: BigInt(3) + } + } + ] + ]), + coins: BigInt(2_000_000) } - ], - type: 'Mint' - } as Wallet.Cip30SignTxSummary; + ] + ]); + + const txInspectionDetails = { + assets: new Map(), + coins: BigInt(0), + collateral: BigInt(0), + deposit: BigInt(1000), + fee: BigInt(170_000), + returnedDeposit: BigInt(0), + unresolved: { + inputs: [ + { + address: + 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g', + index: 0, + txId: 'bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0' + } + ], + value: { + assets: new Map([ + ['6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7', BigInt(4)], + ['659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41', BigInt(7)] + ]), + + coins: BigInt(11_171_000) + } + } + }; await act(async () => { ({ queryByTestId } = render(, { wrapper: getWrapper() })); }); - expect(queryByTestId('DappTransaction')).toBeInTheDocument(); + expect(mockDappTransaction).toHaveBeenLastCalledWith( { dappInfo, - transaction: txSummary, + toAddress, + fromAddress: new Map(), fiatCurrencyCode: 'usd', fiatCurrencyPrice: 2, errorMessage, - coinSymbol: 'ADA' + coinSymbol: 'ADA', + collateral: BigInt(1_000_000), + txInspectionDetails }, {} ); @@ -285,21 +358,4 @@ describe('Testing DappTransactionContainer component', () => { expect(queryByTestId('DappTransaction')).not.toBeInTheDocument(); expect(queryByTestId('skeleton')).toBeInTheDocument(); }); - - test('should render loader in case there is no txSummary', async () => { - let queryByTestId: any; - - mockUseCurrencyStore.mockRestore(); - - const signTxData = { tx: { body: {} } } as unknown as SignTxData; - - await act(async () => { - ({ queryByTestId } = render(, { - wrapper: getWrapper() - })); - }); - - expect(queryByTestId('DappTransaction')).not.toBeInTheDocument(); - expect(queryByTestId('skeleton')).toBeInTheDocument(); - }); }); diff --git a/apps/browser-extension-wallet/src/hooks/index.ts b/apps/browser-extension-wallet/src/hooks/index.ts index cccb01e6d..fccba25c0 100644 --- a/apps/browser-extension-wallet/src/hooks/index.ts +++ b/apps/browser-extension-wallet/src/hooks/index.ts @@ -21,3 +21,4 @@ export * from './useAssetInfo'; export * from './useUpdateAddressStatus'; export * from './useOnAddressSave'; export * from './useAppInit'; +export * from './useChainHistoryProvider'; diff --git a/apps/browser-extension-wallet/src/hooks/useChainHistoryProvider.ts b/apps/browser-extension-wallet/src/hooks/useChainHistoryProvider.ts new file mode 100644 index 000000000..413091435 --- /dev/null +++ b/apps/browser-extension-wallet/src/hooks/useChainHistoryProvider.ts @@ -0,0 +1,17 @@ +import { getBaseUrlForChain } from '@src/utils/chain'; +import { useMemo } from 'react'; +import { chainHistoryHttpProvider } from '@cardano-sdk/cardano-services-client'; +import { logger } from '@lib/wallet-api-ui'; + +export type NetworkType = 'Mainnet' | 'Preprod' | 'Preview' | 'Sanchonet'; + +type UseChainHistoryProviderArgs = { + chainName: NetworkType; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useChainHistoryProvider = ({ chainName }: UseChainHistoryProviderArgs) => { + const baseCardanoServicesUrl = getBaseUrlForChain(chainName); + + return useMemo(() => chainHistoryHttpProvider({ baseUrl: baseCardanoServicesUrl, logger }), [baseCardanoServicesUrl]); +}; diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 18624c8d8..503fb9500 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -123,7 +123,16 @@ "recipient": "Recipient", "send": "Send", "sending": "Sending", - "transaction": "Transaction" + "transaction": "Transaction", + "transactionSummary": "Transaction Summary", + "origin": "Origin", + "fromAddress": "From address", + "toAddress": "To address", + "deposit": "Deposit", + "returnedDeposit": "Returned deposit", + "address": "Address", + "nfts": "NFTs", + "tokens": "Tokens" } }, "tab.main.title": "Tab extension", diff --git a/apps/browser-extension-wallet/src/utils/utxo-chain-history-resolver.ts b/apps/browser-extension-wallet/src/utils/utxo-chain-history-resolver.ts new file mode 100644 index 000000000..0861c30cf --- /dev/null +++ b/apps/browser-extension-wallet/src/utils/utxo-chain-history-resolver.ts @@ -0,0 +1,23 @@ +import { Wallet } from '@lace/cardano'; +import { WitnessedTx } from '@cardano-sdk/key-management'; + +import { combineInputResolvers, createBackendInputResolver, createInputResolver } from '@cardano-sdk/wallet'; +import { Cardano } from '@cardano-sdk/core'; +import { Observable } from 'rxjs'; + +interface UtxoAndBackendChainHistoryResolverArgs { + utxo: Wallet.ObservableWallet['utxo']; + chainHistoryProvider: Wallet.ChainHistoryProvider; + transactions: { + outgoing: { + signed$: Observable; + }; + }; +} + +export const utxoAndBackendChainHistoryResolver = ({ + utxo, + transactions, + chainHistoryProvider +}: UtxoAndBackendChainHistoryResolverArgs): Cardano.InputResolver => + combineInputResolvers(createInputResolver({ utxo, transactions }), createBackendInputResolver(chainHistoryProvider)); diff --git a/packages/common/src/ui/lib/add-ellipsis.ts b/packages/common/src/ui/lib/add-ellipsis.ts index 2fa46fac8..8938ff784 100644 --- a/packages/common/src/ui/lib/add-ellipsis.ts +++ b/packages/common/src/ui/lib/add-ellipsis.ts @@ -1,6 +1,13 @@ /* eslint-disable no-magic-numbers */ const addedLength = 4; +export const truncate = (text: string, partOneLength: number, partTwoLength: number): string => { + const textMinLenght = partOneLength + partTwoLength + addedLength; + if (text.length <= textMinLenght) return text; + + return `${text.slice(0, partOneLength)}${text.slice(text.length - partTwoLength)}`; +}; + export const addEllipsis = (text: string, partOnelength: number, partTwoLength: number): string => { const textMinLenght = partOnelength + partTwoLength + addedLength; if (text.length <= textMinLenght) return text; diff --git a/packages/common/src/ui/styles/theme.scss b/packages/common/src/ui/styles/theme.scss index 9bde8195e..b2c556961 100644 --- a/packages/common/src/ui/styles/theme.scss +++ b/packages/common/src/ui/styles/theme.scss @@ -7,6 +7,7 @@ $theme-text-color-light-grey: var(--text-color-light-grey, #878e9e); $theme-light-grey: var(--data-light-grey, #f9f9f9); $theme-light-grey-border: #efefef; $theme-light-grey-border-opacity: #efefef8f; +$theme-white: #ffffff; $breakpoint-minimum: 668px; $breakpoint-xsmall: 1024px; diff --git a/packages/common/src/ui/styles/themes/_light.scss b/packages/common/src/ui/styles/themes/_light.scss index ece0c096e..a4979d456 100644 --- a/packages/common/src/ui/styles/themes/_light.scss +++ b/packages/common/src/ui/styles/themes/_light.scss @@ -19,6 +19,7 @@ if the variables are undefined we use the fallback color $light: ( 'black': #3d3b39, 'cream': #fcf5e3, + 'body':#ffffff, 'dark-grey-plus': #7e8593, 'dark-grey': #6f7786, 'mid-grey': #c0c0c0, diff --git a/packages/core/.storybook/__mocks__/cardano.ts b/packages/core/.storybook/__mocks__/cardano.ts index 6b4b8382d..7c65b255b 100644 --- a/packages/core/.storybook/__mocks__/cardano.ts +++ b/packages/core/.storybook/__mocks__/cardano.ts @@ -1,6 +1,10 @@ import { Cardano, util } from '@cardano-sdk/core'; +import * as WalletUtil from '../../../cardano/src/wallet/util'; export const Wallet = { - util, + util: { + ...util, + ...WalletUtil + }, Cardano }; diff --git a/packages/core/src/ui/components/ActivityDetail/Collateral.tsx b/packages/core/src/ui/components/ActivityDetail/Collateral.tsx index 763f165d1..631484ae7 100644 --- a/packages/core/src/ui/components/ActivityDetail/Collateral.tsx +++ b/packages/core/src/ui/components/ActivityDetail/Collateral.tsx @@ -14,6 +14,8 @@ export interface Props { collateral: string; amountTransformer: (amount: string) => string; coinSymbol: string; + displayFiat?: boolean; + className?: string; status?: CollateralStatus; } @@ -21,6 +23,8 @@ export const Collateral = ({ collateral, amountTransformer, coinSymbol, + displayFiat, + className, status = CollateralStatus.REVIEW }: Props): React.ReactElement => { const { t } = useTranslate(); @@ -44,6 +48,8 @@ export const Collateral = ({ fiatPrice={amountTransformer(collateral)} label={t('package.core.activityDetails.collateral.label')} tooltip={getTooltipText()} + displayFiat={displayFiat} + className={className} /> {status === CollateralStatus.ERROR && ( diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx index 4cd059b35..2a1848aa7 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx @@ -403,7 +403,12 @@ export const TransactionDetails = ({ )} {fee && fee !== '-' && ( - + )} {deposit && renderDepositValueSection({ value: deposit, label: t('package.core.activityDetails.deposit') })} diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx index 26312ad81..13eb8b50f 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx @@ -6,17 +6,36 @@ export interface TransactionFeeProps { fee: string; amountTransformer: (amount: string) => string; coinSymbol: string; + label?: string; + className?: string; + testId?: string; + tooltipInfo?: string; + displayFiat?: boolean; + highlightPositiveAmount?: boolean; } -export const TransactionFee = ({ fee, amountTransformer, coinSymbol }: TransactionFeeProps): React.ReactElement => { +export const TransactionFee = ({ + fee, + amountTransformer, + coinSymbol, + label, + className, + testId, + tooltipInfo, + displayFiat, + highlightPositiveAmount +}: TransactionFeeProps): React.ReactElement => { const { t } = useTranslate(); return ( ); }; diff --git a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.module.scss b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.module.scss new file mode 100644 index 000000000..74bf3b3cc --- /dev/null +++ b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.module.scss @@ -0,0 +1,34 @@ +@import '../../styles/theme.scss'; +@import '../../../../../common/src/ui/styles/abstracts/_typography.scss'; + +.label { + color: var(--text-color-primary, #ffffff) !important; + font-size: 16px; + font-weight: 600; +} + +.value { + color: var(--text-color-primary, #ffffff) !important; + font-size: 14px; + font-weight: 500; +} + +.address { + display: flex; + justify-content: space-between; + padding: size_unit(2.5) 0; +} + +.summaryContent { + padding-bottom: size_unit(2.5); +} + +.tokenCount { + display: flex; + justify-content: space-between; + align-items: end; +} + +.positiveAmount { + color: var(--data-green, #2CB67D) !important; +} diff --git a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx new file mode 100644 index 000000000..e11b7c79b --- /dev/null +++ b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx @@ -0,0 +1,219 @@ +/* eslint-disable sonarjs/no-identical-functions */ +import React from 'react'; +import { truncate, addEllipsis } from '@lace/common'; +import { Wallet } from '@lace/cardano'; +import { Cardano, AssetInfoWithAmount } from '@cardano-sdk/core'; +import { Typography } from 'antd'; + +import styles from './DappAddressSections.module.scss'; +import { useTranslate } from '@src/ui/hooks'; + +import { TransactionAssets, SummaryExpander, DappTransactionSummary, Tooltip } from '@lace/ui'; +import classNames from 'classnames'; + +interface GroupedAddressAssets { + nfts: Array; + tokens: Array; + coins: Array; +} + +export interface DappAddressSectionProps { + groupedFromAddresses: Map; + groupedToAddresses: Map; + isToAddressesEnabled: boolean; + isFromAddressesEnabled: boolean; + coinSymbol: string; +} + +const tryDecodeAsUtf8 = ( + value: WithImplicitCoercion | { [Symbol.toPrimitive](hint: 'string'): string } +): string => { + const bytes = Uint8Array.from(Buffer.from(value, 'hex')); + const decoder = new TextDecoder('utf-8'); + // Decode the Uint8Array to a UTF-8 string + return decoder.decode(bytes); +}; + +const getFallbackName = (asset: AssetInfoWithAmount) => + tryDecodeAsUtf8(asset.assetInfo.name) ? tryDecodeAsUtf8(asset.assetInfo.name) : asset.assetInfo.assetId; + +const isNFT = (asset: AssetInfoWithAmount) => asset.assetInfo.supply === BigInt(1); + +const getAssetTokenName = (assetWithAmount: AssetInfoWithAmount) => { + if (isNFT(assetWithAmount)) { + return assetWithAmount.assetInfo.nftMetadata?.name ?? getFallbackName(assetWithAmount); + } + return assetWithAmount.assetInfo.tokenMetadata?.ticker ?? getFallbackName(assetWithAmount); +}; + +const charBeforeEllName = 9; +const charAfterEllName = 0; + +const displayGroupedNFTs = (nfts: AssetInfoWithAmount[]) => + nfts.map((nft: AssetInfoWithAmount) => { + const imageSrc = nft.assetInfo.tokenMetadata?.icon ?? nft.assetInfo.nftMetadata?.image ?? undefined; + return ( + + ); + }); + +const displayGroupedTokens = (tokens: AssetInfoWithAmount[]) => + tokens.map((token: AssetInfoWithAmount) => { + const imageSrc = token.assetInfo.tokenMetadata?.icon ?? token.assetInfo.nftMetadata?.image ?? undefined; + + return ( + + ); + }); + +const { Title, Text } = Typography; + +const charBeforeEllipsisName = 8; +const charAfterEllipsisName = 8; + +const getStringFromLovelace = (value: bigint): string => Wallet.util.lovelacesToAdaString(value.toString()); + +const getTokenQuantity = (tokens: Array, coins: Array) => { + let quantity = tokens.length; + + if (coins.length > 0) { + quantity += 1; + } + + return quantity; +}; +export const DappAddressSections = ({ + groupedFromAddresses, + groupedToAddresses, + isToAddressesEnabled, + isFromAddressesEnabled, + coinSymbol +}: DappAddressSectionProps): React.ReactElement => { + const { t } = useTranslate(); + + const itemsCountCopy = t('package.core.dappTransaction.items'); + + return ( + <> + +
+ {[...groupedFromAddresses.entries()].map(([address, addressData]) => ( + <> +
+ + {t('package.core.dappTransaction.address')} + + + + {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} + + +
+ {(addressData.tokens.length > 0 || addressData.coins.length > 0) && ( + <> +
+ + {t('package.core.dappTransaction.tokens')} + + + -{getTokenQuantity(addressData.tokens, addressData.coins)} {itemsCountCopy} + +
+ {addressData.coins.map((coin) => ( + + ))} + {displayGroupedTokens(addressData.tokens)} + + )} + + {addressData.nfts.length > 0 && ( + <> +
+ + {t('package.core.dappTransaction.nfts')} + + + -{addressData.nfts.length} {itemsCountCopy} + +
+ {displayGroupedNFTs(addressData.nfts)} + + )} + + ))} +
+
+ + +
+ {[...groupedToAddresses.entries()].map(([address, addressData]) => ( + <> +
+ + {t('package.core.dappTransaction.address')} + + + + {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} + + +
+ {(addressData.tokens.length > 0 || addressData.coins.length > 0) && ( + <> +
+ + {t('package.core.dappTransaction.tokens')} + + + {getTokenQuantity(addressData.tokens, addressData.coins)} {itemsCountCopy} + +
+ {addressData.coins.map((coin) => ( + + ))} + {displayGroupedTokens(addressData.tokens)} + + )} + + {addressData.nfts.length > 0 && ( + <> +
+ + {t('package.core.dappTransaction.nfts')} + + + {addressData.nfts.length} {itemsCountCopy} + +
+ {displayGroupedNFTs(addressData.nfts)} + + )} + + ))} +
+
+ + ); +}; diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss b/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss index ea1eb99b3..c612aad03 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.module.scss @@ -1,52 +1,23 @@ @import '../../styles/theme.scss'; @import '../../../../../common/src/ui/styles/abstracts/_typography.scss'; -.dappInfo { - margin: size_unit(1) 0px; -} - .details { display: flex; - flex: 1; flex-direction: column; - justify-content: space-between; - margin: size_unit(4) 0 size_unit(2) 0px; - padding: size_unit(3) 0; - border-top: 2px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); - gap: size_unit(3); } .error { margin: size_unit(2) 0px; } +.feeContainer { + padding-bottom: size_unit(2.5); +} -.warningAlert { - flex-direction: row; - background: var(--lace-cream); - display: flex; - align-items: center; - border-radius: size_unit(2); - padding: size_unit(2) size_unit(3); - gap: size_unit(3); - - svg { - height: size_unit(3); - width: size_unit(3); - color: var(--data-orange); - } - - p { - @include text-body-semi-bold; - margin: 0; - } +.depositContainer { + padding-bottom: size_unit(1.8) !important; } -.feeContainer { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: space-between; - @include text-body-semi-bold; - color: var(--text-color-primary); +.transactionAssetsContainer { + margin-bottom: size_unit(2.5); } diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx index 07525eac9..f734549d4 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx @@ -17,20 +17,52 @@ type Story = StoryObj; const data: ComponentProps = { dappInfo: { - logo: 'https://cdn.mint.handle.me/favicon.png', - name: 'Mint', - url: 'https://preprod.mint.handle.me' + name: 'Mint' }, - transaction: { - fee: '0.17', - outputs: [ - { - coins: '1', - recipient: - 'addr_test1qrl0s3nqfljv8dfckn7c4wkzu5rl6wn4hakkddcz2mczt3szlqss933x0aag07qcgspcaglmay6ufl4y4lalmlpe02mqhl0fx2' + coinSymbol: 'tAda', + fiatCurrencyCode: 'usd', + fromAddress: new Map(), + toAddress: new Map(), + // eslint-disable-next-line no-magic-numbers + collateral: 150_000 as unknown as bigint, + txInspectionDetails: { + assets: new Map(), + // eslint-disable-next-line no-magic-numbers + coins: 1_171_000 as unknown as bigint, + // eslint-disable-next-line no-magic-numbers + collateral: 150_000 as unknown as bigint, + // eslint-disable-next-line no-magic-numbers + deposit: 1_000_000 as unknown as bigint, + // eslint-disable-next-line no-magic-numbers + fee: 170_000 as unknown as bigint, + // eslint-disable-next-line no-magic-numbers + returnedDeposit: 90_000 as unknown as bigint, + + unresolved: { + inputs: [ + { + index: 0, + txId: Wallet.Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0') + } + ], + value: { + assets: new Map([ + [ + Wallet.Cardano.AssetId('6b8d07d69639e9413dd637a1a815a7323c69c86abbafb66dbfdb1aa7'), + // eslint-disable-next-line no-magic-numbers + 4 as unknown as bigint + ], + [ + Wallet.Cardano.AssetId('659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41'), + // eslint-disable-next-line no-magic-numbers + 7 as unknown as bigint + ] + ]), + + // eslint-disable-next-line no-magic-numbers + coins: 11_171_000 as unknown as bigint } - ], - type: Wallet.Cip30TxType.Mint + } } }; diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx index 8a70fb94c..86e5673d8 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx @@ -1,97 +1,209 @@ -/* eslint-disable sonarjs/no-duplicate-string */ +/* eslint-disable no-console */ import React from 'react'; -import { ErrorPane } from '@lace/common'; +import { ErrorPane, truncate } from '@lace/common'; import { Wallet } from '@lace/cardano'; -import { DappInfo, DappInfoProps } from '../DappInfo'; -import { DappTxHeader } from './DappTxHeader/DappTxHeader'; -import { DappTxAsset } from './DappTxAsset/DappTxAsset'; -import { DappTxOutput } from './DappTxOutput/DappTxOutput'; + +import { Cardano, TransactionSummaryInspection, TokenTransferValue, AssetInfoWithAmount } from '@cardano-sdk/core'; + +import { DappTransactionHeader, DappTransactionHeaderProps, TransactionTypes } from '../DappTransactionHeader'; + import styles from './DappTransaction.module.scss'; import { useTranslate } from '@src/ui/hooks'; import { TransactionFee, Collateral } from '@ui/components/ActivityDetail'; +import { TransactionType, DappTransactionSummary, TransactionAssets } from '@lace/ui'; +import { DappAddressSections } from '../DappAddressSections/DappAddressSections'; + const amountTransformer = (fiat: { price: number; code: string }) => (ada: string) => `${Wallet.util.convertAdaToFiat({ ada, fiat: fiat.price })} ${fiat.code}`; export interface DappTransactionProps { /** Transaction details such as type, amount, fee and address */ - transaction: Wallet.Cip30SignTxSummary; + txInspectionDetails: TransactionSummaryInspection; /** dApp information such as logo, name and url */ - dappInfo: Omit; + dappInfo: Omit; /** Optional error message */ errorMessage?: string; fiatCurrencyCode?: string; fiatCurrencyPrice?: number; coinSymbol?: string; + /** tokens send to being sent to or from the user */ + fromAddress: Map; + toAddress: Map; + collateral?: bigint; } +const isNFT = (asset: AssetInfoWithAmount) => asset.assetInfo.supply === BigInt(1); + +interface GroupedAddressAssets { + nfts: Array; + tokens: Array; + coins: Array; +} + +const groupAddresses = (addresses: Map) => { + const groupedAddresses: Map = new Map(); + + for (const [address, value] of addresses) { + const group: GroupedAddressAssets = { + coins: [], + nfts: [], + tokens: [] + }; + + if (value.coins !== BigInt(0)) { + group.coins.push(value.coins); + } + + const addressAssets = value.assets; + for (const [, asset] of addressAssets) { + if (asset.assetInfo.supply === BigInt(1)) { + group.nfts.push(asset); + } else { + group.tokens.push(asset); + } + } + + if (group.coins.length > 0 || group.nfts.length > 0 || group.tokens.length > 0) { + groupedAddresses.set(address, group); + } + } + + return groupedAddresses; +}; + +type TransactionType = keyof typeof TransactionTypes; + +const tryDecodeAsUtf8 = ( + value: WithImplicitCoercion | { [Symbol.toPrimitive](hint: 'string'): string } +): string => { + const bytes = Uint8Array.from(Buffer.from(value, 'hex')); + const decoder = new TextDecoder('utf-8'); + // Decode the Uint8Array to a UTF-8 string + return decoder.decode(bytes); +}; + +const getFallbackName = (asset: AssetInfoWithAmount) => + tryDecodeAsUtf8(asset.assetInfo.name) ? tryDecodeAsUtf8(asset.assetInfo.name) : asset.assetInfo.assetId; + +const getAssetTokenName = (assetWithAmount: AssetInfoWithAmount) => { + if (isNFT(assetWithAmount)) { + return assetWithAmount.assetInfo.nftMetadata?.name ?? getFallbackName(assetWithAmount); + } + return assetWithAmount.assetInfo.tokenMetadata?.ticker ?? getFallbackName(assetWithAmount); +}; + +const getTxType = (coins: bigint): TransactionType => { + const balance = Wallet.util.lovelacesToAdaString(coins.toString()); + return balance.includes('-') === true ? 'Send' : 'Receive'; +}; + +const charBeforeEllName = 9; +const charAfterEllName = 0; + export const DappTransaction = ({ - transaction: { type, outputs, fee, mintedAssets, burnedAssets, collateral }, - dappInfo, + txInspectionDetails: { assets, coins, fee, deposit, returnedDeposit }, + toAddress, + fromAddress, + collateral, errorMessage, fiatCurrencyCode, fiatCurrencyPrice, - coinSymbol + coinSymbol, + dappInfo }: DappTransactionProps): React.ReactElement => { const { t } = useTranslate(); + + const groupedToAddresses = groupAddresses(toAddress); + const groupedFromAddresses = groupAddresses(fromAddress); + + const isFromAddressesEnabled = groupedFromAddresses.size > 0; + const isToAddressesEnabled = groupedToAddresses.size > 0; + return (
- {errorMessage && }
- {type === Wallet.Cip30TxType.Mint && mintedAssets?.length > 0 && ( - <> - - {mintedAssets.map((asset) => ( - - ))} - - )} - {type === Wallet.Cip30TxType.Mint && burnedAssets?.length > 0 && ( - <> - 0 ? undefined : t('package.core.dappTransaction.transaction')} - subtitle={t('package.core.dappTransaction.burn')} - /> - {burnedAssets.map((asset) => ( - - ))} - - )} - {type === Wallet.Cip30TxType.Send && ( - <> - - {outputs.map((output) => ( - - ))} - - )} - {collateral && ( + + +
+ {[...assets].map(([key, assetWithAmount]: [string, AssetInfoWithAmount]) => { + const imageSrc = + assetWithAmount.assetInfo.tokenMetadata?.icon ?? + assetWithAmount.assetInfo.nftMetadata?.image ?? + undefined; + return ( + + ); + })} +
+ + {collateral !== undefined && collateral !== BigInt(0) && ( )} - {fee && fee !== '-' && ( + + {returnedDeposit !== BigInt(0) && ( + `${Wallet.util.lovelacesToAdaString(returnedDeposit.toString())} ${fiatCurrencyCode}` + } + /> + )} + + {deposit !== BigInt(0) && ( + `${Wallet.util.lovelacesToAdaString(deposit.toString())} ${fiatCurrencyCode}`} /> )} + + `${Wallet.util.lovelacesToAdaString(fee.toString())} ${fiatCurrencyCode}`} + coinSymbol={coinSymbol} + className={styles.feeContainer} + displayFiat={false} + /> + +
); diff --git a/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss b/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss deleted file mode 100644 index 0d5bf78af..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.module.scss +++ /dev/null @@ -1,41 +0,0 @@ -@import '../../../styles/theme.scss'; -@import '../../../../../../common/src/ui/styles/abstracts/_typography.scss'; - -.body { - display: flex; - flex-direction: column; - gap: size_unit(3); - background-color: var(--light-mode-light-grey, var(--dark-mode-grey, #f9f9f9)); - border-radius: size_unit(2); - padding: size_unit(2); - - .detail { - @include text-body-semi-bold; - display: flex; - justify-content: space-between; - align-items: center; - - .title { - color: var(--text-color-primary); - line-height: 1; - } - - .value { - display: flex; - max-width: 50%; - align-items: flex-end; - flex-direction: column; - gap: size_unit(2); - - font-size: var(--bodySmall); - font-weight: 500; - line-height: 1; - /* Secondary - Black */ - color: var(--text-color-primary); - } - - .ellipsis > p { - margin: 0; - } - } -} diff --git a/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx b/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx deleted file mode 100644 index ea929db53..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxAsset/DappTxAsset.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import React from 'react'; -import styles from './DappTxAsset.module.scss'; -import { Ellipsis } from '@lace/common'; -import { useTranslate } from '@src/ui/hooks'; -import { Wallet } from '@lace/cardano'; - -export interface DappTxAssetProps { - name: string; - amount: string; - ticker?: string; -} - -export const DappTxAsset = ({ amount, name, ticker }: Wallet.Cip30SignTxAssetItem): React.ReactElement => { - const { t } = useTranslate(); - return ( -
-
-
{t('package.core.dappTransaction.asset')}
-
- -
-
-
-
{t('package.core.dappTransaction.quantity')}
-
{amount}
-
-
- ); -}; diff --git a/packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts b/packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts deleted file mode 100644 index 3418661f1..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxAsset/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { DappTxAsset } from './DappTxAsset'; diff --git a/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss b/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss deleted file mode 100644 index 94eefef49..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.module.scss +++ /dev/null @@ -1,27 +0,0 @@ -@import '../../../styles/theme.scss'; -@import '../../../../../../common/src/ui/styles/abstracts/_typography.scss'; - -.header { - font-size: var(--bodyLarge); - letter-spacing: -0.015em; - margin-bottom: size_unit(1); - display: flex; - justify-content: space-between; - align-items: center; - - .title { - font-weight: 600; - line-height: size_unit(3); - /* or 133% */ - /* Secondary - Black */ - color: var(--text-color-primary); - } - .type { - font-weight: 500; - line-height: size_unit(4); - /* or 178% */ - text-align: right; - /* Primary - Purple */ - color: var(--primary-default, #7f5af0); - } -} diff --git a/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx b/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx deleted file mode 100644 index 8117fcd3e..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxHeader/DappTxHeader.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import styles from './DappTxHeader.module.scss'; - -export interface DappTxHeaderProps { - title?: string; - subtitle?: string; -} - -export const DappTxHeader = (props: DappTxHeaderProps): React.ReactElement => ( -
-
- {props?.title ?? ''} -
- {props?.subtitle && ( -
- {props.subtitle} -
- )} -
-); diff --git a/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss b/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss deleted file mode 100644 index 04c3b12f0..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.module.scss +++ /dev/null @@ -1,67 +0,0 @@ -@import '../../../styles/theme.scss'; -@import '../../../../../../common/src/ui/styles/abstracts/_typography.scss'; - -.body { - display: flex; - flex-direction: column; - gap: size_unit(3); - background-color: var(--light-mode-light-grey, var(--dark-mode-grey, #f9f9f9)); - border-radius: size_unit(2); - padding: size_unit(2); -} - -.detail { - display: flex; - justify-content: space-between; - align-items: baseline; - - > * { - display: flex; - flex: 0 1 50%; - min-width: 0; - } - - .title { - font-weight: 500; - font-size: var(--body); - line-height: size_unit(3); - /* or 150% */ - /* Secondary - Black */ - color: var(--text-color-primary); - text-align: right; - } - .value { - display: flex; - align-items: flex-end; - flex-direction: column; - gap: size_unit(2); - - font-size: var(--bodySmall); - font-weight: 500; - line-height: size_unit(2); - /* Secondary - Black */ - color: var(--text-color-primary); - - .bold { - font-weight: 600; - line-height: size_unit(3); - font-size: var(--body); - word-break: break-all; - } - - .rightAligned { - text-align: right; - > div { - justify-content: flex-end; - } - div, - p { - text-align: right; - } - } - } -} - -:global(.__react_component_tooltip) { - @include tooltip-default; -} diff --git a/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx b/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx deleted file mode 100644 index 9eccd4e0e..000000000 --- a/packages/core/src/ui/components/DappTransaction/DappTxOutput/DappTxOutput.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import { Ellipsis } from '@lace/common'; -import styles from './DappTxOutput.module.scss'; -import { useTranslate } from '@src/ui/hooks'; -import { Wallet } from '@lace/cardano'; - -export interface DappTxOutputProps { - coins: string; - recipient: string; - assets?: Wallet.Cip30SignTxAssetItem[]; -} - -export const DappTxOutput = ({ recipient, coins, assets }: DappTxOutputProps): React.ReactElement => { - const { t } = useTranslate(); - return ( -
-
-
- {t('package.core.dappTransaction.sending')} -
-
-
- {coins.toString()} ADA -
- {assets?.map((asset) => ( -
- {asset.amount} {asset.ticker || asset.name} -
- ))} -
-
-
-
- {t('package.core.dappTransaction.recipient')} -
-
- -
-
-
- ); -}; diff --git a/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.module.scss b/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.module.scss new file mode 100644 index 000000000..f20879be7 --- /dev/null +++ b/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.module.scss @@ -0,0 +1,14 @@ +@import '../../styles/theme.scss'; +@import '../../../../../../packages/common/src/ui/styles/abstracts/_typography'; + +.dappInfo { + font-weight: 500; + line-height: 1.7; + color: var(--text-color-light-grey) !important; +} + +.dappInfoContainer { + padding: size_unit(2); + margin: size_unit(1); + background-color: var(--data-light-grey); +} \ No newline at end of file diff --git a/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx b/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx new file mode 100644 index 000000000..0e38fc19a --- /dev/null +++ b/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import { Typography } from 'antd'; + +import styles from './DappTransactionHeader.module.scss'; +import { useTranslate } from '@src/ui/hooks'; + +import { TransactionType, SummaryExpander, Card } from '@lace/ui'; + +const { Text } = Typography; + +export enum TransactionTypes { + Withdrawal = 'withdrawal', + Receive = 'receive', + Sent = 'sent', + Send = 'send', + Sending = 'sending', + Mint = 'mint', + 'Self Transaction' = 'self' +} + +type TransactionType = keyof typeof TransactionTypes; + +export interface DappTransactionHeaderProps { + name: string; + transactionType?: TransactionType; +} + +export const DappTransactionHeader = ({ transactionType, name }: DappTransactionHeaderProps): React.ReactElement => { + const { t } = useTranslate(); + + return ( +
+ + + + + {name} + + + +
+ ); +}; diff --git a/packages/core/src/ui/components/DappTransactionHeader/index.ts b/packages/core/src/ui/components/DappTransactionHeader/index.ts new file mode 100644 index 000000000..f2108aff8 --- /dev/null +++ b/packages/core/src/ui/components/DappTransactionHeader/index.ts @@ -0,0 +1 @@ +export * from './DappTransactionHeader'; diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index 452ed9656..54da6476e 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -415,6 +415,16 @@ "recipient": "Recipient", "send": "Send", "sending": "Sending", + "toAddress": "To address", + "fromAddress": "From address", + "tokens": "Tokens", + "nfts": "NFTs", + "address": "Address", + "origin": "Origin", + "transactionSummary": "Transaction Summary", + "deposit": "Deposit", + "returnedDeposit": "Returned deposit", + "items": "item(s)", "transaction": "Transaction" }, "confirmationBanner": { diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index 3d388b5fb..9fe8c158c 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -28,7 +28,7 @@ export type ExpectedDAppDetails = { export type ExpectedTransactionData = { typeOfTransaction: string; - amountADA: string; + amountADA: number; amountAsset?: string; recipientAddress: string; }; @@ -41,23 +41,8 @@ class DAppConnectorAssert { expect(await commonDappPageElements.betaPill.getText()).to.equal(await t('core.dapp.beta')); } - async assertSeeTitleAndDappDetails(expectedTitleKey: string, expectedDappDetails: ExpectedDAppDetails) { - const currentDAppUrl = new URL(expectedDappDetails.url); - const commonDappPageElements = new CommonDappPageElements(); - await commonDappPageElements.pageTitle.waitForDisplayed(); - expect(await commonDappPageElements.pageTitle.getText()).to.equal(await t(expectedTitleKey)); - await commonDappPageElements.dAppLogo.waitForDisplayed({ reverse: !expectedDappDetails.hasLogo }); - await commonDappPageElements.dAppName.waitForDisplayed(); - expect(await commonDappPageElements.dAppName.getText()).to.equal(expectedDappDetails.name); - await commonDappPageElements.dAppUrl.waitForDisplayed(); - const expectedUrl = `${currentDAppUrl.protocol}//${currentDAppUrl.host}`; - expect(await commonDappPageElements.dAppUrl.getText()).to.equal(expectedUrl); - } - - async assertSeeAuthorizeDAppPage(expectedDappDetails: ExpectedDAppDetails) { + async assertSeeAuthorizeDAppPage() { await this.assertSeeHeader(); - await this.assertSeeTitleAndDappDetails('dapp.connect.header', expectedDappDetails); - await AuthorizeDAppPage.banner.container.waitForDisplayed(); await AuthorizeDAppPage.banner.icon.waitForDisplayed(); await AuthorizeDAppPage.banner.description.waitForDisplayed(); @@ -72,7 +57,6 @@ class DAppConnectorAssert { async assertSeeCollateralDAppPage(expectedDappDetails: ExpectedDAppDetails) { await this.assertSeeHeader(); - await this.assertSeeTitleAndDappDetails('dapp.collateral.set.header', expectedDappDetails); await CollateralDAppPage.modalDescription.waitForDisplayed(); const currentDAppUrl = new URL(expectedDappDetails.url); @@ -275,49 +259,25 @@ class DAppConnectorAssert { } } - async assertSeeConfirmTransactionPage( - expectedDApp: ExpectedDAppDetails, - expectedTransactionData: ExpectedTransactionData - ) { + async assertSeeConfirmTransactionPage(expectedTransactionData: ExpectedTransactionData) { await this.assertSeeHeader(); - await this.assertSeeTitleAndDappDetails('dapp.confirm.header', expectedDApp); await ConfirmTransactionPage.transactionTypeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionTypeTitle.getText()).to.equal( - await t('dapp.confirm.details.header') + await t('package.core.dappTransaction.transaction') ); await ConfirmTransactionPage.transactionType.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionType.getText()).to.equal(expectedTransactionData.typeOfTransaction); - await ConfirmTransactionPage.transactionAmountTitle.waitForDisplayed(); - expect(await ConfirmTransactionPage.transactionAmountTitle.getText()).to.equal( - await t('package.core.dappTransaction.sending', 'core') - ); - - await ConfirmTransactionPage.transactionAmountValue.waitForDisplayed(); - expect(await ConfirmTransactionPage.transactionAmountValue.getText()).to.equal(expectedTransactionData.amountADA); - await ConfirmTransactionPage.transactionFeeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionFeeTitle.getText()).to.equal( await t('package.core.activityDetails.transactionFee', 'core') ); - await ConfirmTransactionPage.transactionFeeTooltipIcon.waitForDisplayed(); await ConfirmTransactionPage.transactionFeeValueAda.waitForDisplayed(); - await ConfirmTransactionPage.transactionFeeValueFiat.waitForDisplayed(); + const fee = Number((await ConfirmTransactionPage.transactionFeeValueAda.getText()).split(' ')[0]); - if (expectedTransactionData.amountAsset && expectedTransactionData.amountAsset !== '0') { - await ConfirmTransactionPage.transactionAmountAsset.waitForDisplayed(); - expect(await ConfirmTransactionPage.transactionAmountAsset.getText()).to.equal( - expectedTransactionData.amountAsset - ); - } - - await ConfirmTransactionPage.transactionRecipientTitle.waitForDisplayed(); - expect(await ConfirmTransactionPage.transactionRecipientTitle.getText()).to.equal( - await t('dapp.confirm.details.recepient') - ); - expect(await ConfirmTransactionPage.transactionRecipientAddress.getText()).to.contain( - expectedTransactionData.recipientAddress.slice(-10) - ); + await ConfirmTransactionPage.transactionAmountValue.waitForDisplayed(); + const totalAdaAmount = (expectedTransactionData.amountADA - fee).toFixed(2); + expect(await ConfirmTransactionPage.transactionAmountValue.getText()).to.equal(`${totalAdaAmount} tADA`); await ConfirmTransactionPage.confirmButton.waitForDisplayed(); expect(await ConfirmTransactionPage.confirmButton.getText()).to.equal(await t('dapp.confirm.btn.confirm')); @@ -335,22 +295,6 @@ class DAppConnectorAssert { expect(await SignTransactionPage.cancelButton.getText()).to.equal(await t('dapp.confirm.btn.cancel')); } - async assertSeeSignDataConfirmTransactionPage( - expectedDApp: ExpectedDAppDetails, - expectedTransactionRecipientAddress: string - ) { - await this.assertSeeHeader(); - await this.assertSeeTitleAndDappDetails('dapp.confirm.header', expectedDApp); - - expect(await ConfirmTransactionPage.transactionRecipientAddressTitle.getText()).to.equal('Address:'); - expect(await ConfirmTransactionPage.transactionRecipientAddress.getText()).to.equal( - expectedTransactionRecipientAddress - ); - - expect(await ConfirmTransactionPage.transactionDataTitle.getText()).to.equal('Data:'); - expect(await ConfirmTransactionPage.transactionData.getText()).to.equal('fixed the bug'); - } - async assertSeeSomethingWentWrongPage() { await this.assertSeeHeader(); await ErrorDAppModal.image.waitForDisplayed(); diff --git a/packages/e2e-tests/src/assert/tokensPageAssert.ts b/packages/e2e-tests/src/assert/tokensPageAssert.ts index 2e57677cd..fe85c0d4a 100644 --- a/packages/e2e-tests/src/assert/tokensPageAssert.ts +++ b/packages/e2e-tests/src/assert/tokensPageAssert.ts @@ -145,10 +145,14 @@ class TokensPageAssert { const expectedValueRounded = Number.parseFloat(expectedValue.toFixed(2)); Logger.log(`waiting for token: ${tokenName} with value: ${expectedValueRounded}`); await browser.waitUntil( - async () => - (await TokensPage.getTokenBalanceAsFloatByName(tokenName)) === expectedValueRounded + 0.01 || - (await TokensPage.getTokenBalanceAsFloatByName(tokenName)) === expectedValueRounded - 0.01 || - (await TokensPage.getTokenBalanceAsFloatByName(tokenName)) === expectedValueRounded, + async () => { + const tokenValueAsFloat = await TokensPage.getTokenBalanceAsFloatByName(tokenName); + return ( + tokenValueAsFloat === expectedValueRounded + 0.01 || + tokenValueAsFloat === expectedValueRounded - 0.01 || + tokenValueAsFloat === expectedValueRounded + ); + }, { timeout: 120_000, interval: 3000, diff --git a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts index 772693953..e5ddbe951 100644 --- a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts +++ b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts @@ -7,72 +7,107 @@ class ConfirmTransactionPage extends CommonDappPageElements { private TRANSACTION_TYPE = '[data-testid="dapp-transaction-type"]'; private TRANSACTION_AMOUNT_TITLE = '[data-testid="dapp-transaction-amount-title"]'; private TRANSACTION_AMOUNT_VALUE = '[data-testid="dapp-transaction-amount-value"]'; - private TRANSACTION_AMOUNT_FEE_TITLE = '[data-testid="tx-amount-fee-label"]'; - private TRANSACTION_AMOUNT_FEE_TITLE_TOOLTIP_ICON = '[data-testid="tx-amount-fee-tooltip-icon"]'; - private TRANSACTION_AMOUNT_FEE_VALUE_ADA = '[data-testid="tx-amount-fee-amount"]'; - private TRANSACTION_AMOUNT_FEE_VALUE_FIAT = '[data-testid="tx-amount-fee-fiat"]'; - private TRANSACTION_AMOUNT_ASSET = '[data-testid="dapp-transaction-asset"]'; - private TRANSACTION_RECIPIENT_TITLE = '[data-testid="dapp-transaction-recipient-title"]'; - private TRANSACTION_RECIPIENT_ADDRESS_TITLE = '[data-testid="dapp-transaction-recipient-address-title"]'; - private TRANSACTION_RECIPIENT_ADDRESS = '[data-testid="dapp-transaction-recipient-address"]'; - private TRANSACTION_DATA_TITLE = '[data-testid="dapp-transaction-data-title"]'; - private TRANSACTION_DATA = '[data-testid="dapp-transaction-data"]'; + + private TRANSACTION_ORIGIN = '[data-testid="dapp-transaction-origin"]'; + + private TRANSACTION_RETURNED_DEPOSIT_TITLE = '[data-testid="tx-amount-returned-deposit-label"]'; + private TRANSACTION_RETURNED_DEPOSIT_ADA = '[data-testid="tx-amount-returned-deposit-amount"]'; + + private TRANSACTION_DEPOSIT_TITLE = '[data-testid="tx-amount-deposit-label"]'; + private TRANSACTION_DEPOSIT_ADA = '[data-testid="tx-amount-deposit-amount"]'; + + private TRANSACTION_FEE_TITLE = '[data-testid="tx-amount-fee-label"]'; + private TRANSACTION_FEE_ADA = '[data-testid="tx-amount-fee-amount"]'; + + private TRANSACTION_TO_ADDRESS_TITLE = '[data-testid="dapp-transaction-to-address-title"]'; + private TRANSACTION_TO_ADDRESS = '[data-testid="dapp-transaction-to-address-address"]'; + + private TRANSACTION_FROM_ADDRESS_TITLE = '[data-testid="dapp-transaction-from-address-title"]'; + private TRANSACTION_FROM_ADDRESS_ADDRESS = '[data-testid="dapp-transaction-from-address-address"]'; + + private TRANSACTION_AMOUNT_NFTS_TITLE = '[data-testid="dapp-transaction-nfts-title"]'; + private TRANSACTION_AMOUNT_NFTS_CONTAINER = '[data-testid="dapp-transaction-nfts-container"]'; + + private TRANSACTION_AMOUNT_TOKENS_TITLE = '[data-testid="dapp-transaction-tokens-title"]'; + private TRANSACTION_AMOUNT_TOKEN_CONTAINER = '[data-testid="dapp-transaction-token-container"]'; + private CONFIRM_BUTTON = '[data-testid="dapp-transaction-confirm"]'; private CANCEL_BUTTON = '[data-testid="dapp-transaction-cancel"]'; - get transactionTypeTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_TYPE_TITLE); + get transactionOrigin(): ChainablePromiseElement { + return $(this.TRANSACTION_ORIGIN); } - get transactionType(): ChainablePromiseElement { - return $(this.TRANSACTION_TYPE); + get transactionFeeTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_FEE_TITLE); } - get transactionAmountTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_TITLE); + get transactionFeeValueAda(): ChainablePromiseElement { + return $(this.TRANSACTION_FEE_ADA); } - get transactionAmountValue(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_VALUE); + get transactionDepositTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_DEPOSIT_TITLE); } - get transactionFeeTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_FEE_TITLE); + + get transactionDepositValueAda(): ChainablePromiseElement { + return $(this.TRANSACTION_DEPOSIT_ADA); } - get transactionFeeTooltipIcon(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_FEE_TITLE_TOOLTIP_ICON); + get transactionReturnedDepositValueAda(): ChainablePromiseElement { + return $(this.TRANSACTION_RETURNED_DEPOSIT_ADA); } - get transactionFeeValueAda(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_FEE_VALUE_ADA); + get transactionReturnedDepositTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_RETURNED_DEPOSIT_TITLE); + } + + get transactionAmountNftsTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_NFTS_TITLE); + } + + get transactionAmountNftsContainer(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_NFTS_CONTAINER); + } + + get transactionAmountTokensTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_TOKENS_TITLE); + } + + get transactionAmountTokensContainer(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_TOKEN_CONTAINER); } - get transactionFeeValueFiat(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_FEE_VALUE_FIAT); + get transactionToAddressTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_TO_ADDRESS_TITLE); } - get transactionAmountAsset(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_ASSET); + get transactionToAddress(): ChainablePromiseElement { + return $(this.TRANSACTION_TO_ADDRESS); } - get transactionRecipientTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_RECIPIENT_TITLE); + get transactionFromAddressTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_FROM_ADDRESS_TITLE); } - get transactionRecipientAddressTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_RECIPIENT_ADDRESS_TITLE); + get transactionFromAddress(): ChainablePromiseElement { + return $(this.TRANSACTION_FROM_ADDRESS_ADDRESS); } - get transactionRecipientAddress(): ChainablePromiseElement { - return $(this.TRANSACTION_RECIPIENT_ADDRESS); + get transactionTypeTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_TYPE_TITLE); + } + + get transactionType(): ChainablePromiseElement { + return $(this.TRANSACTION_TYPE); } - get transactionDataTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_DATA_TITLE); + get transactionAmountTitle(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_TITLE); } - get transactionData(): ChainablePromiseElement { - return $(this.TRANSACTION_DATA); + get transactionAmountValue(): ChainablePromiseElement { + return $(this.TRANSACTION_AMOUNT_VALUE); } get confirmButton(): ChainablePromiseElement { diff --git a/packages/e2e-tests/src/elements/newTransaction/transactionSummaryPage.ts b/packages/e2e-tests/src/elements/newTransaction/transactionSummaryPage.ts index f110eaaeb..44d11b5fb 100644 --- a/packages/e2e-tests/src/elements/newTransaction/transactionSummaryPage.ts +++ b/packages/e2e-tests/src/elements/newTransaction/transactionSummaryPage.ts @@ -74,8 +74,7 @@ class TransactionSummaryPage extends CommonDrawerElements { } async saveFeeValue() { - let feeValue = await this.transactionFeeValueAda.getText(); - feeValue = feeValue.replace('ADA', ''); + const [feeValue] = (await this.transactionFeeValueAda.getText()).split(' '); testContext.save('feeValue', feeValue); } } diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature index ed8538db1..5c8a99065 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature @@ -42,7 +42,7 @@ Feature: Analytics - Posthog - Sending - Extended View And I open and authorize test DApp with "Only once" setting And I set send to wallet address to: "WalletAnalyticsReceiveSimpleTransactionE2E" in test DApp And I click "Send ADA" "Run" button in test DApp - And I see DApp connector "Confirm transaction" page with: "3.00 ADA", "0" assets and receiving wallet "WalletAnalyticsReceiveSimpleTransactionE2E" + And I see DApp connector "Confirm transaction" page with: "-3.00" tADA - fee, "0" assets and receiving wallet "WalletAnalyticsReceiveSimpleTransactionE2E" And I set up request interception for posthog analytics request(s) When I click "Confirm" button on "Confirm transaction" page Then I validate latest analytics single event "send | transaction summary | confirm | click" diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature index 2b72cdbb8..00d416513 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature @@ -10,7 +10,7 @@ Feature: Send Transactions from Dapp - E2E And I open and authorize test DApp with "Only once" setting And I set send to wallet address to: "WalletReceiveDappTransactionE2E" in test DApp When I click "Send ADA" "Run" button in test DApp - Then I see DApp connector "Confirm transaction" page with: "3.00 ADA", "0" assets and receiving wallet "WalletReceiveDappTransactionE2E" + Then I see DApp connector "Confirm transaction" page with: "-3.00" tADA - fee, "0" assets and receiving wallet "WalletReceiveDappTransactionE2E" And I save fee value on DApp "Confirm transaction" page And I click "Confirm" button on "Confirm transaction" page And I see DApp connector "Sign transaction" page @@ -41,7 +41,7 @@ Feature: Send Transactions from Dapp - E2E And I set send to wallet address to: "WalletReceiveDappTransactionE2E" in test DApp And I click "Send Token" button in test DApp When I click "Send Token" "Run" button in test DApp - Then I see DApp connector "Confirm transaction" page with: "1.38 ADA", "2 LaceCoin2" assets and receiving wallet "WalletReceiveDappTransactionE2E" + Then I see DApp connector "Confirm transaction" page with: "-1.38" tADA - fee, "-2 LaceCoin2" assets and receiving wallet "WalletReceiveDappTransactionE2E" And I save fee value on DApp "Confirm transaction" page And I click "Confirm" button on "Confirm transaction" page And I see DApp connector "Sign transaction" page @@ -70,7 +70,7 @@ Feature: Send Transactions from Dapp - E2E And I open and authorize test DApp with "Only once" setting And I set send to wallet address to: "WalletReceiveDappTransactionE2E" in test DApp When I click "Send ADA" "Run" button in test DApp - Then I see DApp connector "Confirm transaction" page with: "3.00 ADA", "0" assets and receiving wallet "WalletReceiveDappTransactionE2E" + Then I see DApp connector "Confirm transaction" page with: "-3.00" tADA - fee, "0" assets and receiving wallet "WalletReceiveDappTransactionE2E" And I click "Confirm" button on "Confirm transaction" page And I see DApp connector "Sign transaction" page And I fill correct password diff --git a/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts b/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts index 3724d937d..e3f94b868 100644 --- a/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts +++ b/packages/e2e-tests/src/pageobject/dAppConnectorPageObject.ts @@ -102,8 +102,7 @@ class DAppConnectorPageObject { } async saveDappTransactionFeeValue() { - let feeValue = await ConfirmTransactionPage.transactionFeeValueAda.getText(); - feeValue = feeValue.replace(' ADA', '').replace('Fee: ', ''); + const [feeValue] = (await ConfirmTransactionPage.transactionFeeValueAda.getText()).split(' '); await testContext.save('feeValueDAppTx', feeValue); } diff --git a/packages/e2e-tests/src/steps/dAppConnectorSteps.ts b/packages/e2e-tests/src/steps/dAppConnectorSteps.ts index 0812d96ac..894774b0b 100755 --- a/packages/e2e-tests/src/steps/dAppConnectorSteps.ts +++ b/packages/e2e-tests/src/steps/dAppConnectorSteps.ts @@ -28,7 +28,7 @@ When(/^I open test DApp$/, async () => { Then(/^I see DApp authorization window$/, async () => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); - await DAppConnectorAssert.assertSeeAuthorizeDAppPage(testDAppDetails); + await DAppConnectorAssert.assertSeeAuthorizeDAppPage(); }); Then(/^I see DApp collateral window$/, async () => { @@ -43,7 +43,7 @@ Then(/^I see DApp insufficient funds window$/, async () => { Then(/^I see DApp authorization window in (dark|light) mode$/, async (mode: 'dark' | 'light') => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); - await DAppConnectorAssert.assertSeeAuthorizeDAppPage(testDAppDetails); + await DAppConnectorAssert.assertSeeAuthorizeDAppPage(); await CommonAssert.assertSeeThemeMode(mode); }); @@ -66,24 +66,20 @@ Then(/^I see DApp connector "Confirm transaction" page in (dark|light) mode$/, a Then(/^I see DApp connector Sign data "Confirm transaction" page$/, async () => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); - await DAppConnectorAssert.assertSeeSignDataConfirmTransactionPage( - testDAppDetails, - String(getTestWallet('TestAutomationWallet').address) - ); }); Then( - /^I see DApp connector "Confirm transaction" page with: "([^"]*)", "([^"]*)" assets and receiving wallet "([^"]*)"$/, + /^I see DApp connector "Confirm transaction" page with: "([^"]*)" tADA - fee, "([^"]*)" assets and receiving wallet "([^"]*)"$/, async (adaValue: string, assetValue: string, walletName: string) => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); const expectedTransactionData: ExpectedTransactionData = { typeOfTransaction: 'Send', - amountADA: adaValue, + amountADA: Number(adaValue), amountAsset: assetValue, recipientAddress: String(getTestWallet(walletName).address) }; - await DAppConnectorAssert.assertSeeConfirmTransactionPage(testDAppDetails, expectedTransactionData); + await DAppConnectorAssert.assertSeeConfirmTransactionPage(expectedTransactionData); } ); @@ -94,14 +90,14 @@ Then( const defaultDAppTransactionData: ExpectedTransactionData = { typeOfTransaction: 'Send', - amountADA: '3.00 ADA', + amountADA: -3, amountAsset: '0', recipientAddress: String(getTestWallet('WalletReceiveDappTransactionE2E').address) }; switch (expectedPage) { case 'Confirm transaction': - await DAppConnectorAssert.assertSeeConfirmTransactionPage(testDAppDetails, defaultDAppTransactionData); + await DAppConnectorAssert.assertSeeConfirmTransactionPage(defaultDAppTransactionData); break; case 'Something went wrong': await DAppConnectorAssert.assertSeeSomethingWentWrongPage(); diff --git a/packages/icons/raw/ada.component.svg b/packages/icons/raw/ada.component.svg new file mode 100644 index 000000000..6d1927e11 --- /dev/null +++ b/packages/icons/raw/ada.component.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/icons/src/AdaComponent.tsx b/packages/icons/src/AdaComponent.tsx new file mode 100644 index 000000000..9ef4abae3 --- /dev/null +++ b/packages/icons/src/AdaComponent.tsx @@ -0,0 +1,28 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const SvgAdaComponent = (props: SVGProps) => ( + + + + + + + + + +); +export { SvgAdaComponent as ReactComponent }; diff --git a/packages/ui/src/assets/images/dark-mode-fallback.png b/packages/ui/src/assets/images/dark-mode-fallback.png new file mode 100644 index 0000000000000000000000000000000000000000..c44198169b80b4b954c38441c8306b2363ef3dbf GIT binary patch literal 3271 zcmeHK`CHOiAN_)4SS}En=90u@n&y(4C7Sy>?wSdvqA&CQ2k-O#IzQa!-gEEg`TTI7bIv`Ne!|UBX^Z9- z005Mno$&4e0K)z=pz^Yvmh`+o06;O-8IL=C12ofnD9q=KCVyd=gvI(3QWN6s2UVSn zo{Mm*c+^GL-|z}-K#VB*^EueY=5X!n-l&q^D2;old(;CC&vW@kn?9HfDdD!*gF@yk zMjV38caq-+3M6(swq@qo5Ig3Y$XZt82->%*Czo?g2d92~rVXJz!9uU%9n zaIBM)Z=pr%jSS{=PQ`xh@jdVDU3F%U<*7aG31N9Z<@>Ifs3(g+X(td3$FrI+Sul$G zdrJ?%)TeyjVbMX?3a!4C|KWAVzTe+%w4nQABHK+5o&d7-svE{I>*R=YdjWlK#kBs* zOek7R9-O9_dU|=Sgd(crtk4^$0k`T>*U~;;PtWC`(9o&LLiJQdB7zs4{Zb{X39;#k z?bCByG<=^c1oLf*K$f4Uto0cIy=CTxv?Fo`6f8k}ci;%b(6QT8;#0T z!-neS7AHxHcs(9yZEk++4H$M7jjfiv>s29Y4|!pNr#lcn(H7JlYDFnhrC!Lo7lxrWZ9=je`lNfOL9frH za`?$I_!*9Mp<*2h$eWORL6A?|ut?*7r~mm1IHwW~?Ib-Bqg^3v<8R--jaJQ78%59d z?B!o%0!H&|i)|9Q<&g)+nGxfy4_UsqUf3-OPi@k01&pqZHD_Xa(qIha^l@3`VjBkY zBLiBy4k-cmQ&u_9T8L}~=e<13Wgzc514g@OX4RQ_d3of-OZaJ%^OZv0&L2};9DlM)?VWf_ftct(NMFE119=~{&{tI_1ED^-C+qy*BjWH`i3#h~OraxS zq(}Q)R$gvEQ`^R8&<7$3kYW2nMRLc8LYo?q!S^p`{pcgH1#UT$Va9PQ69uaR4;2Qx zb8_;J6z7L9{%!|?*--RM_W^`~V$$%-Ve^ZzvAquWj%tAQ{V_EQ4PX2Ht7@9t+SDhJ zRC?0DvXzz9&RdtSeOIV-0c`hC;DJe2Xms=d7hbdAX?y|=nl2RS{Su%CdK>feyQjB4 z0Y=fM>iiG|rX5m!yExv)A|)ZAME!JpjI^JMO5-pc1)spI;cMwZtq)dhX6MkU){`Q9LZ40CatRmh|M;HETC; zqmw|VCL0}=8bcwItN3rA$W@zG==*6SlDP6+b($2cFbu;gkCdZstcqvW6Q0G-1>TBF zrAYznlHu;-W~~A(R%uAvVUbGveOCdvnc9zOezB~w-P9y+6uaWLOCfx z(&=>Md3~xj1Ntcl+?Xo^demcy&`$(#qa3_2BlVIy%Bd5IjCFBw2?`3zdn@h^m40y2 z*P}(!flq0E&2^oOQ&(rv#$OCjPG_OS@c4z$mX?->bue5d{}rD;qsD{Xd7|@`q+_c+ zWjq{#e=|NlzA72k7|J-aXMK)MS$PrX+57dY$8Wt5vlk~9swmPGVeonDk5Grd1Ij7u zYd>@KlhXt!28o{eJjaKQyUkZfVffYW8XL*Zf2N@hCPt^7k3?lC!o zXdLjM%ar0+7HQc6b*T4^S!j^E6DC@HxAwj1PIvne9f;XIsNh+WU#KShZX1kM#~6&j zM{?Pltvxqq)E8dUc3ktib58%@q(!}ankP?P%O=2MLM8(7FP+63-;5$(;kEN$b=;YK z1(!iO?|LekC}4}xef1+R;5=9-36n2M8<%vKSX?Rtt|Ry#bKWeMy{8XQ z*n>@`ycpQKlXKCdA0Mup?5|5Ax`91kJ<#NR#i2CrjJi%);CBy8@###!WAeFxTjmD| z#PIz0NEK0ySHYgS#r50+vwKZdR(JfjBtLW^RL7F`c|{TB1miOz4^TK#lt#zO;2qtM zZ<3H;PrBHQ>t6}ueJzB(r^J4KObF&d-2w|j-Aq75KCGcmo7op{#L37dnM$wkrx?RW z4nm1}mD)TeG+YQNu|H7H)uU_8c>Tj4kvp(l33FKEiZHk`8T$PzwcCIblE9zE)593(RVa94s~@BurW z!Kag;#LW%P{LMKwquLVAuMYDI0$o#CKU)Lk4$Zs7W@e1c%WCKH)oNrerzGXQhzFvC zVzKy0T7&gIRKnV93HoU(UW zNm<#M!n$Ln!fV`iXO_u%!^OqLBkbf|;ffDK)o{re@aB}0^(GWw(xHpt`y&{o^c|em6%S3bnAs=c_!f_PxFNq%)NXJG1?|IYj>7cl-a2E)1a05ct_6 z3qX09nVBiRi)~(@#&M6izIWseW8j13%&HBz+zYrrv2^R@yRNRTF9C;;>1~(7u3VX6 zE>W@O8w~N1MD8+pnF4Zpx?`Q9+G#N(*VNS1kDiiK-->Mrkg&MC^_7^O*_m7OVVji2-EC(jO9gG7mjJ;>R$q#U^G|H^}#+qcK)htF2>WW1Nu3 zH^*4Pd*_$HCs@&0dAzte3NdP4%@x8m*|)F0{YD-j9F)pfsUolYNJspU!{MBPLk!^y zoknj6vXsEG}{2sfoa)gy^OxCMBbRM3=6|e<&hOetfWLCVx*T4rL0t4P-+MeL@5bGNq?;%p;# zKJGTU2)o3 z336p9)vN-Pi-&O|l(e5Gf6KjqNh7%ztg`g13>-?H$}9h?tYEV4L5lYLvLI$+^}-MD zYQB%O9xwdd)1dir&E5;c;u6RdirM&gIe|r^lQ(N?TI7h?wK;)d%Nsq{w+T$n{Cc#v zpEJr&-bqX`GuO;y-H{G;X5A6F_fJy-mLrRh8(N4(2y*!&|&^Nn1{4MEBiA5F)t_G+EB zIdl>)rtSd7oD+bj)!Cf4Frm&!yIu7K?@77rP`eMNO%(zHjn(aXVCcguecC_`zX1?D z;-?{wRq*!(AXFS#HG4}dY-U@&l5pR`;lOR*J&ghANa~kKR8uJ(5)d91WZl)q^qHFL zZ|XRxP!N~fM_E}H6OEx0s2Mz@jN3UGEj194z%>M-+Dqd6{DSPt;~;wlm`xN_*bP0D^;lh_)5w~Y!BcdUKp96ad zMkJsV+2d%Wm|w@0rzE@utDxxS;M-bgnsGMZPK=H90ZH;u&jgG2qA_x1vR1DxfYPQJ zH)YN(P3I>DU}ptf7U8jFROqK_Q~&#|z}+5vNG6jVK$I?33BmqwA69EhDZ6ux zD&u(eROtG`PlIzCma$v6R$`;qKD{MU)~$rQjUl|wsVo7aO}7(gJmW7|Ayv0wZ%X?# zgm&0v*o#kR-CYFzRs7>UJUkRQdtxEaS_3qO!}EM9htJ*OvOA7{GJ-5ssV27@jrI5U z4-s@|%n;843UuURwG^@aBvjmI&h5Pw;R=!-0ugUC<99+%+zv_ZG{7o?RS#xd)BF^z zo+H|3v5a0&5w2?p@tt~sce#DWUO)M><54!r6h8(#)|HxJ@fsnk(0=QM*y1tz`jz&? z*Hw;qs^!OG8ydX1udk17IU%l(ovfcYo>fBgJA{!x^BQ&`MjSkAT>-}pzVXzD_?A7m zKZR!Pm~041s26R`AS1&WNn`D9cqj0X)YR|^cysXdOKbWo1CWuNZc3BrsH1W;ZI8bC zaX`gW8RC1gONkx!v0qME{k+&xIq<`YdU?jbHHO1!{B2?FFa&>)zZH5o707-aB9ONM zdC(g|UKvcw)HL$yj!;x%GpE508f5Z8b$KKZOimm>$*8$z~?a0_>M^!$cf7&Q+ zadFYXJN&~59o39Q(e|Cdk=MEPLH_>!RUPE@IeT?q6^p_UVT#3Cbjx!`o9c7wT_;#y zwiemBdQQL0HO7XOSd3m1)X3wWTalFmyakTb5)cvrs9Td9xP4E6Hy(Rg}NBc4Ks z;AioP6|t4metBz!zoY^P3qv8f&-9SGNDUcnPMJ{6YfkR?aa?pbd0ZT3P#wR!wO%-& z(y5^=ah+$wpfc;d0X%O_?Mm$WyNsaX7kis#W@g|76pmSX zj**jWJ6Dsn3?Wa4ex?)1t{_j1MVgD1L;e*9^(?GtuRLu;du6bthLvnn_a|f_+36_T zuSE*33nSth)mZO~VfYtWQ4RNDNNVPd(kQvXL~)HIM-_|S8z)U3AnQ|8D$cZkRiQrj z35)d)|IRsVwV+2;TD-b*3HeuuVw-*VnK(oEY?&nB0Z9Z0@QgjwGdpKy_>fileZalA zQ(S{(6lqa2lQ2WAL)&)Pm4$cZiP17zk7hV?5>FS0VHtjK%vn3oOA1oV#4>P_kv`49 z8ko0)`(AFs*lrR@e2YZa6SIQ4+yXroRG5v^GEZvceBRb+IzKKN6sW}KLv(&o6!&5T|G3a}{)(y9XR}1A)yK#F1*2g)lc-GCJAca_ih>Uw0 zsHeEQhH)~EM~jj!$FTmp`0jAaEgqi@(B&+ycW-S)7s^9NNc<%9DrLEbJpAi1dDZKe z!BY6!*xmJE1&#QUrX7a}&m9aNFxkVTt3eb@`MH(!t+6HUqT1&0QvKJU;`LT{T!xA7 zO`oq~jh&<0MI)o!t&#l|w(9ltK!5+^9TZ`S!^7@Olt;IvK3~4e7M+gn40CKsaT39Z zB__QQp0}`OMl`-r0Qij~s{uO5|U>ZW51*(ceqns2PS#8Uax;YI|Nk?+q;-=%TDhP;C6V=~mM*&82*q&m;+$G0#tC z?uyKc+V;%M&1ErFsc)cZRBNM%;%^pG{2jOaRV9+XClcX`%t`AdoUsW=n(2FUd|@Mwy>e7)b4R=sbqj6;7Xf)jxqTZX zMC(B5E-f265@0Qk2wSBvTYLa#=ceBsmIRaEnC76U&ss`ePy~TNzj1hXk9G*HiJP_E mTy{!g-g0|w-5>8Re}V3I7ztN4F_@RYKVXM*##UGpY5xKK@K{*@ literal 0 HcmV?d00001 diff --git a/packages/ui/src/assets/images/token-1.png b/packages/ui/src/assets/images/token-1.png new file mode 100644 index 0000000000000000000000000000000000000000..159c0e9740a44e988ca21fe8bb0d18eea7a384f3 GIT binary patch literal 1554 zcmV+t2JQKYP)P000;W1^@s654Bdt00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPa4kXikia>%Y*e4((v^;pdxdRU4M`i@}S4#BsvF-1!88UDbjG zQ`q7Sae7+x?kPBxyR*5~saUGIjxfNvuv`G|n92{vtu!v~x`AYa7xC#VY|FlDGugo; zhTgeC=I%PWX-vM9tr{#|0kJFY`ryd)&Oq-dcZo4vJ2{5wD<3^Qltnt`^9k;GiXEX-G9*c_-*)lr!k{=vHtxB-HmY+1H+Ʋm%4`WkV{JOUf z9TelkM@P}_t4BT;1DnmFV_PFu|2~8CNEGFPG7O!aM9u?roY(-h#xKLg3XyT}90Hc~ z#sy8e13l=2LGw4>>w`U#L`UQn$}|m?n@XW=2qT>{QA(jm2Tatxx)k*let2tj+?_R2 zV@hxvwr#ndNlbnFw_@z{546%e^i| zsKKNz1@Fxd=`d-2irB67Xz+R!)CLsvo=M_5>6Ifh!^$HQ&12%TzhI>e#1BWoTnG^r z_m)g-+JQ|~&1Kp-SIr|~Jh!tMVRCL{Oq7^;=squ6)0B+8S(JDbBni0OE`vT=jID_haNcMlTlRv@ct z`0@A;2ydk?79Pnwb3RCd_C?uxo58!M1&?2?eOj zal9~c8{4ZYuu)SGj0~doP!HN)UqWFbaD|I9WAnG$RGn~H6b_kL9Ldo%ZXCRcE4wdH zCUeoFLYqQpeY+JF0S(8d(fj>r-1_Mcn7!BFf2sm(sb8QfiULVSc4JqGqVP7cP$~a{ z59kQ`RjfX;k;vKdtf1!#%Ct+rOybm~Bm#s`rKkusmSOiBD`l7$9Su(E7EhLWcU(9k zHnelYBnDcs`knRJ+ZmMH%s|Z>__IRC8R`h>8Y;m4De$a?`(Fw%ajKo->4=@8mY!Z) zF5ng7ZV-5H&nV6xiy$&>B1rS=({8 zUzkCqb0!e03}skqFoQDHY1JGpOvQ4_Dlaivo3ju$OjvXZM(7Ce{&okIqSUxlV1Y!W z_l#m%=5a}eVUC0iwdi83Dildtl(6I>&_H5)iA~nvqH*W*lo-_7XP4P000;W1^@s654Bdt00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPU9J&qzV*?E;TL*~ruPwLjrFU(i_j>MO#g(<4;hQu$_x$;N&iD5_=aj(% zNz?jnNO+GlEqIKAQ8gqn36oLEHDv`X2ASq)ul&JKz>NMpL|n+VLKIm{|DnLwH!4wH z)r$E$_p#sTM=Tn^yC*o*RzNei$9L%V?<;vMra$4qsG)O&xh)D6599nZYG3c;nRm=pyT-#jQ>0fB_pE!xn>k;3vu<@6!dyM?6!k2mOg=a za+-j%XCyDgzs|zZSPw~2h_Q+5#6be3`;2I)Jp|WZUXr!|jE1AwUtxkTB*9X-2Z&Cq z#O%yGPMz#P|M_kNNwGaXKUhY>J8!>E=cH01);xYxyp+4HY(B_=5+70$3ogQAiv$zm zaWpp6A{>qG%o+OT3fSbmoT+*&B#=A|gTa8CbH5`Ni|zEg$<8BDo*V+ewc^GRyCz?l z{E*wLHSl`9n2{mm=&B0}Xn(!EM!o^~a%7CUyt^95o*_Ozhw5?xUB|{D1b>3wP>e;_ z53*Y_91l=*zW(?mghh1DjeT|hAKL9Tm#|LLiQN(?Y zXpDirNZOkdqmF?=r@+je612CsgWpuaIp;#flZWAGK8{uYcCqsTaykj~5`JCQz`3vw zqc;@jI@N`XXU@QCwUVbu#Mt-*x$PR9KK&Lx8eYV+2Pa`Wu!+**#Q%egI5qh2lQO7O zDjF~?hr@x%$w?#<3EE#*SBG1-Ze#GP1q`ALN~IDeeFB#*{!NZE@~7+LZ?_l7ZnedKxNR<#O3}N) zFbqaVN1-4uZ&Onf9W$HF7#bR)jtUD4DI_gkVDm%1nE1~p%d+HXZqoMJ+8Qht3)9IK##{mzaLIfTK1~s?k=)x=|`4vgu_3~GQHUrr6s3EmfP*7ZJs0000P000;W1^@s654Bdt00009a7bBm000XU z000XU0RWnu7ytkO0drDELIAGL9O(c600d`2O+f$vv5yPNgj_;Mn})h7v_e&*s-TEaRpJK91zdB3Xa!Mw zQINQx^a4;MC?G8Xw_k|L5`*|KF~TgQlspwY6~cTQ73-*lV~w9+oVd z#@TAsqBo**eR7_6e*PX)S4SwT-p89cHcq*K-!vV~=7&R5*WXd%y$6r27V_`cYxV!1 zAy;!K$d7*VI@@;c;r`4rsWok=Zm60j6PY^mIh!|jZ)9`G!!Le%g4FOZ3#Nrq$PlbL z;8G~)O_`!nOQ_zU_Zw|NANhQNLZS3H3$KUo9Uo+2W|oPrt?cUG$@?FEM737MbMG4U zr@l#Z?^cSkS0S6F+}=&Q@qqo-BhV}p}CZ}s+NmJ-Qy<|(g_9v)arGf zef1^g)6-0bW6X?kU@|e0qTQ?E&Xw3=1Q{kE zi3oTy-Hz8#LzN1ZnoK!Js)`YfvRO0v&FQo38`#2`Kb~dH`idXZxHw%k3HI$ruUOnv!|c;!F(UO6Pe2B?@4R5~klE;l#;ZV`tf4-a&9uy_3$viTb8V;%H_qud)$ z)0v&Yt^3h?V@x+`MAk-`zH*+}(lq(c=I~o3W^Rt53TZ-;eP5HGyliecE(8Qrt14m= z4Rl0WXiLsh*&L&4o7|A!y33}uNoQ*!j z|JTecKi;{Cgkp1ge42aL?urOom{culW|lykc%8?K%LwZPMB(mgnfGqp<5FIr5h<@0 zSJ>)N#n8%Jx_OuR&mU4xuVOEy#Ry$wlW8vh>js{cEEgw}WNekwLV{CMv)rDU!|fNv zOYmyr@&%JTbOa%^f3^WoL|!m5Y_Fioql=S;~V z>k}6c(di)rOYBAwt1@&I@m^eRp`2B%(bu~HpI4<>bn(KT1X{G2#bO#$LQw?_UYB!^@hr2_jE_owd975!R*Ol#00J!HqG4W}*0ls0pd@Z=E5t-ErMyM6Un3ZcPQCrN-@WOwZGP)-i{3n#oMTzsEhkNE zTT3%ndUjxK?x#J`MM4x@Nw3fy@{t+8! & { + imageSrc: string | undefined; + balance: string; + tokenName?: string; + coins?: string; + testId?: string; + showImageBackground?: boolean; +}; + +const isImageBase64Encoded = (image: string): boolean => { + try { + atob(image); + + return true; + } catch { + return false; + } +}; + +export const TransactionAssets = ({ + imageSrc, + balance, + tokenName, + testId, + showImageBackground = true, + ...props +}: Readonly): JSX.Element => { + const { theme } = useThemeVariant(); + const isNegativeBalance = balance.includes('-'); + + const setThemeFallbackImagine = + theme === ThemeColorScheme.Dark ? DarkFallBack : LightFallBack; + + const getImageSource = (value: string | undefined): string => { + if (value === '' || value === undefined) { + return setThemeFallbackImagine; + } else if (value.startsWith('ipfs')) { + return value.replace('ipfs://', 'https://ipfs.io/ipfs/'); + } else if (isImageBase64Encoded(value)) { + return `data:image/png;base64,${value}`; + } else { + return value; + } + }; + + return ( +
+ + + + + + + + + {balance} {tokenName} + + + + + +
+ ); +}; diff --git a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx new file mode 100644 index 000000000..40429f2b9 --- /dev/null +++ b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx @@ -0,0 +1,56 @@ +/* eslint-disable @typescript-eslint/prefer-optional-chain */ +import React from 'react'; + +import { ReactComponent as AdaComponent } from '@lace/icons/dist/AdaComponent'; +import classNames from 'classnames'; + +import { Flex } from '../flex'; +import { Grid, Cell } from '../grid'; +import * as Typography from '../typography'; + +import * as styles from './dapp-transaction-summary.css'; + +import type { OmitClassName } from '../../types'; + +type Props = OmitClassName<'div'> & { + transactionAmount: string; + title?: string; + cardanoSymbol?: string; +}; + +export const TransactionSummary = ({ + transactionAmount, + title, + cardanoSymbol, + ...props +}: Readonly): JSX.Element => ( +
+ {title !== undefined && ( + + + {title} + + + )} +
+ + + + + + + + {transactionAmount} {cardanoSymbol} + + + + +
+
+); diff --git a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.css.ts b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.css.ts new file mode 100644 index 000000000..70e09012c --- /dev/null +++ b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.css.ts @@ -0,0 +1,102 @@ +import { sx, style } from '../../design-tokens'; + +export const transactionTypeContainer = style({ + padding: '20px 0', +}); + +export const label = sx({ + fontWeight: '$semibold', +}); + +export const positiveBalance = sx({ + color: '$dapp_transaction_summary_positive_balance_label_color', +}); + +export const negativeBalance = sx({ + color: '$dapp_transaction_summary_label', +}); + +export const txSummaryTitle = style([ + sx({ + color: '$transaction_summary_label_color', + fontWeight: '$bold', + }), + { + paddingBottom: '18px', + }, +]); + +export const coloredText = sx({ + color: '$dapp_transaction_summary_type_label_color', + fontWeight: '$bold', +}); + +export const text = style([ + sx({ + color: '$transaction_summary_label_color', + fontWeight: '$medium', + }), + { + wordBreak: 'break-all', + }, +]); + +export const secondaryText = style([ + sx({ + color: '$transaction_summary_secondary_label_color', + fontWeight: '$medium', + }), + { + wordBreak: 'break-all', + }, +]); + +export const adaIcon = style([ + sx({ + display: 'flex', + }), + { + width: '34px', + height: '26px', + }, +]); + +export const avatarRoot = style([ + sx({ + borderRadius: '$small', + }), + { + display: 'inline-flex', + alignItems: 'center', + justifyContent: 'center', + verticalAlign: 'middle', + overflow: 'hidden', + userSelect: 'none', + width: '45px', + height: '45px', + }, +]); + +export const avatarImage = style({ + width: '70%', + height: '70%', + objectFit: 'cover', + borderRadius: 'inherit', +}); + +export const txAmountContainer = style({ + padding: '10px 0px', +}); + +export const balanceDetailContainer = style({ + height: '100%', + paddingRight: '0', +}); + +export const assetsContainer = style({ + padding: '10px 0px', +}); + +export const txSummaryContainer = style({ + paddingTop: '20px', +}); diff --git a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.stories.tsx b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.stories.tsx new file mode 100644 index 000000000..d5a01c37a --- /dev/null +++ b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.stories.tsx @@ -0,0 +1,131 @@ +import React from 'react'; + +import type { Meta } from '@storybook/react'; + +import token1 from '../../assets/images/token-1.png'; +import token2 from '../../assets/images/token-2.png'; +import token3 from '../../assets/images/token-3.png'; +import { ThemeColorScheme, LocalThemeProvider } from '../../design-tokens'; +import { Box } from '../box'; +import { page, Variants, Section } from '../decorators'; +import { Flex } from '../flex'; +import { Grid, Cell } from '../grid'; + +import { TransactionAssets } from './dapp-transaction-assets.component'; +import { TransactionSummary } from './dapp-transaction-summary.component'; +import { TransactionType } from './dapp-transaction-type.component'; + +const subtitle = `Control that displays data items in rows.`; + +export default { + title: 'List & tables/DApp transaction summary', + subcomponents: { TransactionSummary, TransactionType }, + decorators: [page({ title: 'Dapp transaction summary', subtitle })], +} as Meta; + +const Layout = ({ + children, +}: Readonly<{ children: React.ReactNode }>): JSX.Element => ( + + + {children} + + +); + +const items = [ + { + imageSrc: token1, + balance: '-200.00', + tokenName: 'Maui', + recipient: '', + }, + { + imageSrc: '', + balance: '-10.00', + tokenName: 'HairMaui', + recipient: '', + }, + { + imageSrc: token2, + balance: '1000.00', + tokenName: 'Lapisluzzz', + recipient: '', + }, + { + imageSrc: token3, + balance: '-1078.00', + tokenName: 'HawaiSand', + metadataHash: '3430008', + recipient: '', + }, + { + imageSrc: '', + balance: '-20780.00', + tokenName: 'HelloSand', + recipient: '', + }, +]; + +const Example = (): JSX.Element => ( + + + + {items.map(value => ( + + ))} + +); + +const MainComponents = (): JSX.Element => ( + + + + + + <> + {items.map(value => ( + + ))} + + + + +); + +export const Overview = (): JSX.Element => ( + + +
+ +
+ +
+ + + + + + + + +
+
+
+); diff --git a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-type.component.tsx b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-type.component.tsx new file mode 100644 index 000000000..b43204b3f --- /dev/null +++ b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-type.component.tsx @@ -0,0 +1,57 @@ +import React from 'react'; + +import { Flex } from '../flex'; +import { Grid, Cell } from '../grid'; +import * as Typography from '../typography'; + +import * as cx from './dapp-transaction-summary.css'; + +import type { OmitClassName } from '../../types'; + +export enum TransactionTypes { + Withdrawal = 'withdrawal', + Receive = 'receive', + Sent = 'sent', + Send = 'send', + Sending = 'sending', + Mint = 'mint', + 'Self Transaction' = 'self', +} + +type TransactionType = keyof typeof TransactionTypes; + +type Props = OmitClassName<'div'> & { + label: string; + transactionType: TransactionType; +}; + +export const TransactionType = ({ + label, + transactionType, + ...props +}: Readonly): JSX.Element => { + return ( +
+ + + + {label} + + + + + + {transactionType} + + + + +
+ ); +}; diff --git a/packages/ui/src/design-system/dapp-transaction-summary/index.ts b/packages/ui/src/design-system/dapp-transaction-summary/index.ts new file mode 100644 index 000000000..24037ad5d --- /dev/null +++ b/packages/ui/src/design-system/dapp-transaction-summary/index.ts @@ -0,0 +1,3 @@ +export { TransactionType } from './dapp-transaction-type.component'; +export { TransactionSummary } from './dapp-transaction-summary.component'; +export { TransactionAssets } from './dapp-transaction-assets.component'; diff --git a/packages/ui/src/design-system/grid/grid.component.tsx b/packages/ui/src/design-system/grid/grid.component.tsx index 55691561f..960008b55 100644 --- a/packages/ui/src/design-system/grid/grid.component.tsx +++ b/packages/ui/src/design-system/grid/grid.component.tsx @@ -9,13 +9,15 @@ export type Props = PropsWithChildren<{ columns?: GridVariants['columns']; rows?: GridVariants['rows']; gutters?: GridVariants['gutters']; + alignItems?: GridVariants['alignItems']; }>; export const Grid = ({ columns = '$none', children, rows = '$none', + alignItems, gutters = '$16', }: Readonly): JSX.Element => ( -
{children}
+
{children}
); diff --git a/packages/ui/src/design-system/grid/grid.css.ts b/packages/ui/src/design-system/grid/grid.css.ts index a9f236f8f..e9c1c7e6e 100644 --- a/packages/ui/src/design-system/grid/grid.css.ts +++ b/packages/ui/src/design-system/grid/grid.css.ts @@ -83,6 +83,23 @@ export const grid = recipe({ $20: sx({ gap: '$20' }), $32: sx({ gap: '$32' }), }, + alignItems: { + $center: { + alignItems: 'center', + }, + $fleStart: { + alignItems: 'flex-start', + }, + $flexEnd: { + alignItems: 'flex-end', + }, + $baseline: { + alignItems: 'baseline', + }, + $stretch: { + alignItems: 'stretch', + }, + }, }, defaultVariants: { diff --git a/packages/ui/src/design-system/index.ts b/packages/ui/src/design-system/index.ts index 5498d4b91..dcc11a288 100644 --- a/packages/ui/src/design-system/index.ts +++ b/packages/ui/src/design-system/index.ts @@ -46,6 +46,10 @@ export type { export { SelectGroup } from './select'; export { ActionCard } from './action-card'; export { Loader } from './loader'; +export { TransactionType } from './dapp-transaction-summary'; +export { TransactionSummary as DappTransactionSummary } from './dapp-transaction-summary'; +export { TransactionAssets } from './dapp-transaction-summary'; +export { SummaryExpander } from './summary-expander'; export * from './auto-suggest-box'; export * from './table'; export { InfoBar } from './info-bar'; diff --git a/packages/ui/src/design-system/profile-picture/user-profile.component.tsx b/packages/ui/src/design-system/profile-picture/user-profile.component.tsx index 1980ab990..9d858ad1f 100644 --- a/packages/ui/src/design-system/profile-picture/user-profile.component.tsx +++ b/packages/ui/src/design-system/profile-picture/user-profile.component.tsx @@ -13,6 +13,7 @@ interface Props { alt?: string; delayMs?: number; radius?: 'circle' | 'rounded'; + background?: 'none'; } export const UserProfile = ({ @@ -21,11 +22,13 @@ export const UserProfile = ({ alt, delayMs = 600, radius = 'circle', + background, }: Readonly): JSX.Element => ( diff --git a/packages/ui/src/design-system/profile-picture/user-profile.css.ts b/packages/ui/src/design-system/profile-picture/user-profile.css.ts index 51511fcf6..70355d535 100644 --- a/packages/ui/src/design-system/profile-picture/user-profile.css.ts +++ b/packages/ui/src/design-system/profile-picture/user-profile.css.ts @@ -25,6 +25,10 @@ export const circle = sx({ borderRadius: '$circle', }); +export const noBackground = style({ + background: 'none', +}); + export const image = style({ width: '100%', height: '100%', diff --git a/packages/ui/src/design-system/summary-expander/index.ts b/packages/ui/src/design-system/summary-expander/index.ts new file mode 100644 index 000000000..24df5614c --- /dev/null +++ b/packages/ui/src/design-system/summary-expander/index.ts @@ -0,0 +1 @@ +export { SummaryExpander } from './summary-expander.component'; diff --git a/packages/ui/src/design-system/transaction-summary/transaction-summary-amount.component.tsx b/packages/ui/src/design-system/transaction-summary/transaction-summary-amount.component.tsx index d6bc56c3d..cccad9650 100644 --- a/packages/ui/src/design-system/transaction-summary/transaction-summary-amount.component.tsx +++ b/packages/ui/src/design-system/transaction-summary/transaction-summary-amount.component.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { ReactComponent as InfoIcon } from '@lace/icons/dist/InfoComponent'; +import classNames from 'classnames'; import { Box } from '../box'; import { Flex } from '../flex'; @@ -16,8 +17,11 @@ type Props = OmitClassName<'div'> & { label?: string; tooltip?: string; amount: string; - fiatPrice: string; + fiatPrice?: string; 'data-testid'?: string; + className?: string; + displayFiat?: boolean; + highlightPositiveAmount?: boolean; }; const makeTestId = (namespace = '', path = ''): string | undefined => { @@ -29,50 +33,61 @@ export const Amount = ({ amount, fiatPrice, tooltip, + className, + displayFiat = true, + highlightPositiveAmount, ...props }: Readonly): JSX.Element => { const testId = props['data-testid']; - + const shouldHighlightPositiveAmount = + highlightPositiveAmount === true && !amount.includes('-'); return ( - - - - - {label} - - {tooltip !== undefined && ( - - -
- -
-
-
- )} -
-
- - - - {amount} - - - {fiatPrice} - - - -
+
+ + + + + {label} + + {tooltip !== undefined && ( + + +
+ +
+
+
+ )} +
+
+ + + + {amount} + + {displayFiat && ( + + {fiatPrice} + + )} + + +
+
); }; diff --git a/packages/ui/src/design-system/transaction-summary/transaction-summary.css.ts b/packages/ui/src/design-system/transaction-summary/transaction-summary.css.ts index 995421544..93b18eb18 100644 --- a/packages/ui/src/design-system/transaction-summary/transaction-summary.css.ts +++ b/packages/ui/src/design-system/transaction-summary/transaction-summary.css.ts @@ -2,13 +2,13 @@ import { sx, style } from '../../design-tokens'; export const label = sx({ color: '$transaction_summary_label_color', - fontWeight: '$semibold', + fontWeight: '$bold', }); export const text = style([ sx({ color: '$transaction_summary_label_color', - fontWeight: '$medium', + fontWeight: '$semibold', }), { wordBreak: 'break-all', @@ -18,7 +18,7 @@ export const text = style([ export const secondaryText = style([ sx({ color: '$transaction_summary_secondary_label_color', - fontWeight: '$medium', + fontWeight: '$semibold', }), { wordBreak: 'break-all', @@ -39,3 +39,15 @@ export const tooltipText = style([ display: 'flex', }), ]); + +export const normalAmount = style([ + sx({ + color: '$transaction_summary_amount_color', + }), +]); + +export const highlightedAmount = style([ + sx({ + color: '$transaction_summary_highlighted_amount_color', + }), +]); diff --git a/packages/ui/src/design-system/transaction-summary/transaction-summary.stories.tsx b/packages/ui/src/design-system/transaction-summary/transaction-summary.stories.tsx index 46b4c49d8..33f5e137b 100644 --- a/packages/ui/src/design-system/transaction-summary/transaction-summary.stories.tsx +++ b/packages/ui/src/design-system/transaction-summary/transaction-summary.stories.tsx @@ -55,9 +55,6 @@ const Example = (): JSX.Element => ( - - - ( data-testid="sample" /> + + + diff --git a/packages/ui/src/design-tokens/colors.data.ts b/packages/ui/src/design-tokens/colors.data.ts index a77f4e07c..4c9ef372b 100644 --- a/packages/ui/src/design-tokens/colors.data.ts +++ b/packages/ui/src/design-tokens/colors.data.ts @@ -161,7 +161,13 @@ export const colors = { $summary_expander_trigger_label_color_pressed: '', $transaction_summary_label_color: '', + $transaction_summary_amount_color: '', + $transaction_summary_highlighted_amount_color: '', $transaction_summary_secondary_label_color: '', + $dapp_transaction_summary_positive_balance_label_color: '', + + $dapp_transaction_summary_type_label_color: '', + $dapp_transaction_summary_label: '', $toast_bar_container_bgColor: '', $toast_bar_label_color: '', diff --git a/packages/ui/src/design-tokens/theme/dark-theme.css.ts b/packages/ui/src/design-tokens/theme/dark-theme.css.ts index 65017e510..159d6e1b5 100644 --- a/packages/ui/src/design-tokens/theme/dark-theme.css.ts +++ b/packages/ui/src/design-tokens/theme/dark-theme.css.ts @@ -232,8 +232,17 @@ const colors: Colors = { darkColorScheme.$primary_mid_grey, $transaction_summary_label_color: darkColorScheme.$primary_white, + $transaction_summary_amount_color: darkColorScheme.$primary_white, + $transaction_summary_highlighted_amount_color: + darkColorScheme.$secondary_data_green, $transaction_summary_secondary_label_color: darkColorScheme.$primary_light_grey, + $dapp_transaction_summary_positive_balance_label_color: + darkColorScheme.$secondary_data_green, + + $dapp_transaction_summary_type_label_color: + darkColorScheme.$primary_accent_purple, + $dapp_transaction_summary_label: darkColorScheme.$primary_white, $toast_bar_container_bgColor: darkColorScheme.$primary_dark_grey, $toast_bar_label_color: darkColorScheme.$primary_white, diff --git a/packages/ui/src/design-tokens/theme/hooks/use-theme-variant.tsx b/packages/ui/src/design-tokens/theme/hooks/use-theme-variant.tsx new file mode 100644 index 000000000..9dfa42797 --- /dev/null +++ b/packages/ui/src/design-tokens/theme/hooks/use-theme-variant.tsx @@ -0,0 +1,11 @@ +import { useContext } from 'react'; + +import { ThemeContext } from '../theme.context'; + +import type { ThemeColorScheme } from '../theme.context'; + +export const useThemeVariant = (): { theme: ThemeColorScheme } => { + const themeContext = useContext(ThemeContext); + + return { theme: themeContext.colorScheme }; +}; diff --git a/packages/ui/src/design-tokens/theme/light-theme.css.ts b/packages/ui/src/design-tokens/theme/light-theme.css.ts index 50f553764..8ba4cc1f0 100644 --- a/packages/ui/src/design-tokens/theme/light-theme.css.ts +++ b/packages/ui/src/design-tokens/theme/light-theme.css.ts @@ -252,8 +252,17 @@ const colors: Colors = { lightColorScheme.$primary_light_grey_plus, $transaction_summary_label_color: lightColorScheme.$primary_black, + $transaction_summary_amount_color: lightColorScheme.$primary_black, + $transaction_summary_highlighted_amount_color: + lightColorScheme.$secondary_data_green, $transaction_summary_secondary_label_color: lightColorScheme.$primary_dark_grey, + $dapp_transaction_summary_positive_balance_label_color: + lightColorScheme.$secondary_data_green, + + $dapp_transaction_summary_type_label_color: + lightColorScheme.$primary_accent_purple, + $dapp_transaction_summary_label: lightColorScheme.$primary_dark_grey, $toast_bar_container_bgColor: lightColorScheme.$primary_white, $toast_bar_label_color: lightColorScheme.$primary_black, From ec6072d1921abbfcdc42e05370eaa30a4aec3512 Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Thu, 28 Mar 2024 10:55:18 +0100 Subject: [PATCH 05/74] chore(staking): fix chromatic deployment for staking (#1002) --- packages/staking/.storybook/main.ts | 4 ++++ packages/staking/package.json | 1 + yarn.lock | 8 ++++++++ 3 files changed, 13 insertions(+) diff --git a/packages/staking/.storybook/main.ts b/packages/staking/.storybook/main.ts index a9567ece8..8890b21ee 100644 --- a/packages/staking/.storybook/main.ts +++ b/packages/staking/.storybook/main.ts @@ -5,6 +5,7 @@ import { mergeConfig, UserConfig } from 'vite'; import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'; import svgrPlugin from 'vite-plugin-svgr'; import tsconfigPaths from 'vite-tsconfig-paths'; +import turbosnap from 'vite-plugin-turbosnap'; import { nodePolyfills } from 'vite-plugin-node-polyfills'; // TODO to be removed when @lace/icons properly supports ESM import commonjs from 'vite-plugin-commonjs'; @@ -47,6 +48,9 @@ const config: StorybookConfig = { esbuildOptions: { loader: { '.css': 'empty' } }, }), tsconfigPaths(), + turbosnap({ + rootDir: baseConfig.root ?? process.cwd(), + }), ], resolve: { alias: [ diff --git a/packages/staking/package.json b/packages/staking/package.json index ed790bdb3..eef481fbd 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -114,6 +114,7 @@ "vite-plugin-commonjs": "^0.10.1", "vite-plugin-node-polyfills": "^0.19.0", "vite-plugin-svgr": "^4.2.0", + "vite-plugin-turbosnap": "1.0.3", "vite-tsconfig-paths": "^4.2.3", "vitest": "^0.31.0", "wait-on": "^7.0.1" diff --git a/yarn.lock b/yarn.lock index b2919de3b..1f4842bcb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10877,6 +10877,7 @@ __metadata: vite-plugin-commonjs: ^0.10.1 vite-plugin-node-polyfills: ^0.19.0 vite-plugin-svgr: ^4.2.0 + vite-plugin-turbosnap: 1.0.3 vite-tsconfig-paths: ^4.2.3 vitest: ^0.31.0 wait-on: ^7.0.1 @@ -51804,6 +51805,13 @@ __metadata: languageName: node linkType: hard +"vite-plugin-turbosnap@npm:1.0.3": + version: 1.0.3 + resolution: "vite-plugin-turbosnap@npm:1.0.3" + checksum: a66d09ecafef293e78a2bfceed133c3436556f8407c55be2796b5ed6ff6f80a85e1961e67382aa4e3a971f03fa33d7ee318706acc5a6c289bb7ce6a1a49293bc + languageName: node + linkType: hard + "vite-tsconfig-paths@npm:^4.2.3": version: 4.2.3 resolution: "vite-tsconfig-paths@npm:4.2.3" From f0d9b2977f752fc1692565c53f3468981a113c09 Mon Sep 17 00:00:00 2001 From: Lucas Date: Thu, 28 Mar 2024 09:04:05 -0300 Subject: [PATCH 06/74] fix: check for active wallet id (#990) --- .../MainMenu/DropdownMenuOverlay/components/UserInfo.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx index 549b8d93d..ab0831627 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx +++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/UserInfo.tsx @@ -47,6 +47,7 @@ export const UserInfo = ({ onOpenWalletAccounts, avatarVisible = true }: UserInf const activeWalletName = addEllipsis(fullWalletName, WALLET_NAME_MAX_LENGTH, 0); const [handle] = useGetHandles(); const handleName = handle?.nftMetadata?.name; + const activeWalletId = cardanoWallet.source.wallet.walletId; const handleOnAddressCopy = () => { toast.notify({ duration: TOAST_DEFAULT_DURATION, text: t('general.clipboard.copiedToClipboard') }); @@ -87,6 +88,9 @@ export const UserInfo = ({ onOpenWalletAccounts, avatarVisible = true }: UserInf id={`wallet-option-${wallet.walletId}`} onOpenAccountsMenu={() => onOpenWalletAccounts(wallet)} onClick={async () => { + if (activeWalletId === wallet.walletId) { + return; + } analytics.sendEventToPostHog(PostHogAction.MultiWalletSwitchWallet); await activateWallet({ @@ -103,7 +107,7 @@ export const UserInfo = ({ onOpenWalletAccounts, avatarVisible = true }: UserInf /> ); }, - [activateWallet, getLastActiveAccount, onOpenWalletAccounts, setIsDropdownMenuOpen, analytics, t] + [activateWallet, getLastActiveAccount, onOpenWalletAccounts, setIsDropdownMenuOpen, analytics, t, activeWalletId] ); const renderWallet = useCallback( From dd53753d888e50994595c4b2bb5526f1e112a4c8 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Thu, 28 Mar 2024 15:44:24 +0000 Subject: [PATCH 07/74] chore: LW-10174 update sdk (#1005) --- apps/browser-extension-wallet/package.json | 14 +- .../stores/slices/stake-pool-search-slice.ts | 6 +- packages/cardano/package.json | 18 +- packages/staking/package.json | 14 +- yarn.lock | 236 +++++++++--------- 5 files changed, 142 insertions(+), 146 deletions(-) diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 80c93435b..22380fe05 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -40,14 +40,14 @@ }, "dependencies": { "@ant-design/icons": "^4.7.0", - "@cardano-sdk/cardano-services-client": "0.17.10", - "@cardano-sdk/core": "0.29.0", - "@cardano-sdk/dapp-connector": "0.12.13", - "@cardano-sdk/input-selection": "0.12.24", - "@cardano-sdk/tx-construction": "0.18.0", + "@cardano-sdk/cardano-services-client": "0.18.0", + "@cardano-sdk/core": "0.30.0", + "@cardano-sdk/dapp-connector": "0.12.14", + "@cardano-sdk/input-selection": "0.12.25", + "@cardano-sdk/tx-construction": "0.18.1", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.0", - "@cardano-sdk/web-extension": "0.25.0", + "@cardano-sdk/wallet": "0.35.1", + "@cardano-sdk/web-extension": "0.26.0", "@emurgo/cip14-js": "~3.0.1", "@koralabs/handles-public-api-interfaces": "^1.6.6", "@lace/cardano": "0.1.0", diff --git a/apps/browser-extension-wallet/src/stores/slices/stake-pool-search-slice.ts b/apps/browser-extension-wallet/src/stores/slices/stake-pool-search-slice.ts index ea87d9976..b47adaf8e 100644 --- a/apps/browser-extension-wallet/src/stores/slices/stake-pool-search-slice.ts +++ b/apps/browser-extension-wallet/src/stores/slices/stake-pool-search-slice.ts @@ -44,11 +44,7 @@ const fetchStakePools = startAt: skip, limit: limit - skip + 1 }, - // @ts-expect-error TODO remove when ticker sort is available; https://input-output.atlassian.net/browse/LW-9981 - sort: sort && { - ...sort, - ...(sort.field === 'ticker' ? { field: 'name' } : {}) - } + sort }; const { totalResultCount, pageResults } = await get().blockchainProvider.stakePoolProvider.queryStakePools(filters); diff --git a/packages/cardano/package.json b/packages/cardano/package.json index 5eeaa8363..5ab27b610 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -39,15 +39,15 @@ "watch": "yarn build --watch" }, "dependencies": { - "@cardano-sdk/cardano-services-client": "0.17.10", - "@cardano-sdk/core": "0.29.0", - "@cardano-sdk/crypto": "0.1.21", - "@cardano-sdk/hardware-ledger": "0.8.17", - "@cardano-sdk/hardware-trezor": "0.4.17", - "@cardano-sdk/key-management": "0.20.0", + "@cardano-sdk/cardano-services-client": "0.18.0", + "@cardano-sdk/core": "0.30.0", + "@cardano-sdk/crypto": "0.1.22", + "@cardano-sdk/hardware-ledger": "0.8.18", + "@cardano-sdk/hardware-trezor": "0.4.18", + "@cardano-sdk/key-management": "0.20.1", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.0", - "@cardano-sdk/web-extension": "0.25.0", + "@cardano-sdk/wallet": "0.35.1", + "@cardano-sdk/web-extension": "0.26.0", "@lace/common": "0.1.0", "@stablelib/chacha20poly1305": "1.0.1", "bignumber.js": "9.0.1", @@ -67,7 +67,7 @@ "webextension-polyfill": "0.8.0" }, "devDependencies": { - "@cardano-sdk/util-dev": "0.19.17", + "@cardano-sdk/util-dev": "0.19.18", "@emurgo/cardano-message-signing-browser": "1.0.1", "rollup-plugin-polyfill-node": "^0.8.0", "typescript": "^4.3.5" diff --git a/packages/staking/package.json b/packages/staking/package.json index eef481fbd..2c24d3c56 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -72,9 +72,9 @@ }, "devDependencies": { "@babel/core": "^7.21.0", - "@cardano-sdk/core": "0.29.0", - "@cardano-sdk/input-selection": "0.12.24", - "@cardano-sdk/tx-construction": "0.18.0", + "@cardano-sdk/core": "0.30.0", + "@cardano-sdk/input-selection": "0.12.25", + "@cardano-sdk/tx-construction": "0.18.1", "@cardano-sdk/util": "0.15.0", "@storybook/addon-actions": "^7.6.7", "@storybook/addon-essentials": "^7.6.7", @@ -120,11 +120,11 @@ "wait-on": "^7.0.1" }, "peerDependencies": { - "@cardano-sdk/input-selection": "0.12.24", - "@cardano-sdk/tx-construction": "0.18.0", + "@cardano-sdk/input-selection": "0.12.25", + "@cardano-sdk/tx-construction": "0.18.1", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.0", - "@cardano-sdk/web-extension": "0.25.0", + "@cardano-sdk/wallet": "0.35.1", + "@cardano-sdk/web-extension": "0.26.0", "@lace/cardano": "^0.1.0", "@lace/common": "^0.1.0", "@lace/core": "0.1.0", diff --git a/yarn.lock b/yarn.lock index 1f4842bcb..efd6f939c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7675,27 +7675,27 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/cardano-services-client@npm:0.17.10": - version: 0.17.10 - resolution: "@cardano-sdk/cardano-services-client@npm:0.17.10" +"@cardano-sdk/cardano-services-client@npm:0.18.0": + version: 0.18.0 + resolution: "@cardano-sdk/cardano-services-client@npm:0.18.0" dependencies: - "@cardano-sdk/core": ~0.29.0 + "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/util": ~0.15.0 axios: ^0.27.2 class-validator: ^0.14.0 json-bigint: ~1.0.0 ts-log: ^2.2.4 - checksum: 436920c0ae81d1ba060a9b01c4fa4215d15550b34a80e4847abeef2ecd110b840674da9562eea28a61a2a7eb6d92f19ecb2d72a0b710a9e1366fb877451aa08d + checksum: f722a14121c944ecd37360525ef664da41cad3076042acc43961e4313935a4a4acbda3e3d5019979f1e14a1a3df3889e5509a453b93b99b94c8cf79c7fb2cfe7 languageName: node linkType: hard -"@cardano-sdk/core@npm:0.29.0, @cardano-sdk/core@npm:~0.29.0": - version: 0.29.0 - resolution: "@cardano-sdk/core@npm:0.29.0" +"@cardano-sdk/core@npm:0.30.0, @cardano-sdk/core@npm:~0.30.0": + version: 0.30.0 + resolution: "@cardano-sdk/core@npm:0.30.0" dependencies: "@cardano-ogmios/client": 5.6.0 "@cardano-ogmios/schema": 5.6.0 - "@cardano-sdk/crypto": ~0.1.21 + "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/util": ~0.15.0 "@foxglove/crc": ^0.0.3 "@scure/base": ^1.1.1 @@ -7710,13 +7710,13 @@ __metadata: peerDependenciesMeta: rxjs: optional: true - checksum: d7a0cf092158d5e68f2451b025ee1504342c74cdb5698f1a657ec7683d498c32a2faa7c8d222dc97e9ea9bb74659ab19e3c635ee58ce7cffe98e89de90b52f94 + checksum: 0685e62bc4ee053a616ac38168ebed20e17608f673124d3be8c3cd8f7858c9da65e157df9e254be281d4130479c662a8bd705ada831530ea244124e79e1c8d56 languageName: node linkType: hard -"@cardano-sdk/crypto@npm:0.1.21, @cardano-sdk/crypto@npm:~0.1.21": - version: 0.1.21 - resolution: "@cardano-sdk/crypto@npm:0.1.21" +"@cardano-sdk/crypto@npm:0.1.22, @cardano-sdk/crypto@npm:~0.1.22": + version: 0.1.22 + resolution: "@cardano-sdk/crypto@npm:0.1.22" dependencies: "@cardano-sdk/util": ~0.15.0 blake2b: ^2.1.4 @@ -7738,82 +7738,82 @@ __metadata: optional: true "@dcspark/cardano-multiplatform-lib-nodejs": optional: true - checksum: 6c5e552f68f8f83a142a73f735252c3b5ad2bd17a5af1e588bd617782d005203a90ded0d2290e8f0d943f50e789a48c7babfc341fac8432adadd8bec78419780 + checksum: 0d7abe149886efef4785dafd82e7e210e59ca4078d7766225a1ebdc3b880d929a9fcf209649b34e0c0dbcd8e45ca52fdd454899596431671f853447c4bddce23 languageName: node linkType: hard -"@cardano-sdk/dapp-connector@npm:0.12.13, @cardano-sdk/dapp-connector@npm:~0.12.13": - version: 0.12.13 - resolution: "@cardano-sdk/dapp-connector@npm:0.12.13" +"@cardano-sdk/dapp-connector@npm:0.12.14, @cardano-sdk/dapp-connector@npm:~0.12.14": + version: 0.12.14 + resolution: "@cardano-sdk/dapp-connector@npm:0.12.14" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/util": ~0.15.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 webextension-polyfill: ^0.8.0 - checksum: dc6c1dbf5ad411164a85f96c0f654ba3a075f9b853d2ed6c4c9c965b6b57c9ed5ce326784cb3b13a79b7da9f14aa739f948c3d4803c865f32262c75ed91d430a + checksum: 06f3b81fbca27d6a7fa8f36671eb1a8de85a422a28a663b7e19bd504d156a3c93d25976b3fd94a842f79eda222988731ae79b508a2ef4cf4be126d014e6654e7 languageName: node linkType: hard -"@cardano-sdk/hardware-ledger@npm:0.8.17, @cardano-sdk/hardware-ledger@npm:~0.8.17": - version: 0.8.17 - resolution: "@cardano-sdk/hardware-ledger@npm:0.8.17" +"@cardano-sdk/hardware-ledger@npm:0.8.18, @cardano-sdk/hardware-ledger@npm:~0.8.18": + version: 0.8.18 + resolution: "@cardano-sdk/hardware-ledger@npm:0.8.18" dependencies: "@cardano-foundation/ledgerjs-hw-app-cardano": ^6.0.0 - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/key-management": ~0.20.0 - "@cardano-sdk/tx-construction": ~0.18.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/key-management": ~0.20.1 + "@cardano-sdk/tx-construction": ~0.18.1 "@cardano-sdk/util": ~0.15.0 "@ledgerhq/hw-transport": ^6.28.1 "@ledgerhq/hw-transport-node-hid-noevents": ^6.27.12 "@ledgerhq/hw-transport-webhid": ^6.27.12 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: baebdbf0cd18cf772a4494c2158f4dac42b6bf56ccdda783870c38affb7a675e404c814beaa9f3ba3ac697a3c8e8788043eaedc14cad5a5c1ca30dab63b8a4e0 + checksum: 29010d85913440bda40f0ab0e0f0510f840007f0172e5d7d1e5951dea959d39b1c686a6f3bfef8e497f992b97b0f9bb3edcfd6e9f6e5e91a9c6a33d13f487862 languageName: node linkType: hard -"@cardano-sdk/hardware-trezor@npm:0.4.17, @cardano-sdk/hardware-trezor@npm:~0.4.17": - version: 0.4.17 - resolution: "@cardano-sdk/hardware-trezor@npm:0.4.17" +"@cardano-sdk/hardware-trezor@npm:0.4.18, @cardano-sdk/hardware-trezor@npm:~0.4.18": + version: 0.4.18 + resolution: "@cardano-sdk/hardware-trezor@npm:0.4.18" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/key-management": ~0.20.0 - "@cardano-sdk/tx-construction": ~0.18.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/key-management": ~0.20.1 + "@cardano-sdk/tx-construction": ~0.18.1 "@cardano-sdk/util": ~0.15.0 "@trezor/connect": 9.1.6 "@trezor/connect-web": 9.1.6 lodash: ^4.17.21 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 4cc4beb8717b7a7e6dcb7490197612522d8f18926d3f0b691716f185393ac17111b52e96aec8d727c734f6dd59631151ec81ae97dabaf9057e8d7d5794dd6f5f + checksum: b37d0093a499170f225699607d578cd9ad6040aa1441184c9372d41ac2da709693571f5e08ca99bbd43ecadaa15cff3575590fd5d7b6806af9991fda7c0873b3 languageName: node linkType: hard -"@cardano-sdk/input-selection@npm:0.12.24, @cardano-sdk/input-selection@npm:~0.12.24": - version: 0.12.24 - resolution: "@cardano-sdk/input-selection@npm:0.12.24" +"@cardano-sdk/input-selection@npm:0.12.25, @cardano-sdk/input-selection@npm:~0.12.25": + version: 0.12.25 + resolution: "@cardano-sdk/input-selection@npm:0.12.25" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/key-management": ~0.20.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/key-management": ~0.20.1 "@cardano-sdk/util": ~0.15.0 bignumber.js: ^9.1.1 lodash: ^4.17.21 ts-custom-error: ^3.2.0 - checksum: 0075869c61bb98a2e3aca77018fc0cadb55089577f281834d863bcdba8d2de4a3701aee1ba4470cae6848a436a9304573feb1510d9ce9856bf66968fdfce1ab0 + checksum: c035b2aad5a7a584594efc7efa99dd8bb9ecaac2c68025df730914d146a5f3334f07da2dddfe8349180d51b09229f5c599315f7fa5609b02723515478bebdec7 languageName: node linkType: hard -"@cardano-sdk/key-management@npm:0.20.0, @cardano-sdk/key-management@npm:~0.20.0": - version: 0.20.0 - resolution: "@cardano-sdk/key-management@npm:0.20.0" +"@cardano-sdk/key-management@npm:0.20.1, @cardano-sdk/key-management@npm:~0.20.1": + version: 0.20.1 + resolution: "@cardano-sdk/key-management@npm:0.20.1" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/dapp-connector": ~0.12.13 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/dapp-connector": ~0.12.14 "@cardano-sdk/util": ~0.15.0 "@emurgo/cardano-message-signing-nodejs": ^1.0.1 bip39: ^3.0.4 @@ -7824,36 +7824,36 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 5c0200558c23ba2adfd74686c1d5c855f21380ce5ca852fefcca7c3e6a661b583121783ec709c075c5d6b969d008ce93d74ae632da06ddce74424b9a4dbe1fe4 + checksum: 8a85033c0034f8bcd7f56240f03fe43c261e370deb2a44cb8485748626472084ff05003b91425ced0ecd3ca5e32af3aa7f9e980e330bdbdbc018944bc1b198ff languageName: node linkType: hard -"@cardano-sdk/tx-construction@npm:0.18.0, @cardano-sdk/tx-construction@npm:~0.18.0": - version: 0.18.0 - resolution: "@cardano-sdk/tx-construction@npm:0.18.0" +"@cardano-sdk/tx-construction@npm:0.18.1, @cardano-sdk/tx-construction@npm:~0.18.1": + version: 0.18.1 + resolution: "@cardano-sdk/tx-construction@npm:0.18.1" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/input-selection": ~0.12.24 - "@cardano-sdk/key-management": ~0.20.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/input-selection": ~0.12.25 + "@cardano-sdk/key-management": ~0.20.1 "@cardano-sdk/util": ~0.15.0 - "@cardano-sdk/util-rxjs": ~0.7.8 + "@cardano-sdk/util-rxjs": ~0.7.9 lodash: ^4.17.21 npm: ^9.3.0 rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: f568839b111df074bd3ed149e17d104eb0a46f8835c4e046833272f8b1a9523c3c38b5575b297f136f4d2e114c74ddca92207856668ca023e60b9007496bdc52 + checksum: 231e150ccaddb5d68eb91b675aff4356e5de2fe2fda518b2539baa212c93f46fd0b49e35f3015e5b7609b0b4603448de26214fa794562274c6f568b8574cc559 languageName: node linkType: hard -"@cardano-sdk/util-dev@npm:0.19.17": - version: 0.19.17 - resolution: "@cardano-sdk/util-dev@npm:0.19.17" +"@cardano-sdk/util-dev@npm:0.19.18": + version: 0.19.18 + resolution: "@cardano-sdk/util-dev@npm:0.19.18" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/key-management": ~0.20.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/key-management": ~0.20.1 "@cardano-sdk/util": ~0.15.0 "@types/dockerode": ^3.3.8 axios: ^0.27.2 @@ -7866,18 +7866,18 @@ __metadata: lodash: ^4.17.21 rxjs: ^7.4.0 ts-log: ^2.2.4 - checksum: 11718857d459052e9797144059a81353ab16b169aee45bd48fda1f7cd98d82be9577fb1d5ad5cc5c0e73b334c932928f88ed4d67ca8372883b1f17b1db1c95bb + checksum: 3502b7acea63857b4bed59e52ec583eae6bf35c5dea0733e42d3aaf989af69b25a9364b7346a54aec61d1b538d4ce4c4da9b9656cb5e5d54d06924afb5ce080e languageName: node linkType: hard -"@cardano-sdk/util-rxjs@npm:~0.7.8": - version: 0.7.8 - resolution: "@cardano-sdk/util-rxjs@npm:0.7.8" +"@cardano-sdk/util-rxjs@npm:~0.7.9": + version: 0.7.9 + resolution: "@cardano-sdk/util-rxjs@npm:0.7.9" dependencies: "@cardano-sdk/util": ~0.15.0 backoff-rxjs: ^7.0.0 rxjs: ^7.4.0 - checksum: 5370ee9177a90c19644af1caf71bdf33dad05291de918af5b7b2dacbf4874b768f124e3760595ab0b95408b5ee10c14dca8d4a6b3b29633f203a9085993c890d + checksum: 8474a90a5f7a3715e2ba333ceafbc3bffff715c11ce2b67121f29b486697064727d8dcf51a8dc954f0a35976ce31f0cc7c21beeedb7159aa5482d2ca2e7509fa languageName: node linkType: hard @@ -7895,20 +7895,20 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/wallet@npm:0.35.0, @cardano-sdk/wallet@npm:~0.35.0": - version: 0.35.0 - resolution: "@cardano-sdk/wallet@npm:0.35.0" +"@cardano-sdk/wallet@npm:0.35.1, @cardano-sdk/wallet@npm:~0.35.1": + version: 0.35.1 + resolution: "@cardano-sdk/wallet@npm:0.35.1" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/dapp-connector": ~0.12.13 - "@cardano-sdk/hardware-ledger": ~0.8.17 - "@cardano-sdk/hardware-trezor": ~0.4.17 - "@cardano-sdk/input-selection": ~0.12.24 - "@cardano-sdk/key-management": ~0.20.0 - "@cardano-sdk/tx-construction": ~0.18.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/dapp-connector": ~0.12.14 + "@cardano-sdk/hardware-ledger": ~0.8.18 + "@cardano-sdk/hardware-trezor": ~0.4.18 + "@cardano-sdk/input-selection": ~0.12.25 + "@cardano-sdk/key-management": ~0.20.1 + "@cardano-sdk/tx-construction": ~0.18.1 "@cardano-sdk/util": ~0.15.0 - "@cardano-sdk/util-rxjs": ~0.7.8 + "@cardano-sdk/util-rxjs": ~0.7.9 backoff-rxjs: ^7.0.0 bignumber.js: ^9.1.1 delay: ^5.0.0 @@ -7918,24 +7918,24 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.3 - checksum: 6ac40bba7d3860fb21e1b0090a9f4fbc67831dbb4eecb27521f0cefff999ef819d0f21106f38c9e423bf3a8c2d582d5a2531f4861ad800f4d67e84f26e746675 + checksum: d22698c52b61d1407746ea971ec825f7a86aff0ef43bbdb64ad74c01f651c15fbfd3eefcb4bd6e536c675dd70d9b05d202072dc37d61c86488fd5df4deeeb38f languageName: node linkType: hard -"@cardano-sdk/web-extension@npm:0.25.0": - version: 0.25.0 - resolution: "@cardano-sdk/web-extension@npm:0.25.0" +"@cardano-sdk/web-extension@npm:0.26.0": + version: 0.26.0 + resolution: "@cardano-sdk/web-extension@npm:0.26.0" dependencies: - "@cardano-sdk/core": ~0.29.0 - "@cardano-sdk/crypto": ~0.1.21 - "@cardano-sdk/dapp-connector": ~0.12.13 - "@cardano-sdk/hardware-ledger": ~0.8.17 - "@cardano-sdk/hardware-trezor": ~0.4.17 - "@cardano-sdk/key-management": ~0.20.0 - "@cardano-sdk/tx-construction": ~0.18.0 + "@cardano-sdk/core": ~0.30.0 + "@cardano-sdk/crypto": ~0.1.22 + "@cardano-sdk/dapp-connector": ~0.12.14 + "@cardano-sdk/hardware-ledger": ~0.8.18 + "@cardano-sdk/hardware-trezor": ~0.4.18 + "@cardano-sdk/key-management": ~0.20.1 + "@cardano-sdk/tx-construction": ~0.18.1 "@cardano-sdk/util": ~0.15.0 - "@cardano-sdk/util-rxjs": ~0.7.8 - "@cardano-sdk/wallet": ~0.35.0 + "@cardano-sdk/util-rxjs": ~0.7.9 + "@cardano-sdk/wallet": ~0.35.1 backoff-rxjs: ^7.0.0 lodash: ^4.17.21 rxjs: ^7.4.0 @@ -7943,7 +7943,7 @@ __metadata: ts-log: ^2.2.3 uuid: ^8.3.2 webextension-polyfill: ^0.8.0 - checksum: e9cf69fa5f1c4c2433e1446a8820824f8f2a0b5c96bc61ffbceda7ec983d4ca024f801d97a5ef7c4384c4dc2d3092e2d272b44cc0c50e4c0df5267ca42be7483 + checksum: 54c356067e0c63c88af6cb7131349106f71a35790826a9d60c562f2bb7af6f19330142615fa71989753a852ddbae41dccb85f1818b2c5fe72f1c8e11b668bced languageName: node linkType: hard @@ -10577,14 +10577,14 @@ __metadata: resolution: "@lace/browser-extension-wallet@workspace:apps/browser-extension-wallet" dependencies: "@ant-design/icons": ^4.7.0 - "@cardano-sdk/cardano-services-client": 0.17.10 - "@cardano-sdk/core": 0.29.0 - "@cardano-sdk/dapp-connector": 0.12.13 - "@cardano-sdk/input-selection": 0.12.24 - "@cardano-sdk/tx-construction": 0.18.0 + "@cardano-sdk/cardano-services-client": 0.18.0 + "@cardano-sdk/core": 0.30.0 + "@cardano-sdk/dapp-connector": 0.12.14 + "@cardano-sdk/input-selection": 0.12.25 + "@cardano-sdk/tx-construction": 0.18.1 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/wallet": 0.35.0 - "@cardano-sdk/web-extension": 0.25.0 + "@cardano-sdk/wallet": 0.35.1 + "@cardano-sdk/web-extension": 0.26.0 "@emurgo/cardano-message-signing-asmjs": 1.0.1 "@emurgo/cip14-js": ~3.0.1 "@koralabs/handles-public-api-interfaces": ^1.6.6 @@ -10647,16 +10647,16 @@ __metadata: version: 0.0.0-use.local resolution: "@lace/cardano@workspace:packages/cardano" dependencies: - "@cardano-sdk/cardano-services-client": 0.17.10 - "@cardano-sdk/core": 0.29.0 - "@cardano-sdk/crypto": 0.1.21 - "@cardano-sdk/hardware-ledger": 0.8.17 - "@cardano-sdk/hardware-trezor": 0.4.17 - "@cardano-sdk/key-management": 0.20.0 + "@cardano-sdk/cardano-services-client": 0.18.0 + "@cardano-sdk/core": 0.30.0 + "@cardano-sdk/crypto": 0.1.22 + "@cardano-sdk/hardware-ledger": 0.8.18 + "@cardano-sdk/hardware-trezor": 0.4.18 + "@cardano-sdk/key-management": 0.20.1 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/util-dev": 0.19.17 - "@cardano-sdk/wallet": 0.35.0 - "@cardano-sdk/web-extension": 0.25.0 + "@cardano-sdk/util-dev": 0.19.18 + "@cardano-sdk/wallet": 0.35.1 + "@cardano-sdk/web-extension": 0.26.0 "@emurgo/cardano-message-signing-browser": 1.0.1 "@lace/common": 0.1.0 "@stablelib/chacha20poly1305": 1.0.1 @@ -10817,9 +10817,9 @@ __metadata: dependencies: "@ant-design/icons": ^4.7.0 "@babel/core": ^7.21.0 - "@cardano-sdk/core": 0.29.0 - "@cardano-sdk/input-selection": 0.12.24 - "@cardano-sdk/tx-construction": 0.18.0 + "@cardano-sdk/core": 0.30.0 + "@cardano-sdk/input-selection": 0.12.25 + "@cardano-sdk/tx-construction": 0.18.1 "@cardano-sdk/util": 0.15.0 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 @@ -10883,11 +10883,11 @@ __metadata: wait-on: ^7.0.1 zustand: ^4.4.1 peerDependencies: - "@cardano-sdk/input-selection": 0.12.24 - "@cardano-sdk/tx-construction": 0.18.0 + "@cardano-sdk/input-selection": 0.12.25 + "@cardano-sdk/tx-construction": 0.18.1 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/wallet": 0.35.0 - "@cardano-sdk/web-extension": 0.25.0 + "@cardano-sdk/wallet": 0.35.1 + "@cardano-sdk/web-extension": 0.26.0 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0 From 79ee9a4338b0593b485fdf67ffaa7ed33198b428 Mon Sep 17 00:00:00 2001 From: Vanessa Rodriguez Cristobal Date: Thu, 28 Mar 2024 16:54:05 +0000 Subject: [PATCH 08/74] fix: single delegation now opens drawer on transition and fetches pools (#1003) --- .../src/features/delegation/components/DelegationContent.tsx | 4 ---- .../src/views/browser-view/features/staking/utils.ts | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/delegation/components/DelegationContent.tsx b/apps/browser-extension-wallet/src/features/delegation/components/DelegationContent.tsx index 6894c7232..b46d80e82 100644 --- a/apps/browser-extension-wallet/src/features/delegation/components/DelegationContent.tsx +++ b/apps/browser-extension-wallet/src/features/delegation/components/DelegationContent.tsx @@ -92,10 +92,6 @@ export const DelegationContent = (): React.ReactElement => { ); useEffect(() => { - const hasPersistedHwStakepool = !!localStorage.getItem('TEMP_POOLID'); - const isHardwareWalletPopupTransition = !isInMemoryWallet && hasPersistedHwStakepool; - // `hasPersistedHwStakepool` will get immidiately unset once the HW transition is over. - if (isHardwareWalletPopupTransition) return; if (searchValue?.length !== 0 && searchValue?.length < MIN_CHARS_TO_SEARCH) return; fetchStakePools({ searchString: searchValue || '', limit: MAX_ITEMS_TO_SHOW }); }, [searchValue, fetchStakePools, isInMemoryWallet, blockchainProvider]); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/utils.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/utils.ts index 47c414666..e5fa0b843 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/utils.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/utils.ts @@ -12,7 +12,7 @@ export const fetchPoolsInfo = async ({ ...(searchString && { identifier: { _condition: 'or', - values: [{ name: searchString }, { ticker: searchString }] + values: [{ name: searchString }, { ticker: searchString }, { id: Wallet.Cardano.PoolId(searchString) }] } }), pledgeMet: true, From c5aa4226b2afee04651fd7842a51d1644da54dba Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Thu, 28 Mar 2024 18:31:23 +0100 Subject: [PATCH 09/74] chore: remove build lace artifact with all features enabled (#1007) --- .github/workflows/ci.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c3ce7a99..8e13f39ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,19 +45,3 @@ jobs: github-token: ${{ secrets.COVERALLS_REPO_TOKEN }} allow-empty: true compare-ref: main - - name: Rebuild a version of Lace with feature flags enabled - shell: bash - env: - LACE_EXTENSION_KEY: ${{ secrets.MANIFEST_PUBLIC_KEY }} - POSTHOG_PRODUCTION_TOKEN_MAINNET: ${{ startsWith(github.ref, 'refs/heads/release') && secrets.POSTHOG_PRODUCTION_TOKEN_MAINNET || '' }} - POSTHOG_PRODUCTION_TOKEN_PREPROD: ${{ startsWith(github.ref, 'refs/heads/release') && secrets.POSTHOG_PRODUCTION_TOKEN_PREPROD || '' }} - POSTHOG_PRODUCTION_TOKEN_PREVIEW: ${{ startsWith(github.ref, 'refs/heads/release') && secrets.POSTHOG_PRODUCTION_TOKEN_PREVIEW || '' }} - PRODUCTION_MODE_TRACKING: ${{ startsWith(github.ref, 'refs/heads/release') && 'true' || 'false' }} - run: | - eval "set -x ; $(cat apps/browser-extension-wallet/.env.* | grep ^USE_ | sed -r 's/false/true/' | sort --unique | sed -r 's/^/export /' | tr '\n' ';') set +x" - yarn browser build - - name: Upload build - uses: actions/upload-artifact@v4 - with: - name: lace--all-feature-flags - path: apps/browser-extension-wallet/dist From b4f7c81417f84d4762e91f67cd6a1a76ce3355ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomislav=20Hora=C4=8Dek?= Date: Fri, 29 Mar 2024 14:42:43 +0100 Subject: [PATCH 10/74] chore: LW-9786 enable single pool delegation in multi-delegation view (#970) * chore: enable single pool delegation in multi-delegation view * fix: fix hw multi-delegation tx signing * fix(extension): fix broken ledger connection --- .../browser-view/features/staking/helpers.ts | 6 ++++++ .../components/HardwareWalletFlow.tsx | 5 ----- .../StakePoolConfirmationFooter.tsx | 17 +++++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts index 666bcd18d..77452c9f9 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts @@ -29,6 +29,12 @@ export const isMultidelegationSupportedByDevice = async ( ); } case WalletType.Trezor: { + // To allow checks once the app is refreshed. It won't affect the user flow + // TODO: Smarter Trezor initialization logic after onboarding revamp LW-9808 + await HardwareTrezor.TrezorKeyAgent.initializeTrezorTransport({ + manifest: Wallet.manifest, + communicationType: Wallet.KeyManagement.CommunicationType.Web + }); const trezorInfo = await HardwareTrezor.TrezorKeyAgent.checkDeviceConnection( Wallet.KeyManagement.CommunicationType.Web ); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx index ba49c4eb3..8d0edd615 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx @@ -133,11 +133,6 @@ export const HardwareWalletFlow = ({ if (enhancedAnalyticsStatus === EnhancedAnalyticsOptInStatus.OptedIn) { await analytics.sendAliasEvent(); } - - // Check if app reloading workaround can be removed with this in LW-9970 - if (connectedDevice !== WalletType.Trezor && typeof deviceConnection === 'object') { - deviceConnection.transport.close(); - } } }; diff --git a/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx b/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx index 170b6cf38..62ae3df8b 100644 --- a/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx +++ b/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx @@ -25,6 +25,7 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation submittingState: { setIsRestaking }, delegationStoreDelegationTxBuilder: delegationTxBuilder, isMultidelegationSupportedByDevice, + walletManagerExecuteWithPassword, } = useOutsideHandles(); const { isBuildingTx, stakingError } = useStakingStore(); const [isConfirmingTx, setIsConfirmingTx] = useState(false); @@ -42,7 +43,8 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation const signAndSubmitTransaction = useCallback(async () => { if (!delegationTxBuilder) throw new Error('Unable to submit transaction. The delegationTxBuilder not available'); - if (!isInMemory) { + const isMultidelegation = draftPortfolio && draftPortfolio.length > 1; + if (!isInMemory && isMultidelegation) { const isSupported = await isMultidelegationSupportedByDevice(walletType); if (!isSupported) { throw new Error('MULTIDELEGATION_NOT_SUPPORTED'); @@ -50,7 +52,7 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation } const signedTx = await delegationTxBuilder.build().sign(); await inMemoryWallet.submitTx(signedTx); - }, [delegationTxBuilder, inMemoryWallet, isInMemory, isMultidelegationSupportedByDevice, walletType]); + }, [delegationTxBuilder, inMemoryWallet, isInMemory, isMultidelegationSupportedByDevice, walletType, draftPortfolio]); const handleSubmission = useCallback(async () => { setOpenPoolsManagementConfirmationModal(null); @@ -62,7 +64,7 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation // HW-WALLET setIsConfirmingTx(true); try { - await signAndSubmitTransaction(); + await walletManagerExecuteWithPassword(signAndSubmitTransaction); setIsRestaking(currentPortfolio.length > 0); portfolioMutators.executeCommand({ type: 'HwSkipToSuccess' }); } catch (error: any) { @@ -73,7 +75,14 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation } finally { setIsConfirmingTx(false); } - }, [currentPortfolio, isInMemory, portfolioMutators, setIsRestaking, signAndSubmitTransaction]); + }, [ + currentPortfolio, + isInMemory, + portfolioMutators, + setIsRestaking, + signAndSubmitTransaction, + walletManagerExecuteWithPassword, + ]); const onClick = useCallback(async () => { analytics.sendEventToPostHog(PostHogAction.StakingManageDelegationStakePoolConfirmationNextClick); From 8dbe257cc607e4b93156c95c59dc75c0d080f459 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Tue, 2 Apr 2024 12:41:04 +0300 Subject: [PATCH 11/74] feat(extension): change ticket copy in the preferences card (#981) --- packages/staking/src/features/i18n/translations/en.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/staking/src/features/i18n/translations/en.ts b/packages/staking/src/features/i18n/translations/en.ts index bf5ca5209..8b5a1840a 100644 --- a/packages/staking/src/features/i18n/translations/en.ts +++ b/packages/staking/src/features/i18n/translations/en.ts @@ -29,7 +29,7 @@ export const en: Translations = { 'browsePools.preferencesCard.sort.pledge': 'Pledge', 'browsePools.preferencesCard.sort.ros': 'ROS', 'browsePools.preferencesCard.sort.saturation': 'Saturation', - 'browsePools.preferencesCard.sort.ticker': 'Ticker name', + 'browsePools.preferencesCard.sort.ticker': 'Ticker', 'browsePools.stakePoolGrid.notAvailable': 'N/A', 'browsePools.stakePoolGrid.selected': 'Selected', 'browsePools.stakePoolTableBrowser.addPool': 'Add pool', From a307304644a05bdbe22a63eb5b97d44dcfd13062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Tue, 2 Apr 2024 12:25:07 +0200 Subject: [PATCH 12/74] test(extension): add tests for LW-10143, LW-10147, LW-10148, LW-10149 (#994) * test(extension): add tests for LW-10143, LW-10147, LW-10148, LW-10149 * test(extension): make LW-2550 and LW-2561 more stable * test(extension): adjust timeout --- .../MoreOptionsComponentAssert.ts | 96 +++++++++++++++ .../MultidelegationPageAssert.ts | 2 +- .../src/assert/transactionDetailsAssert.ts | 2 + .../multidelegation/MoreOptionsComponent.ts | 105 ++++++++++++++++ .../multidelegation/MultidelegationPage.ts | 5 + .../elements/multidelegation/SortingOption.ts | 17 +++ .../MultiDelegationPageExtended.feature | 6 + .../AnalyticsStakingExtended.feature | 114 +++++++++--------- ...yticsStakingSwitchingPoolsExtended.feature | 61 ++++++++++ .../src/steps/multidelegationSteps.ts | 16 ++- packages/e2e-tests/src/types/staking.ts | 10 ++ .../BrowsePoolsPreferencesCard.tsx | 21 +++- .../radio-button/radio-button.component.tsx | 1 + 13 files changed, 395 insertions(+), 61 deletions(-) create mode 100644 packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts create mode 100644 packages/e2e-tests/src/elements/multidelegation/MoreOptionsComponent.ts create mode 100644 packages/e2e-tests/src/elements/multidelegation/SortingOption.ts create mode 100644 packages/e2e-tests/src/features/analytics/AnalyticsStakingSwitchingPoolsExtended.feature diff --git a/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts b/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts new file mode 100644 index 000000000..96dc80bcb --- /dev/null +++ b/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts @@ -0,0 +1,96 @@ +import MoreOptionsComponent from '../../elements/multidelegation/MoreOptionsComponent'; +import { expect } from 'chai'; +import { t } from '../../utils/translationService'; + +class MoreOptionsComponentAssert { + assertSeeMoreOptionsComponent = async (tab: 'sorting' | 'filtering') => { + await MoreOptionsComponent.moreOptionsLabel.waitForDisplayed(); + expect(await MoreOptionsComponent.moreOptionsLabel.getText()).to.equal( + await t('browsePools.preferencesCard.headers.moreOptions', 'staking') + ); + + // TODO: uncomment when USE_MULTI_DELEGATION_STAKING_FILTERS=true + // await MoreOptionsComponent.sortingToggle.waitForDisplayed(); + // expect(await MoreOptionsComponent.sortingToggle.getText()).to.equal( + // await t('browsePools.preferencesCard.headers.sorting', 'staking') + // ); + // await MoreOptionsComponent.filtersToggle.waitForDisplayed(); + // expect(await MoreOptionsComponent.filtersToggle.getText()).to.equal( + // await t('browsePools.preferencesCard.headers.filters', 'staking') + // ); + + if (tab === 'sorting') { + // TODO: uncomment when USE_MULTI_DELEGATION_STAKING_FILTERS=true + // expect(await MoreOptionsComponent.sortingToggle.getAttribute('aria-checked')).to.equal('true'); + await this.assertSeeSortingTab(); + } else { + expect(await MoreOptionsComponent.filtersToggle.getAttribute('aria-checked')).to.equal('true'); + await this.assertSeeFiltersTab(); + } + }; + + assertSeeSortingTab = async () => { + await MoreOptionsComponent.tickerOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.tickerOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.tickerOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.ticker', 'staking') + ); + await MoreOptionsComponent.saturationOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.saturationOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.saturationOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.saturation', 'staking') + ); + await MoreOptionsComponent.rosOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.rosOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.rosOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.ros', 'staking') + ); + await MoreOptionsComponent.costOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.costOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.costOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.cost', 'staking') + ); + await MoreOptionsComponent.marginOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.marginOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.marginOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.margin', 'staking') + ); + await MoreOptionsComponent.blocksOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.blocksOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.blocksOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.blocks', 'staking') + ); + await MoreOptionsComponent.pledgeOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.pledgeOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.pledgeOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.pledge', 'staking') + ); + await MoreOptionsComponent.liveStakeOption.radioButton.waitForDisplayed(); + await MoreOptionsComponent.liveStakeOption.label.waitForDisplayed(); + expect(await MoreOptionsComponent.liveStakeOption.label.getText()).to.equal( + await t('browsePools.preferencesCard.sort.liveStake', 'staking') + ); + }; + + assertSeeFiltersTab = async () => { + await MoreOptionsComponent.saturationFilterLabel.waitForDisplayed(); + expect(await MoreOptionsComponent.saturationFilterLabel.getText()).to.equal( + await t('browsePools.preferencesCard.filter.saturation', 'staking') + ); + await MoreOptionsComponent.profitMarginFilterLabel.waitForDisplayed(); + expect(await MoreOptionsComponent.profitMarginFilterLabel.getText()).to.equal( + await t('browsePools.preferencesCard.filter.profitMargin', 'staking') + ); + await MoreOptionsComponent.performanceFilterLabel.waitForDisplayed(); + expect(await MoreOptionsComponent.performanceFilterLabel.getText()).to.equal( + await t('browsePools.preferencesCard.filter.performance', 'staking') + ); + await MoreOptionsComponent.rosFilterLabel.waitForDisplayed(); + expect(await MoreOptionsComponent.rosFilterLabel.getText()).to.equal( + await t('browsePools.preferencesCard.filter.ros.title', 'staking') + ); + // TODO: add assertions for input fields when USE_MULTI_DELEGATION_STAKING_FILTERS=true + }; +} + +export default new MoreOptionsComponentAssert(); diff --git a/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts b/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts index 172180ab7..0299852de 100644 --- a/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts +++ b/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts @@ -11,7 +11,7 @@ import { StakePoolGridCard } from '../../elements/multidelegation/StakePoolGridC class MultidelegationPageAssert { assertSeeStakingOnPoolsCounter = async (poolsCount: number) => { - await MultidelegationPage.delegationCardPoolsValue.waitForClickable({ timeout: 60_000 }); + await MultidelegationPage.delegationCardPoolsValue.waitForClickable({ timeout: 120_000 }); const poolsCounter = Number(await MultidelegationPage.delegationCardPoolsValue.getText()); expect(poolsCounter).to.equal(poolsCount); }; diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index c86863d30..564ab041e 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -156,7 +156,9 @@ class TransactionsDetailsAssert { for (let i = 0; i <= rowsNumber && i < 10; i++) { await TransactionsPage.clickOnTransactionRow(i); await TransactionDetailsPage.transactionDetailsInputsDropdown.click(); + await TransactionDetailsPage.transactionDetailsInputsDropdown.waitForStable(); await TransactionDetailsPage.transactionDetailsOutputsDropdown.click(); + await TransactionDetailsPage.transactionDetailsOutputsDropdown.waitForStable(); const txDetailsInputADAValueString = await TransactionDetailsPage.transactionDetailsInputAdaAmount.getText(); const txDetailsInputADAValue = Number(txDetailsInputADAValueString.split(' ', 1)); diff --git a/packages/e2e-tests/src/elements/multidelegation/MoreOptionsComponent.ts b/packages/e2e-tests/src/elements/multidelegation/MoreOptionsComponent.ts new file mode 100644 index 000000000..bb3c104db --- /dev/null +++ b/packages/e2e-tests/src/elements/multidelegation/MoreOptionsComponent.ts @@ -0,0 +1,105 @@ +import { SortingOption } from './SortingOption'; +import { StakePoolSortingOptionType } from '../../types/staking'; + +class MoreOptionsComponent { + private MORE_OPTIONS_LABEL = '[data-testid="stake-pools-more-options-label"]'; + private SORTING_TOGGLE = '[data-testid="stake-pools-sorting-toggle"]'; + private FILTERS_TOGGLE = '[data-testid="stake-pools-filters-toggle"]'; + private SATURATION_FILTER_LABEL = '[data-testid="filter-Saturation-label"]'; + private PROFIT_MARGIN_FILTER_LABEL = '[data-testid="filter-ProfitMargin-label"]'; + private PERFORMANCE_FILTER_LABEL = '[data-testid="filter-Performance-label"]'; + private ROS_FILTER_LABEL = '[data-testid="filter-Ros-label"]'; + + get moreOptionsLabel() { + return $(this.MORE_OPTIONS_LABEL); + } + + get sortingToggle() { + return $(this.SORTING_TOGGLE); + } + + get filtersToggle() { + return $(this.FILTERS_TOGGLE); + } + + get tickerOption() { + return new SortingOption('ticker'); + } + + get saturationOption() { + return new SortingOption('saturation'); + } + + get rosOption() { + return new SortingOption('ros'); + } + + get costOption() { + return new SortingOption('cost'); + } + + get marginOption() { + return new SortingOption('margin'); + } + + get blocksOption() { + return new SortingOption('blocks'); + } + + get pledgeOption() { + return new SortingOption('pledge'); + } + + get liveStakeOption() { + return new SortingOption('liveStake'); + } + + get saturationFilterLabel() { + return $(this.SATURATION_FILTER_LABEL); + } + + get profitMarginFilterLabel() { + return $(this.PROFIT_MARGIN_FILTER_LABEL); + } + + get performanceFilterLabel() { + return $(this.PERFORMANCE_FILTER_LABEL); + } + + get rosFilterLabel() { + return $(this.ROS_FILTER_LABEL); + } + + async selectSortingOption(sortingOption: StakePoolSortingOptionType) { + switch (sortingOption) { + case 'Ticker': + await this.tickerOption.radioButton.click(); + break; + case 'Saturation': + await this.saturationOption.radioButton.click(); + break; + case 'ROS': + await this.rosOption.radioButton.click(); + break; + case 'Cost': + await this.costOption.radioButton.click(); + break; + case 'Margin': + await this.marginOption.radioButton.click(); + break; + case 'Produced blocks': + await this.blocksOption.radioButton.click(); + break; + case 'Pledge': + await this.pledgeOption.radioButton.click(); + break; + case 'Live Stake': + await this.liveStakeOption.radioButton.click(); + break; + default: + throw new Error(`Unsupported column name: ${sortingOption}`); + } + } +} + +export default new MoreOptionsComponent(); diff --git a/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts b/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts index e6986b005..d6311743d 100644 --- a/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts +++ b/packages/e2e-tests/src/elements/multidelegation/MultidelegationPage.ts @@ -12,6 +12,7 @@ import { StakePoolListColumnType } from '../../types/staking'; import { StakePoolListItem } from './StakePoolListItem'; import { StakePoolGridCard } from './StakePoolGridCard'; import StakePoolDetailsDrawer from './StakePoolDetailsDrawer'; +import MoreOptionsComponent from './MoreOptionsComponent'; class MultidelegationPage { private ACTIVITY_TAB = '[data-testid="activity-tab"]'; @@ -289,6 +290,10 @@ class MultidelegationPage { return $$(this.SELCECTED_STAKE_POOLS_IN_LIST_VIEW); } + get moreOptionsComponent(): typeof MoreOptionsComponent { + return MoreOptionsComponent; + } + async getPoolByTicker(ticker: string) { return (await this.displayedPools.find( async (item) => (await item.$(this.POOL_TICKER).getText()) === ticker diff --git a/packages/e2e-tests/src/elements/multidelegation/SortingOption.ts b/packages/e2e-tests/src/elements/multidelegation/SortingOption.ts new file mode 100644 index 000000000..86242547b --- /dev/null +++ b/packages/e2e-tests/src/elements/multidelegation/SortingOption.ts @@ -0,0 +1,17 @@ +/* eslint-disable no-undef */ +import { ChainablePromiseElement } from 'webdriverio'; + +export class SortingOption { + private readonly optionName; + constructor(optionName: 'ticker' | 'saturation' | 'ros' | 'cost' | 'margin' | 'blocks' | 'pledge' | 'liveStake') { + this.optionName = optionName; + } + + get radioButton(): ChainablePromiseElement { + return $(`#radio-btn-control-id-${this.optionName}`); + } + + get label(): ChainablePromiseElement { + return $(`#radio-btn-label-id-${this.optionName}`); + } +} diff --git a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature b/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature index 03b3bf15f..39f72ab4f 100644 --- a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature +++ b/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature @@ -205,3 +205,9 @@ Feature: Staking Page - Extended View Then I see 4 stake pool cards in a row When I resize the window to a width of: 1023 and a height of: 1080 Then I see 3 stake pool cards in a row + + @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 diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature index 113b1c48d..18070bcd5 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature @@ -1,61 +1,67 @@ -@Analytics-Staking-SwitchingPools-Extended-E2E @Analytics @Testnet @Pending -Feature: Analytics - Posthog - Switching pools - Extended View +@Staking-NonDelegatedFunds-Extended @Analytics @Testnet +Feature: Analytics - PostHog - Staking - Extended View Background: Given Wallet is synced - @LW-7868 - Scenario: Analytics - Extended View - Staking - Success screen - Close drawer - Given I set up request interception for posthog analytics request(s) - And I save token: "Cardano" balance + @LW-10147 + Scenario: Analytics - Extended View - Staking - switching between views When I navigate to Staking extended page - Then I validate latest analytics single event "staking | staking | click" - Then I see currently staking stake pool in extended mode and choose new pool as "OtherStakePool" - When I input "OtherStakePool" to the search bar - And I wait for single search result - And I click stake pool with name "OtherStakePool" - Then I validate latest analytics single event "staking | staking | stake pool | click" - Then I see drawer with "OtherStakePool" stake pool details and a button available for staking - When I click "Stake on this pool" button on stake pool details drawer - Then I validate latest analytics single event "staking | stake pool detail | stake on this pool | click" - And I click "Fine by me" button on "Switching pool?" modal - Then I validate latest analytics single event "staking | switching pool? | fine by me | click" - Then I see drawer with stakepool: "OtherStakePool" confirmation screen in extended mode - And I click "Next" button on staking confirmation drawer - Then I validate latest analytics single event "staking | manage delegation | stake pool confirmation | next | click" - And I enter correct wallet password and confirm staking - Then Switching Delegation success screen is displayed in extended mode - And I validate latest analytics multiple events: - | staking \| manage delegation \| hurray! \| view | - | staking \| manage delegation \| password confirmation \| confirm \| click | - When I click "Close" button on staking success drawer - Then I validate latest analytics single event "staking | manage delegation | hurray! | close | click" - And I validate that 8 analytics event(s) have been sent + And I open Browse pools tab + And I set up request interception for posthog analytics request(s) + And I switch to list view on "Browse pools" tab + Then I validate latest analytics single event "staking | browse pools | toggle | list view | click" + # TODO: enable when USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true by default + # When I switch to grid view on "Browse pools" tab + # Then I validate latest analytics single event "staking | browse pools | toggle | grid view | click" + And I validate that 1 analytics event(s) have been sent - @LW-7869 - Scenario: Analytics - Extended View - Staking - Success screen - Close drawer by clicking X button - Given I set up request interception for posthog analytics request(s) - And I save token: "Cardano" balance + @LW-10148 + Scenario: Analytics - Extended View - Staking - List View - click on column headers When I navigate to Staking extended page - Then I validate latest analytics single event "staking | staking | click" - Then I see currently staking stake pool in extended mode and choose new pool as "OtherStakePool" - When I input "OtherStakePool" to the search bar - And I wait for single search result - And I click stake pool with name "OtherStakePool" - Then I validate latest analytics single event "staking | staking | stake pool | click" - Then I see drawer with "OtherStakePool" stake pool details and a button available for staking - When I click "Stake on this pool" button on stake pool details drawer - Then I validate latest analytics single event "staking | stake pool detail | stake on this pool | click" - And I click "Fine by me" button on "Switching pool?" modal - Then I validate latest analytics single event "staking | switching pool? | fine by me | click" - Then I see drawer with stakepool: "OtherStakePool" confirmation screen in extended mode - And I click "Next" button on staking confirmation drawer - Then I validate latest analytics single event "staking | manage delegation | stake pool confirmation | next | click" - And I enter correct wallet password and confirm staking - Then Switching Delegation success screen is displayed in extended mode - And I validate latest analytics multiple events: - | staking \| manage delegation \| hurray! \| view | - | staking \| manage delegation \| password confirmation \| confirm \| click | - When I close the drawer by clicking close button - Then I validate latest analytics single event "staking | manage delegation | hurray! | x | click" - And I validate that 8 analytics event(s) have been sent + And I open Browse pools tab + And I switch to list view on "Browse pools" tab + And I set up request interception for posthog analytics request(s) + And I click on stake pools table "Ticker" column header + Then I validate latest analytics single event "staking | browse pools | ticker | click" + When I click on stake pools table "Saturation" column header + Then I validate latest analytics single event "staking | browse pools | saturation | click" + # TODO: Uncomment when USE_ROS_STAKING_COLUMN=true + # When I click on stake pools table "ROS" column header + # Then I validate latest analytics single event "staking | browse pools | ros | click" + When I click on stake pools table "Cost" column header + Then I validate latest analytics single event "staking | browse pools | cost | click" + When I click on stake pools table "Margin" column header + Then I validate latest analytics single event "staking | browse pools | margin | click" + When I click on stake pools table "Blocks" column header + Then I validate latest analytics single event "staking | browse pools | blocks | click" + When I click on stake pools table "Pledge" column header + Then I validate latest analytics single event "staking | browse pools | pledge | click" + When I click on stake pools table "Live Stake" column header + Then I validate latest analytics single event "staking | browse pools | live-stake | click" + And I validate that 7 analytics event(s) have been sent + + @LW-10149 + Scenario: Analytics - Extended View - Staking - More options - Sorting - select each option + When I navigate to Staking extended page + And I open Browse pools tab + And I switch to list view on "Browse pools" tab + And I set up request interception for posthog analytics request(s) + When I select "Saturation" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | saturation | click" + # TODO: Uncomment when USE_ROS_STAKING_COLUMN=true + # When I select "ROS" sorting option from "More options" component + # Then I validate latest analytics single event "staking | browse pools | more options sorting | ros | click" + When I select "Cost" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | cost | click" + When I select "Margin" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | margin | click" + When I select "Produced blocks" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | produced blocks | click" + When I select "Pledge" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | pledge | click" + When I select "Live Stake" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | live-stake | click" + And I select "Ticker" sorting option from "More options" component + Then I validate latest analytics single event "staking | browse pools | more options sorting | ticker | click" + And I validate that 7 analytics event(s) have been sent diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsStakingSwitchingPoolsExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsStakingSwitchingPoolsExtended.feature new file mode 100644 index 000000000..2bfa1abc1 --- /dev/null +++ b/packages/e2e-tests/src/features/analytics/AnalyticsStakingSwitchingPoolsExtended.feature @@ -0,0 +1,61 @@ +@Analytics-Staking-SwitchingPools-Extended-E2E @Analytics @Testnet @Pending +Feature: Analytics - Posthog - Switching pools - Extended View + + Background: + Given Wallet is synced + + @LW-7868 + Scenario: Analytics - Extended View - Staking - Success screen - Close drawer + Given I set up request interception for posthog analytics request(s) + And I save token: "Cardano" balance + When I navigate to Staking extended page + Then I validate latest analytics single event "staking | staking | click" + Then I see currently staking stake pool in extended mode and choose new pool as "OtherStakePool" + When I input "OtherStakePool" to the search bar + And I wait for single search result + And I click stake pool with name "OtherStakePool" + Then I validate latest analytics single event "staking | staking | stake pool | click" + Then I see drawer with "OtherStakePool" stake pool details and a button available for staking + When I click "Stake on this pool" button on stake pool details drawer + Then I validate latest analytics single event "staking | stake pool detail | stake on this pool | click" + And I click "Fine by me" button on "Switching pool?" modal + Then I validate latest analytics single event "staking | switching pool? | fine by me | click" + Then I see drawer with stakepool: "OtherStakePool" confirmation screen in extended mode + And I click "Next" button on staking confirmation drawer + Then I validate latest analytics single event "staking | manage delegation | stake pool confirmation | next | click" + And I enter correct wallet password and confirm staking + Then Switching Delegation success screen is displayed in extended mode + And I validate latest analytics multiple events: + | staking \| manage delegation \| hurray! \| view | + | staking \| manage delegation \| password confirmation \| confirm \| click | + When I click "Close" button on staking success drawer + Then I validate latest analytics single event "staking | manage delegation | hurray! | close | click" + And I validate that 8 analytics event(s) have been sent + + @LW-7869 + Scenario: Analytics - Extended View - Staking - Success screen - Close drawer by clicking X button + Given I set up request interception for posthog analytics request(s) + And I save token: "Cardano" balance + When I navigate to Staking extended page + Then I validate latest analytics single event "staking | staking | click" + Then I see currently staking stake pool in extended mode and choose new pool as "OtherStakePool" + When I input "OtherStakePool" to the search bar + And I wait for single search result + And I click stake pool with name "OtherStakePool" + Then I validate latest analytics single event "staking | staking | stake pool | click" + Then I see drawer with "OtherStakePool" stake pool details and a button available for staking + When I click "Stake on this pool" button on stake pool details drawer + Then I validate latest analytics single event "staking | stake pool detail | stake on this pool | click" + And I click "Fine by me" button on "Switching pool?" modal + Then I validate latest analytics single event "staking | switching pool? | fine by me | click" + Then I see drawer with stakepool: "OtherStakePool" confirmation screen in extended mode + And I click "Next" button on staking confirmation drawer + Then I validate latest analytics single event "staking | manage delegation | stake pool confirmation | next | click" + And I enter correct wallet password and confirm staking + Then Switching Delegation success screen is displayed in extended mode + And I validate latest analytics multiple events: + | staking \| manage delegation \| hurray! \| view | + | staking \| manage delegation \| password confirmation \| confirm \| click | + When I close the drawer by clicking close button + Then I validate latest analytics single event "staking | manage delegation | hurray! | x | click" + And I validate that 8 analytics event(s) have been sent diff --git a/packages/e2e-tests/src/steps/multidelegationSteps.ts b/packages/e2e-tests/src/steps/multidelegationSteps.ts index 6e47cc8ac..816ab2d70 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 } from '../types/staking'; +import { StakePoolListColumnType, StakePoolSortingOptionType } from '../types/staking'; import SwitchingStakePoolModal from '../elements/staking/SwitchingStakePoolModal'; import OnboardingPageObject from '../pageobject/onboardingPageObject'; +import MoreOptionsComponentAssert from '../assert/multidelegation/MoreOptionsComponentAssert'; Given(/^I open (Overview|Browse pools) tab$/, async (tabToClick: 'Overview' | 'Browse pools') => { await MultidelegationPage.openTab(tabToClick); @@ -498,3 +499,16 @@ When( await MultidelegationPage.clickOnColumnWithName(headerName); } ); + +When( + /^I select "(Ticker|Saturation|ROS|Cost|Margin|Produced blocks|Pledge|Live Stake)" sorting option from "More options" component$/, + async (sortingOption: StakePoolSortingOptionType) => { + 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); + } +); diff --git a/packages/e2e-tests/src/types/staking.ts b/packages/e2e-tests/src/types/staking.ts index 1a31cc040..be72dc088 100644 --- a/packages/e2e-tests/src/types/staking.ts +++ b/packages/e2e-tests/src/types/staking.ts @@ -7,3 +7,13 @@ export type StakePoolListColumnType = | 'Blocks' | 'Pledge' | 'Live Stake'; + +export type StakePoolSortingOptionType = + | 'Ticker' + | 'Saturation' + | 'ROS' + | 'Cost' + | 'Margin' + | 'Produced blocks' + | 'Pledge' + | 'Live Stake'; diff --git a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx index c99700a5c..80a459a83 100644 --- a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx +++ b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx @@ -111,6 +111,7 @@ export const BrowsePoolsPreferencesCard = ({ label={opt} value={localFilters[filterOption.key][idx]} onChange={(e) => handleFilterChange(filterOption.key, idx, e.target.value)} + data-testid={`filter-${filterOption.key}-${opt.toLowerCase()}`} /> ))}
@@ -274,13 +275,15 @@ export const BrowsePoolsPreferencesCard = ({ return ( - {t('browsePools.preferencesCard.headers.moreOptions')} + + {t('browsePools.preferencesCard.headers.moreOptions')} + {USE_MULTI_DELEGATION_STAKING_FILTERS && ( onTabChange(value as SortAndFilterTab)}> - + {t('browsePools.preferencesCard.headers.sorting')} - + {t('browsePools.preferencesCard.headers.filters')} @@ -295,8 +298,16 @@ export const BrowsePoolsPreferencesCard = ({ ) : ( {filterOptions.map((filterOption) => ( - - {filterOption.title} + + + {filterOption.title} + {getFilters(filterOption)} ))} diff --git a/packages/ui/src/design-system/radio-button/radio-button.component.tsx b/packages/ui/src/design-system/radio-button/radio-button.component.tsx index 2cfc5e941..b500ffd1e 100644 --- a/packages/ui/src/design-system/radio-button/radio-button.component.tsx +++ b/packages/ui/src/design-system/radio-button/radio-button.component.tsx @@ -86,6 +86,7 @@ export const RadioButtonGroup = ({ disabled={disabled} onClick={onIconClick} tabIndex={-1} + id={`radio-btn-sorting-id-${value}`} > From c3ecbd065ed33234fec7c8dc12179d23fc93b994 Mon Sep 17 00:00:00 2001 From: Vanessa Rodriguez Cristobal Date: Tue, 2 Apr 2024 12:37:05 +0100 Subject: [PATCH 13/74] fix: hardware wallet now checks for connection before trying to unlock (#1006) --- .../MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx index bad857a6d..de922d87e 100644 --- a/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx +++ b/apps/browser-extension-wallet/src/components/MainMenu/DropdownMenuOverlay/components/WalletAccounts.tsx @@ -2,6 +2,7 @@ import React, { useCallback, useMemo } from 'react'; import { useTranslation, Trans } from 'react-i18next'; import { NavigationButton, PostHogAction, toast } from '@lace/common'; +import { Wallet } from '@lace/cardano'; import styles from './WalletAccounts.module.scss'; import { ProfileDropdown } from '@lace/ui'; import { AccountData } from '@lace/ui/dist/design-system/profile-dropdown/accounts/profile-dropdown-accounts-list.component'; @@ -145,6 +146,8 @@ export const WalletAccounts = ({ isPopup, onBack }: { isPopup: boolean; onBack: const name = defaultAccountName(accountIndex); try { const timeout = setTimeout(showHWErrorState, HW_CONNECT_TIMEOUT_MS); + if (wallet.type === WalletType.InMemory) return; + await Wallet.connectDevice(wallet.type); await addAccount({ wallet, accountIndex, From 641c66030eef2c98d5920fe9b5fb61357791fb4a Mon Sep 17 00:00:00 2001 From: John Oshalusi Date: Tue, 2 Apr 2024 12:50:36 +0100 Subject: [PATCH 14/74] fix: improve mnemonic check in create flow (#992) --- .../WalletSetupMnemonicStepRevamp.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupMnemonicStepRevamp/WalletSetupMnemonicStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupMnemonicStepRevamp/WalletSetupMnemonicStepRevamp.tsx index 7183b95a1..a72833bcf 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupMnemonicStepRevamp/WalletSetupMnemonicStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupMnemonicStepRevamp/WalletSetupMnemonicStepRevamp.tsx @@ -11,6 +11,7 @@ import { Dialog } from '@lace/ui'; import { MnemonicWordsConfirmInputRevamp } from './MnemonicWordsConfirmInputRevamp'; import { Wallet } from '@lace/cardano'; import { readMnemonicFromClipboard, writeMnemonicToClipboard } from './wallet-utils'; +import isEqual from 'lodash/isEqual'; export type MnemonicStage = 'writedown' | 'input'; @@ -118,7 +119,8 @@ export const WalletSetupMnemonicStepRevamp = ({ const isSubmitEnabled = mnemonicStage === 'writedown' || - Wallet.KeyManagement.util.validateMnemonic(Wallet.KeyManagement.util.joinMnemonicWords(mnemonicConfirm)); + (Wallet.KeyManagement.util.validateMnemonic(Wallet.KeyManagement.util.joinMnemonicWords(mnemonicConfirm)) && + isEqual(mnemonic, mnemonicConfirm)); return ( <> From 1045749eb5c738b890cac0b09857332894f0d118 Mon Sep 17 00:00:00 2001 From: Lukasz Jagiela <12641433+ljagiela@users.noreply.github.com> Date: Tue, 2 Apr 2024 19:04:05 +0200 Subject: [PATCH 15/74] test(extension): test maintenance 2 April - fix analytics tests (#1013) --- .../features/analytics/AnalyticsSettingsExtended.feature | 8 +++++--- .../src/features/analytics/AnalyticsSettingsPopup.feature | 7 ++++--- packages/e2e-tests/src/hooks/beforeTagHooks.ts | 4 ++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature index 2b2700a54..74de55cab 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature @@ -1,4 +1,4 @@ -@Settings-Extended @Analytics +@Analytics-Settings-Extended @Analytics Feature: Analytics - Settings - Extended View Background: @@ -37,6 +37,7 @@ Feature: Analytics - Settings - Extended View Then I validate latest analytics single event "settings | authorized dapps | trash bin icon | click" When I click "Back" button in DApp removal confirmation modal Then I validate latest analytics single event "settings | authorized dapps | hold up! | back | click" + And I de-authorize all DApps in extended mode @LW-8551 Scenario: Analytics - Extended view - Settings - Authorized dapps events - Disconnect Dapp @@ -168,8 +169,9 @@ Feature: Analytics - Settings - Extended View Then I validate latest analytics single event "settings | theme | light mode | click" Then I validate that 2 analytics event(s) have been sent - # this test should be executed as the last one in this suite - @LW-8559 + # this test should be executed as the last one in this suite + @LW-8559 @Pending + @issue=LW-10150 Scenario: Analytics - Extended View - Settings - Wallet removal events - Remove wallet When I open settings from header menu And I set up request interception for posthog analytics request(s) diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature index 83d53052b..5b472fd64 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature @@ -1,4 +1,4 @@ -@Settings-Popup @Analytics @Mainnet @Testnet +@Analytics-Settings-Popup @Analytics @Mainnet @Testnet Feature: Analytics - Settings - Popup View Background: @@ -186,8 +186,9 @@ Feature: Analytics - Settings - Popup View Then I validate latest analytics single event "settings | theme | light mode | click" Then I validate that 2 analytics event(s) have been sent - # this test should be executed as the last one in this suite - @LW-8571 + # this test should be executed as the last one in this suite + @LW-8571 @Pending + @issue=LW-10150 Scenario: Analytics - Popup View - Settings - Wallet removal events - Remove wallet Given I am on Tokens popup page When I open settings from header menu diff --git a/packages/e2e-tests/src/hooks/beforeTagHooks.ts b/packages/e2e-tests/src/hooks/beforeTagHooks.ts index 160bf85c6..3b413d2ba 100644 --- a/packages/e2e-tests/src/hooks/beforeTagHooks.ts +++ b/packages/e2e-tests/src/hooks/beforeTagHooks.ts @@ -34,7 +34,7 @@ Before( Before( { - tags: '@AddressBook-extended or @Transactions-Extended or @Tokens-extended or @Staking-Extended or @LockWallet-extended or @Top-Navigation-Extended or @NFTs-Extended or @NFT-Folders-Extended or @SendTx-Bundles-Extended or @SendTx-Simple-Extended or @MainNavigation-Extended or @Send-Transaction-Metadata-Extended or @Settings-Extended or @DAppConnector or @DAppConnector-Extended' + tags: '@AddressBook-extended or @Transactions-Extended or @Tokens-extended or @Staking-Extended or @LockWallet-extended or @Top-Navigation-Extended or @NFTs-Extended or @NFT-Folders-Extended or @SendTx-Bundles-Extended or @SendTx-Simple-Extended or @MainNavigation-Extended or @Send-Transaction-Metadata-Extended or @Settings-Extended or @DAppConnector or @DAppConnector-Extended or @Analytics-Settings-Extended' }, async () => { await extendedViewWalletInitialization(); @@ -44,7 +44,7 @@ Before( Before( { - tags: '@Tokens-popup or @Transactions-Popup or @Staking-Popup or @LockWallet-popup or @Top-Navigation-Popup or @AddressBook-popup or @Common-Popup or @SendTx-Simple-Popup or @MainNavigation-Popup or @Settings-Popup or @NFTs-Popup or @NFT-Folders-Popup or @Send-Transaction-Metadata-Popup or @ForgotPassword or @DAppConnector-Popup' + tags: '@Tokens-popup or @Transactions-Popup or @Staking-Popup or @LockWallet-popup or @Top-Navigation-Popup or @AddressBook-popup or @Common-Popup or @SendTx-Simple-Popup or @MainNavigation-Popup or @Settings-Popup or @NFTs-Popup or @NFT-Folders-Popup or @Send-Transaction-Metadata-Popup or @ForgotPassword or @DAppConnector-Popup or @Analytics-Settings-Popup' }, async () => { await popupViewWalletInitialization(); From 2dd801f12a6e570bbe03a791f2e316261003a51c Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Wed, 3 Apr 2024 10:45:29 +0200 Subject: [PATCH 16/74] chore(extension): update lace version v1.10.0 LW-10198 (#1015) --- apps/browser-extension-wallet/manifest.json | 2 +- apps/browser-extension-wallet/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser-extension-wallet/manifest.json b/apps/browser-extension-wallet/manifest.json index fcbadaafa..2b671e9de 100644 --- a/apps/browser-extension-wallet/manifest.json +++ b/apps/browser-extension-wallet/manifest.json @@ -1,7 +1,7 @@ { "name": "$WALLET_MANIFEST_NAME", "description": "One fast, accessible, and secure platform for digital assets, DApps, NFTs, and DeFi.", - "version": "1.9.0", + "version": "1.10.0", "manifest_version": 3, "key": "$LACE_EXTENSION_KEY", "icons": { diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 22380fe05..909554ce5 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@lace/browser-extension-wallet", - "version": "1.9.0", + "version": "1.10.0", "description": "A fully capable wallet packaged as browser extensions for Chrome, Firefox, and Edge", "homepage": "https://github.com/input-output-hk/lace/blob/master/apps/browser-extension-wallet/README.md", "bugs": { From 7071759c4b8259237b65ca82d4cb93937e89b527 Mon Sep 17 00:00:00 2001 From: John Oshalusi Date: Wed, 3 Apr 2024 10:10:20 +0100 Subject: [PATCH 17/74] fix: improve remove wallet analytics (#1004) Co-authored-by: Emir Hodzic --- .../features/settings/components/SettingsRemoveWallet.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx index 1d6498e55..c5986c127 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx @@ -29,18 +29,18 @@ export const SettingsRemoveWallet = ({ popupView }: { popupView?: boolean }): Re setIsRemoveWalletAlertVisible(!isRemoveWalletAlertVisible); analytics.sendEventToPostHog( - isRemoveWalletAlertVisible ? PostHogAction.SettingsHoldUpBackClick : PostHogAction.SettingsRemoveWalletClick + isRemoveWalletAlertVisible ? PostHogAction.SettingsHoldUpBackClick : PostHogAction.SettingsHoldUpRemoveWalletClick ); }; const removeWallet = async () => { setDeletingWallet(true); - const nextActiveWallet = await deleteWallet(); - setDeletingWallet(false); - analytics.sendEventToPostHog(PostHogAction.SettingsHoldUpRemoveWalletClick, { + await analytics.sendEventToPostHog(PostHogAction.SettingsRemoveWalletClick, { // eslint-disable-next-line camelcase $set: { wallet_accounts_quantity: await getWalletAccountsQtyString(walletRepository) } }); + const nextActiveWallet = await deleteWallet(); + setDeletingWallet(false); if (nextActiveWallet) return; if (popupView) await backgroundServices.handleOpenBrowser({ section: BrowserViewSections.HOME }); // force reload to ensure all stores are cleaned up From 8ce7432babac8dd62130aa040c38e2555dc5bb6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20W=C5=82odek?= Date: Wed, 3 Apr 2024 13:42:04 +0200 Subject: [PATCH 18/74] feat(extension,staking): enable the new Staking Grid View (#1012) * feat(extension,staking): enable the new Staking Grid View by default * feat(extension,staking): enable tests related to the new Staking Grid View --- apps/browser-extension-wallet/.env.defaults | 2 +- .../.env.developerpreview | 2 +- apps/browser-extension-wallet/.env.example | 2 +- .../MoreOptionsComponentAssert.ts | 11 +++---- .../MultidelegationPageAssert.ts | 1 + .../MultiDelegationPageExtended.feature | 29 +++++++++---------- ...onDelegatedFundsSinglePoolExtended.feature | 3 ++ .../AnalyticsStakingExtended.feature | 7 ++--- .../e2e/StakingInitialFundsE2E.feature | 1 + .../src/steps/multidelegationSteps.ts | 5 +--- 10 files changed, 31 insertions(+), 32 deletions(-) diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index 823f04403..687e06644 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -25,7 +25,7 @@ USE_POSTHOG_ANALYTICS=true USE_MULTI_DELEGATION_STAKING_LEDGER=false USE_MULTI_DELEGATION_STAKING_TREZOR=false USE_MULTI_DELEGATION_STAKING_ACTIVITY=true -USE_MULTI_DELEGATION_STAKING_GRID_VIEW=false +USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true USE_MULTI_DELEGATION_STAKING_FILTERS=false USE_ROS_STAKING_COLUMN=false diff --git a/apps/browser-extension-wallet/.env.developerpreview b/apps/browser-extension-wallet/.env.developerpreview index 1f77e24c6..72d9c2e35 100644 --- a/apps/browser-extension-wallet/.env.developerpreview +++ b/apps/browser-extension-wallet/.env.developerpreview @@ -26,7 +26,7 @@ USE_POSTHOG_ANALYTICS=true USE_MULTI_DELEGATION_STAKING_LEDGER=false USE_MULTI_DELEGATION_STAKING_TREZOR=false USE_MULTI_DELEGATION_STAKING_ACTIVITY=true -USE_MULTI_DELEGATION_STAKING_GRID_VIEW=false +USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true USE_ROS_STAKING_COLUMN=false USE_POSTHOG_ANALYTICS_FOR_OPTED_OUT=false diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index 0b8567169..38bf0387a 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -25,7 +25,7 @@ USE_DATA_CHECK=false USE_POSTHOG_ANALYTICS=true USE_POSTHOG_ANALYTICS_FOR_OPTED_OUT=false USE_MULTI_DELEGATION_STAKING_ACTIVITY=true -USE_MULTI_DELEGATION_STAKING_GRID_VIEW=false +USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true USE_MULTI_DELEGATION_STAKING_FILTERS=false USE_ROS_STAKING_COLUMN=false diff --git a/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts b/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts index 96dc80bcb..d8d39397c 100644 --- a/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts +++ b/packages/e2e-tests/src/assert/multidelegation/MoreOptionsComponentAssert.ts @@ -40,11 +40,12 @@ class MoreOptionsComponentAssert { expect(await MoreOptionsComponent.saturationOption.label.getText()).to.equal( await t('browsePools.preferencesCard.sort.saturation', 'staking') ); - await MoreOptionsComponent.rosOption.radioButton.waitForDisplayed(); - await MoreOptionsComponent.rosOption.label.waitForDisplayed(); - expect(await MoreOptionsComponent.rosOption.label.getText()).to.equal( - await t('browsePools.preferencesCard.sort.ros', 'staking') - ); + // TODO: Uncomment when USE_ROS_STAKING_COLUMN=true + // await MoreOptionsComponent.rosOption.radioButton.waitForDisplayed(); + // await MoreOptionsComponent.rosOption.label.waitForDisplayed(); + // expect(await MoreOptionsComponent.rosOption.label.getText()).to.equal( + // await t('browsePools.preferencesCard.sort.ros', 'staking') + // ); await MoreOptionsComponent.costOption.radioButton.waitForDisplayed(); await MoreOptionsComponent.costOption.label.waitForDisplayed(); expect(await MoreOptionsComponent.costOption.label.getText()).to.equal( diff --git a/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts b/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts index 0299852de..3fe3f6ffb 100644 --- a/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts +++ b/packages/e2e-tests/src/assert/multidelegation/MultidelegationPageAssert.ts @@ -294,6 +294,7 @@ class MultidelegationPageAssert { }; assertsSeeCardsInARow = async (expectedCardsCount: number) => { + await MultidelegationPage.gridContainer.waitForStable(); const rowWidth = await MultidelegationPage.gridContainer.getSize('width'); const cardWidth = await new StakePoolGridCard(0).container.getSize('width'); const cardsInARow = Math.floor(rowWidth / cardWidth); diff --git a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature b/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature index 39f72ab4f..91e855d6e 100644 --- a/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature +++ b/packages/e2e-tests/src/features/MultiDelegationPageExtended.feature @@ -98,7 +98,7 @@ Feature: Staking Page - Extended View | column_name | | Ticker | | Saturation | -# | ROS | #TODO: Uncomment when LW-9827 is resolved +# | ROS | #TODO: Uncomment when USE_ROS_STAKING_COLUMN=true | Cost | | Margin | | Blocks | @@ -146,8 +146,7 @@ Feature: Staking Page - Extended View And I wait for stake pool list to be populated Then each stake pool list item contains: checkbox, ticker, saturation, ROS, cost, margin, blocks, pledge and live stake - @LW-9985 @Testnet @Mainnet @skip - # TODO: enable when USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true by default + @LW-9985 @Testnet @Mainnet Scenario: Extended View - Stake pool list - display skeleton while loading list elements And I am on Staking extended page And I open Browse pools tab @@ -158,8 +157,7 @@ Feature: Staking Page - Extended View When I wait 500 milliseconds Then stake pool list row skeleton is not displayed - @LW-9986 @Testnet @Mainnet @skip - # TODO: enable when USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true by default + @LW-9986 @Testnet @Mainnet Scenario: Extended View - Stake pool grid - display skeleton while loading grid cards And I am on Staking extended page And I open Browse pools tab @@ -169,8 +167,7 @@ Feature: Staking Page - Extended View When I wait 500 milliseconds Then stake pool grid card skeleton is not displayed - @LW-9995 @Testnet @Mainnet @skip - # TODO: enable when USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true by default + @LW-9995 @Testnet @Mainnet Scenario Outline: Extended View - Browse pools - preserve selected pools and view type When I am on Staking extended page And I open Browse pools tab @@ -189,8 +186,13 @@ Feature: Staking Page - Extended View | list | I refresh the page | | list | I open Overview tab | - @LW-9996 @Testnet @Mainnet @skip - # TODO: enable when USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true by default + @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 And I open Browse pools tab @@ -201,13 +203,8 @@ Feature: Staking Page - Extended View Then I see 5 stake pool cards in a row When I resize the window to a width of: 1659 and a height of: 1080 Then I see 4 stake pool cards in a row - When I resize the window to a width of: 1024 and a height of: 1080 + When I resize the window to a width of: 668 and a height of: 1080 Then I see 4 stake pool cards in a row - When I resize the window to a width of: 1023 and a height of: 1080 + When I resize the window to a width of: 667 and a height of: 1080 Then I see 3 stake pool cards in a row - @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 diff --git a/packages/e2e-tests/src/features/MultidelegationDelegatedFundsSinglePoolExtended.feature b/packages/e2e-tests/src/features/MultidelegationDelegatedFundsSinglePoolExtended.feature index 30c1ebc1c..5f7d20534 100644 --- a/packages/e2e-tests/src/features/MultidelegationDelegatedFundsSinglePoolExtended.feature +++ b/packages/e2e-tests/src/features/MultidelegationDelegatedFundsSinglePoolExtended.feature @@ -8,6 +8,7 @@ Feature: Staking Page - Delegated funds - Single pool - Extended View Scenario Outline: Extended View - Staking - Close drawer When I navigate to Staking extended page And I open Browse pools tab + And I switch to list view on "Browse pools" tab And I pick "1" pools for delegation from browse pools view: "OCEAN" And I click "Next" button on staking portfolio bar And I click "Fine by me" button on "Changing staking preferences?" modal @@ -82,6 +83,7 @@ Feature: Staking Page - Delegated funds - Single pool - Extended View And I see selected pools counter is showing "1" And I see "Add stake pool" button is enabled When I click "Add stake pool" button + And I switch to list view on "Browse pools" tab And I pick "CAN1" pool for delegation And I click "Next" button on staking portfolio bar And I click "Fine by me" button on "Changing staking preferences?" modal @@ -97,6 +99,7 @@ Feature: Staking Page - Delegated funds - Single pool - Extended View And I see selected pools counter is showing "1" And I see "Add stake pool" button is enabled When I click "Add stake pool" button + And I switch to list view on "Browse pools" tab And I pick "" pools for delegation from browse pools view: "" And I click "Next" button on staking portfolio bar And I click "Fine by me" button on "Changing staking preferences?" modal diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature index 18070bcd5..1d5bcc461 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsStakingExtended.feature @@ -11,10 +11,9 @@ Feature: Analytics - PostHog - Staking - Extended View And I set up request interception for posthog analytics request(s) And I switch to list view on "Browse pools" tab Then I validate latest analytics single event "staking | browse pools | toggle | list view | click" - # TODO: enable when USE_MULTI_DELEGATION_STAKING_GRID_VIEW=true by default - # When I switch to grid view on "Browse pools" tab - # Then I validate latest analytics single event "staking | browse pools | toggle | grid view | click" - And I validate that 1 analytics event(s) have been sent + When I switch to grid view on "Browse pools" tab + Then I validate latest analytics single event "staking | browse pools | toggle | grid view | click" + And I validate that 2 analytics event(s) have been sent @LW-10148 Scenario: Analytics - Extended View - Staking - List View - click on column headers diff --git a/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature b/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature index ca05a0fcf..a0b538118 100644 --- a/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature +++ b/packages/e2e-tests/src/features/e2e/StakingInitialFundsE2E.feature @@ -30,6 +30,7 @@ Feature: Delegating funds to new pool E2E And I disable showing Multidelegation beta banner And I navigate to Staking extended page And I open Browse pools tab + And I switch to list view on "Browse pools" tab And I pick "4" pools for delegation from browse pools view: "ZZZG3, YATP, XSP, CENT" And I click "Next" button on staking portfolio bar And I click on "Next" button on staking preferences drawer diff --git a/packages/e2e-tests/src/steps/multidelegationSteps.ts b/packages/e2e-tests/src/steps/multidelegationSteps.ts index 816ab2d70..2c20aeba3 100644 --- a/packages/e2e-tests/src/steps/multidelegationSteps.ts +++ b/packages/e2e-tests/src/steps/multidelegationSteps.ts @@ -459,10 +459,7 @@ Then(/^I see Expanded View banner$/, async () => { }); When(/^I switch to (grid|list) view on "Browse pools" tab$/, async (viewType: 'grid' | 'list') => { - // TODO: remove `if` when USE_MULTI_DELEGATION_STAKING_GRID_VIEW is enabled by default - if (await MultidelegationPage.gridViewToggle.isExisting()) { - await MultidelegationPage.switchPoolsView(viewType); - } + await MultidelegationPage.switchPoolsView(viewType); }); Then(/^stake pool list row skeleton (is|is not) displayed$/, async (status: 'is' | 'is not') => { From de036a530f7dd7ccd94f98ac772b319ad7513c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20W=C5=82odek?= Date: Wed, 3 Apr 2024 14:07:34 +0200 Subject: [PATCH 19/74] fix(extension): do not render BrowsePoolsPreferencesCard for HW [LW-10202] (#1018) --- .../features/staking/components/StakingContainer.tsx | 4 +++- .../features/staking/components/StakingSkeleton.tsx | 11 ++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx index 00c3ce3ea..c9d8d1bd7 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx @@ -134,7 +134,9 @@ export const StakingContainer = (): React.ReactElement => { isMultidelegationSupportedByDevice }} > - {multiDelegationEnabled ? : } + + {multiDelegationEnabled ? : } + ); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx index 2b41c48b3..2052e7fcf 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingSkeleton.tsx @@ -1,7 +1,7 @@ /* eslint-disable max-statements */ /* eslint-disable complexity */ /* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { PropsWithChildren, useEffect } from 'react'; +import React, { ReactNode, useEffect } from 'react'; import isNumber from 'lodash/isNumber'; import { Skeleton } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -14,8 +14,13 @@ import LightBulb from '@src/assets/icons/light.svg'; import { BrowsePoolsPreferencesCard } from '@lace/staking'; import { Flex } from '@lace/ui'; +type StakingSkeletonProps = { + children: ReactNode; + multiDelegationEnabled: boolean; +}; + // eslint-disable-next-line @typescript-eslint/ban-types -export const StakingSkeleton = ({ children }: PropsWithChildren): React.ReactElement => { +export const StakingSkeleton = ({ children, multiDelegationEnabled }: StakingSkeletonProps): React.ReactElement => { const { t } = useTranslation(); const { networkInfo, fetchNetworkInfo } = useWalletStore(stakingInfoSelector); const { priceResult } = useFetchCoinPrice(); @@ -72,7 +77,7 @@ export const StakingSkeleton = ({ children }: PropsWithChildren): React. const sidePanel = ( - + {multiDelegationEnabled && } From 88afe0f31cadebe6fed0a82388cb6717bd17e154 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 3 Apr 2024 15:49:52 +0300 Subject: [PATCH 20/74] feat(extension): add in app voltaire faqs (#993) * feat(extension): add in app voltaire faqs * fix(extension): resolve sdet comments * test(extension): update LW-2339 --------- Co-authored-by: wklos-iohk --- .../src/lib/translations/en.json | 5 ++++- .../AssetEducationalList.tsx | 22 ++++++++++++++++++- .../e2e-tests/src/data/EducationalArticles.ts | 14 ++++++++++++ .../src/features/TokensPageExtended.feature | 13 ++++++----- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 503fb9500..80d602092 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1062,7 +1062,10 @@ "stakingAndDelegation": "What are staking & delegation?", "howManyPools": "How many stake pools can I delegate stake to, using the multi-staking or multi-delegation feature?", "ledgerSupport": "Do Ledger hardware wallets support multi-staking?", - "stakeDistribution": "Does stake distribution remain the same?" + "stakeDistribution": "Does stake distribution remain the same?", + "conwayEra": "How is the Conway Ledger era (also called governance era) supported by Lace?", + "governanceFeatures": "What type of governance features are supported in Lace using the GovTool in the current SanchoNet test environment?", + "governanceActions": "What type of governance actions are supported by Lace?" } }, "migrations": { diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList.tsx index 02b698ca1..8de313fc8 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetEducationalList/AssetEducationalList.tsx @@ -5,6 +5,8 @@ import LightBulb from '@assets/icons/light.svg'; import Video from '@assets/icons/video.svg'; import { EducationalList } from '@src/views/browser-view/components/EducationalList'; +const faqKey = 'educationalBanners.title.faq'; + export const AssetEducationalList = (): React.ReactElement => { const { t } = useTranslation(); @@ -17,7 +19,7 @@ export const AssetEducationalList = (): React.ReactElement => { link: `${process.env.WEBSITE_URL}/glossary?term=asset` }, { - title: t('educationalBanners.title.faq'), + title: t(faqKey), subtitle: t('educationalBanners.subtitle.howToSendReceiveFunds'), src: LightBulb, link: `${process.env.WEBSITE_URL}/faq?question=how-do-i-send-and-receive-digital-assets` @@ -33,6 +35,24 @@ export const AssetEducationalList = (): React.ReactElement => { subtitle: t('educationalBanners.subtitle.connectingDApps'), src: Video, link: `${process.env.WEBSITE_URL}/learn?video=connecting-to-dapps-with-lace` + }, + { + title: t(faqKey), + subtitle: t('educationalBanners.subtitle.conwayEra'), + src: LightBulb, + link: `${process.env.WEBSITE_URL}/faq?question=how-is-the-conway-ledger-era-also-called-governance-era-supported-by-lace` + }, + { + title: t(faqKey), + subtitle: t('educationalBanners.subtitle.governanceFeatures'), + src: LightBulb, + link: `${process.env.WEBSITE_URL}/faq?question=what-type-of-governance-features-are-supported-in-lace-using-the-govtool-in-the-current` + }, + { + title: t(faqKey), + subtitle: t('educationalBanners.subtitle.governanceActions'), + src: LightBulb, + link: `${process.env.WEBSITE_URL}/faq?question=what-type-of-governance-actions-are-supported-by-lace` } ], [t] diff --git a/packages/e2e-tests/src/data/EducationalArticles.ts b/packages/e2e-tests/src/data/EducationalArticles.ts index baeaa68d8..f697e1c7d 100644 --- a/packages/e2e-tests/src/data/EducationalArticles.ts +++ b/packages/e2e-tests/src/data/EducationalArticles.ts @@ -30,6 +30,20 @@ export const faqArticles: Record> = { 'Does stake distribution remain the same?': { question: 'does-stake-distribution-remain-the-same', title: 'Does stake distribution remain the same?' + }, + 'How is the Conway Ledger era (also called governance era) supported by Lace?': { + question: 'how-is-the-conway-ledger-era-also-called-governance-era-supported-by-lace', + title: 'How is the Conway Ledger era (also called governance era) supported by Lace?' + }, + 'What type of governance features are supported in Lace using the GovTool in the current SanchoNet test environment?': + { + question: 'what-type-of-governance-features-are-supported-in-lace-using-the-govtool-in-the-current', + title: + 'What type of governance features are supported in Lace using the GovTool in the current SanchoNet test environment?' + }, + 'What type of governance actions are supported by Lace?': { + question: 'what-type-of-governance-actions-are-supported-by-lace', + title: 'What type of governance actions are supported by Lace?' } }; diff --git a/packages/e2e-tests/src/features/TokensPageExtended.feature b/packages/e2e-tests/src/features/TokensPageExtended.feature index 58a04fd13..b35e0b267 100644 --- a/packages/e2e-tests/src/features/TokensPageExtended.feature +++ b/packages/e2e-tests/src/features/TokensPageExtended.feature @@ -62,11 +62,14 @@ Feature: LW: Tokens tab - extended view When I click on a widget item with subtitle: "" Then I see a "" article with title "" Examples: - | type | subtitle | - | Glossary | What is a digital asset? | - | FAQ | How do I send and receive funds? | - | Video | Secure self-custody with Lace | - | Video | Connecting to DApps with Lace | + | type | subtitle | + | Glossary | What is a digital asset? | + | FAQ | How do I send and receive funds? | + | Video | Secure self-custody with Lace | + | Video | Connecting to DApps with Lace | + | FAQ | How is the Conway Ledger era (also called governance era) supported by Lace? | + | FAQ | What type of governance features are supported in Lace using the GovTool in the current SanchoNet test environment? | + | FAQ | What type of governance actions are supported by Lace? | @LW-4878 @Testnet @Mainnet Scenario: Extended-view - Tokens details - Enter and Escape buttons support From f1cad2b34334ac07c1ca3bb55dfe78b4458d4472 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 3 Apr 2024 16:51:50 +0300 Subject: [PATCH 21/74] chore(extension): fix default values formatting in staking mappers (#996) * chore(extension): fix default values formatting in staking mappers * fix(extension): resolve sdet comments * fix(extension): fix failing tests --- .../components/DelegationLayout.tsx | 2 ++ .../delegation/components/Staking.tsx | 2 ++ .../stores/createDelegationStore.ts | 25 +++++++++--------- .../src/features/delegation/types/index.ts | 18 ++++++------- .../src/utils/mocks/test-helpers.tsx | 6 ----- .../StakePoolDetails/StakePoolDetail.tsx | 24 ++++++++++++----- .../components/StakingInfo/StakingInfo.tsx | 6 ++--- .../StakePoolMetricsBrowser.tsx | 3 ++- .../__tests__/stake-pool-transformer.test.ts | 3 ++- .../src/wallet/util/stake-pool-transformer.ts | 16 ++++++------ .../WalletSetupNamePasswordStepRevamp.tsx | 2 +- .../StakePoolCard/PoolMetric/PoolMetric.tsx | 2 +- .../StakePoolCard/StakePoolCard.tsx | 4 +-- .../StakePoolsList/StakePoolListCell.tsx | 7 ++--- .../src/features/BrowsePools/formatters.ts | 14 +++++----- .../src/features/Drawer/StakePoolDetail.tsx | 25 +++++++++++------- .../PoolDetailsCard/PoolDetailsCard.tsx | 2 +- .../preferences/StepPreferencesContent.tsx | 4 +-- .../src/features/overview/Overview.tsx | 4 +-- .../src/features/overview/OverviewPopup.tsx | 4 +-- .../StakingInfoCard/StakingInfoCard.tsx | 2 +- .../helpers/mapPortfolioToDisplayData.ts | 2 +- .../mapStakePoolToDisplayData.ts | 26 ++++++++----------- .../store/delegationPortfolioStore/types.ts | 18 ++++++------- 24 files changed, 115 insertions(+), 106 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/delegation/components/DelegationLayout.tsx b/apps/browser-extension-wallet/src/features/delegation/components/DelegationLayout.tsx index e8e8b3f9c..dacd363b2 100644 --- a/apps/browser-extension-wallet/src/features/delegation/components/DelegationLayout.tsx +++ b/apps/browser-extension-wallet/src/features/delegation/components/DelegationLayout.tsx @@ -21,6 +21,8 @@ export interface StakePool { name?: string; ticker?: string; logo?: string; + margin: string; + fee: string; pledgeMet?: boolean; retired?: boolean; onClick?: () => unknown; diff --git a/apps/browser-extension-wallet/src/features/delegation/components/Staking.tsx b/apps/browser-extension-wallet/src/features/delegation/components/Staking.tsx index 45bed32f3..38ae4e419 100644 --- a/apps/browser-extension-wallet/src/features/delegation/components/Staking.tsx +++ b/apps/browser-extension-wallet/src/features/delegation/components/Staking.tsx @@ -21,6 +21,8 @@ export interface StakePool { logo?: string; pledgeMet?: boolean; retired?: boolean; + margin: string; + fee: string; onClick?: () => unknown; } diff --git a/apps/browser-extension-wallet/src/features/delegation/stores/createDelegationStore.ts b/apps/browser-extension-wallet/src/features/delegation/stores/createDelegationStore.ts index f43544371..0aa93234c 100644 --- a/apps/browser-extension-wallet/src/features/delegation/stores/createDelegationStore.ts +++ b/apps/browser-extension-wallet/src/features/delegation/stores/createDelegationStore.ts @@ -1,5 +1,6 @@ /* eslint-disable no-magic-numbers */ import BigNumber from 'bignumber.js'; +import isNil from 'lodash/isNil'; import create, { StateSelector } from 'zustand'; import { Wallet } from '@lace/cardano'; import { formatPercentages, getNumberWithUnit, getRandomIcon } from '@lace/common'; @@ -16,7 +17,7 @@ DelegationStore): stakePoolDetailsSelectorProps => { id, cost, hexId, - metadata: { description = '', name = '', ticker = '', homepage, ext } = {}, + metadata: { description, name, ticker, homepage, ext } = {}, metrics: { ros, delegators, stake, saturation, blocksCreated } = {}, margin, owners, @@ -24,11 +25,11 @@ DelegationStore): stakePoolDetailsSelectorProps => { status, pledge } = selectedStakePool; - const calcMargin = margin ? `${formatPercentages(margin.numerator / margin.denominator)}` : '-'; + const calcMargin = margin ? `${formatPercentages(margin.numerator / margin.denominator)}` : ''; return { // TODO: a lot of this is repeated in `stakePoolTransformer`. Have only one place to parse this info - delegators: new BigNumber(delegators).toFormat() || '-', + ...(!isNil(delegators) && { delegators: new BigNumber(delegators).toFormat() }), description, hexId: hexId.toString(), id: id.toString(), @@ -36,22 +37,22 @@ DelegationStore): stakePoolDetailsSelectorProps => { margin: calcMargin, name, owners: owners ? owners.map((owner: Wallet.Cardano.RewardAccount) => owner.toString()) : [], - saturation: saturation && formatPercentages(saturation), - activeStake: stake?.active - ? getNumberWithUnit(Wallet.util.lovelacesToAdaString(stake?.active?.toString())) - : { number: '-' }, - liveStake: stake?.live - ? getNumberWithUnit(Wallet.util.lovelacesToAdaString(stake?.live?.toString())) - : { number: '-' }, + ...(!isNil(saturation) && { saturation: formatPercentages(saturation) }), + ...(!isNil(stake?.active) && { + activeStake: getNumberWithUnit(Wallet.util.lovelacesToAdaString(stake.active.toString())) + }), + ...(!isNil(stake?.live) && { + liveStake: getNumberWithUnit(Wallet.util.lovelacesToAdaString(stake.live.toString())) + }), ticker, status, - ros: ros && formatPercentages(ros), + ...(!isNil(ros) && { ros: formatPercentages(ros) }), fee: Wallet.util.lovelacesToAdaString(cost.toString()), contact: { primary: homepage, ...ext?.pool.contact }, - blocks: new BigNumber(blocksCreated).toFormat() || '-', + ...(!isNil(blocksCreated) && { blocks: new BigNumber(blocksCreated).toFormat() }), pledge: Wallet.util.lovelacesToAdaString(pledge.toString()) }; } diff --git a/apps/browser-extension-wallet/src/features/delegation/types/index.ts b/apps/browser-extension-wallet/src/features/delegation/types/index.ts index 7a8235057..d57bbf201 100644 --- a/apps/browser-extension-wallet/src/features/delegation/types/index.ts +++ b/apps/browser-extension-wallet/src/features/delegation/types/index.ts @@ -31,22 +31,22 @@ export interface StakePool { } export type stakePoolDetailsSelectorProps = { - delegators: number | string; - description: string; + delegators?: string; + description?: string; hexId: string; id: string; logo?: string; margin: number | string; - name: string; + name?: string; owners: string[]; - saturation: number | string; - activeStake: { number: string; unit?: string }; - liveStake: { number: string; unit?: string }; - ticker: string; - ros: number | string; + saturation?: string; + activeStake?: { number: string; unit?: string }; + liveStake?: { number: string; unit?: string }; + ticker?: string; + ros?: string; status: Wallet.Cardano.StakePool['status']; fee: string; contact: Wallet.Cardano.PoolContactData; - blocks: number | string; + blocks?: string; pledge: string; }; diff --git a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx index e11cf05de..7c6bd3282 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx +++ b/apps/browser-extension-wallet/src/utils/mocks/test-helpers.tsx @@ -565,12 +565,6 @@ export const cardanoStakePoolSelectedDetails = { 'stake_test1uq7g7kqeucnqfweqzgxk3dw34e8zg4swnc7nagysug2mm4cm77jrx' ], saturation: '5.12', - activeStake: { - number: '-' - }, - liveStake: { - number: '-' - }, ros: '69.00', status: 'active', ticker: 'STTST', diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolDetails/StakePoolDetail.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolDetails/StakePoolDetail.tsx index 5b945a042..5b5246e4e 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolDetails/StakePoolDetail.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolDetails/StakePoolDetail.tsx @@ -92,18 +92,28 @@ export const StakePoolDetail = ({ popupView, setIsStaking }: stakePoolDetailProp const formattedCost = getNumberWithUnit(fee); const metricsData = useMemo(() => { const metrics = [ - { t: metricsTranslations.activeStake, testId: 'active-stake', unit: activeStake.unit, value: activeStake.number }, - { t: metricsTranslations.liveStake, testId: 'live-stake', unit: liveStake.unit, value: liveStake.number }, - { t: metricsTranslations.delegators, testId: 'delegators', value: delegators || '-' }, - { t: metricsTranslations.ros, testId: 'ros', unit: '%', value: ros || '-' }, + { + t: metricsTranslations.activeStake, + testId: 'active-stake', + unit: activeStake?.unit, + value: activeStake?.number + }, + { t: metricsTranslations.liveStake, testId: 'live-stake', unit: liveStake?.unit, value: liveStake?.number }, + { t: metricsTranslations.delegators, testId: 'delegators', value: delegators }, + { t: metricsTranslations.ros, testId: 'ros', unit: '%', value: ros }, { t: metricsTranslations.blocks, testId: 'blocks', value: blocks }, - { t: metricsTranslations.cost, testId: 'cost', unit: formattedCost.unit, value: formattedCost.number }, - { t: metricsTranslations.pledge, testId: 'pledge', unit: formattedPledge.unit, value: formattedPledge.number }, + { t: metricsTranslations.cost, testId: 'cost', unit: formattedCost?.unit, value: formattedCost?.number }, + { + t: metricsTranslations.pledge, + testId: 'pledge', + unit: formattedPledge?.unit, + value: formattedPledge?.number + }, { t: metricsTranslations.margin, testId: 'margin', unit: '%', value: margin } ]; if (popupView) { - metrics.push({ t: metricsTranslations.saturation, testId: 'saturation', unit: '%', value: saturation || '-' }); + metrics.push({ t: metricsTranslations.saturation, testId: 'saturation', unit: '%', value: saturation }); } return metrics; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingInfo/StakingInfo.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingInfo/StakingInfo.tsx index 72b25c3ca..44c8a21e9 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingInfo/StakingInfo.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingInfo/StakingInfo.tsx @@ -21,15 +21,15 @@ const formatNumericValue = (val: number | string, suffix: number | string): Reac type stakingInfoPanelProps = { className?: string; coinBalance: number; - fee?: string | number; + fee: string; fiat: number; id: string; logo?: string; - margin?: string | number; + margin: string; name?: string; totalRewards: string; lastReward: string; - ros?: string | number; + ros?: string; ticker?: string; onStakePoolSelect: () => void; popupView?: boolean; diff --git a/packages/cardano/src/ui/components/StakePoolMetricsBrowser/StakePoolMetricsBrowser.tsx b/packages/cardano/src/ui/components/StakePoolMetricsBrowser/StakePoolMetricsBrowser.tsx index c9408941f..d6a422128 100644 --- a/packages/cardano/src/ui/components/StakePoolMetricsBrowser/StakePoolMetricsBrowser.tsx +++ b/packages/cardano/src/ui/components/StakePoolMetricsBrowser/StakePoolMetricsBrowser.tsx @@ -1,10 +1,11 @@ import React from 'react'; +import isNil from 'lodash/isNil'; import cn from 'classnames'; import styles from './StakePoolMetricsBrowser.module.scss'; const formatNumericValue = (val: number | string, suffix: number | string): React.ReactElement => ( <> - {val ?? '-'} {val !== undefined && {suffix}} + {val ?? '-'} {!isNil(val) && {suffix}} ); diff --git a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts index 12da950d6..99ed36100 100644 --- a/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts +++ b/packages/cardano/src/wallet/util/__tests__/stake-pool-transformer.test.ts @@ -91,7 +91,8 @@ const transformedStakePool = { saturation: '5.12', ticker: 'STTST', blocks: '20', - stakePool: cardanoStakePoolMock.pageResults[0] + stakePool: cardanoStakePoolMock.pageResults[0], + fee: '6.04' }; describe('Testing transformers', () => { diff --git a/packages/cardano/src/wallet/util/stake-pool-transformer.ts b/packages/cardano/src/wallet/util/stake-pool-transformer.ts index 0693f4aea..1c6bc4c6a 100644 --- a/packages/cardano/src/wallet/util/stake-pool-transformer.ts +++ b/packages/cardano/src/wallet/util/stake-pool-transformer.ts @@ -15,8 +15,9 @@ export interface StakePool { logo?: string; retired?: boolean; ros?: string; - liveStake: { number: string; unit?: string }; + liveStake?: { number: string; unit?: string }; cost: { number: string; unit?: string }; + fee: string; saturation?: string; blocks?: string; isStakingPool?: boolean; @@ -41,17 +42,16 @@ export const stakePoolTransformer = ({ stakePool, delegatingPoolId }: StakePoolT owners: owners ? owners.map((owner: Cardano.RewardAccount) => owner.toString()) : [], retired: status === Cardano.StakePoolStatus.Retired, description: metadata?.description, - ...(margin && { margin: `${formatPercentages(margin.numerator / margin.denominator)}` }), - cost: cost ? getNumberWithUnit(lovelacesToAdaString(cost.toString())) : { number: '-', unit: '' }, - liveStake: metrics?.stake.live - ? getNumberWithUnit(lovelacesToAdaString(metrics?.stake.live.toString())) - : { number: '-', unit: '' }, + margin: formatPercentages(margin.numerator / margin.denominator), + fee: lovelacesToAdaString(cost.toString()), + cost: getNumberWithUnit(lovelacesToAdaString(cost.toString())), ...(metrics && { - ...(metrics.ros && { ros: formatPercentages(metrics.ros.valueOf()) }), + liveStake: getNumberWithUnit(lovelacesToAdaString(metrics.stake.live.toString())), + ros: formatPercentages(metrics.ros.valueOf()), saturation: formatPercentages(metrics.saturation.valueOf()), blocks: metrics?.blocksCreated?.toString() }), - pledge: pledge ? getNumberWithUnit(lovelacesToAdaString(pledge.toString())) : { number: '-', unit: '' }, + pledge: getNumberWithUnit(lovelacesToAdaString(pledge.toString())), ...(delegatingPoolId && { isStakingPool: delegatingPoolId === id.toString() }), stakePool }; diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx index e7764555f..4bc0e486b 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx @@ -11,7 +11,7 @@ import { } from '../WalletSetup/WalletSetupNamePasswordStep/utils'; import { WalletNameInput } from '../WalletSetup/WalletSetupNamePasswordStep/WalletNameInput'; import { WalletPasswordConfirmationInput } from '../WalletSetup/WalletSetupNamePasswordStep/WalletPasswordConfirmationInput'; -import { WalletSetupStepLayoutRevamp } from '../WalletSetupRevamp'; +import { WalletSetupStepLayoutRevamp } from '../WalletSetupRevamp/WalletSetupStepLayoutRevamp'; import { TranslationsFor } from '@ui/utils/types'; import { useTranslate } from '@ui/hooks'; import { passwordComplexity } from '@ui/utils/password-complexity'; diff --git a/packages/staking/src/features/BrowsePools/StakePoolCard/PoolMetric/PoolMetric.tsx b/packages/staking/src/features/BrowsePools/StakePoolCard/PoolMetric/PoolMetric.tsx index f1647c8a5..d9fed11ba 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolCard/PoolMetric/PoolMetric.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolCard/PoolMetric/PoolMetric.tsx @@ -30,7 +30,7 @@ export const PoolMetric = ({ metricType, metricValue }: PoolMetricProps) => ( {iconsByType[metricType]} - {metricValue} + {metricValue ?? '-'} ); diff --git a/packages/staking/src/features/BrowsePools/StakePoolCard/StakePoolCard.tsx b/packages/staking/src/features/BrowsePools/StakePoolCard/StakePoolCard.tsx index 362ae017a..b160ff8ef 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolCard/StakePoolCard.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolCard/StakePoolCard.tsx @@ -18,7 +18,7 @@ const shouldRenderMetric = (sortField: SortField, metricValue?: string): metricV !!metricValue && !['ros', 'ticker', 'saturation'].includes(sortField); export const StakePoolCard = ({ - title = '-', + title, metricType, metricValue, saturation, @@ -33,7 +33,7 @@ export const StakePoolCard = ({ - {title} + {title || '-'} {shouldRenderMetric(metricType, metricValue) && ( diff --git a/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolListCell.tsx b/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolListCell.tsx index 5b41112a5..fb70ddac5 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolListCell.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolListCell.tsx @@ -22,14 +22,11 @@ const SaturationCell = ({ children: formattedValue, saturation }: { children: Re }; export const StakePoolListCell = ({ sortField, ...stakePool }: StakePoolListCellProps) => { - if (!stakePool[sortField]) return null; - const formattedValue = getFormattedStakePoolProp(stakePool as StakePoolDetails, sortField); - // TODO do not use '-' as a fallback value; https://input-output.atlassian.net/browse/LW-10001 - if (sortField === 'saturation' && stakePool.saturation && stakePool.saturation !== '-') { + if (sortField === 'saturation' && stakePool.saturation) { return {formattedValue}; } - return <>{formattedValue}; + return <>{formattedValue ?? '-'}; }; diff --git a/packages/staking/src/features/BrowsePools/formatters.ts b/packages/staking/src/features/BrowsePools/formatters.ts index 5fca9aefe..96cc50b1f 100644 --- a/packages/staking/src/features/BrowsePools/formatters.ts +++ b/packages/staking/src/features/BrowsePools/formatters.ts @@ -1,4 +1,4 @@ -import { PickByValue } from 'utility-types'; +import isNil from 'lodash/isNil'; import { StakePoolDetails } from '../store'; type StakePoolFormattableKey = keyof Pick< @@ -17,8 +17,8 @@ type StakePoolFormattableKey = keyof Pick< | 'ticker' >; type PercentageStakePoolKey = ('saturation' | 'ros' | 'margin') & StakePoolFormattableKey; -type NumberWithUnitStakePoolKey = keyof PickByValue & - StakePoolFormattableKey; + +type NumberWithUnitStakePoolKey = ('activeStake' | 'cost' | 'liveStake' | 'pledge') & StakePoolFormattableKey; const percentageStakePoolKeys = new Set(['saturation', 'ros', 'margin']); const numberWithUnitStakePoolKeys = new Set(['activeStake', 'cost', 'liveStake', 'pledge']); @@ -28,14 +28,14 @@ const isNumberWithUnitKey = (key: keyof StakePoolDetails): key is NumberWithUnit const isPercentageKey = (key: keyof StakePoolDetails): key is PercentageStakePoolKey => percentageStakePoolKeys.has(key as PercentageStakePoolKey); -export const formatNumberWithUnit = (value: { number: string; unit: string }): string => `${value.number}${value.unit}`; +export const formatNumberWithUnit = (value: { number: string; unit: string } | undefined): string => + value ? `${value.number ?? '-'}${value.unit}` : '-'; export const getFormattedStakePoolProp = ( stakePool: Pick, key: StakePoolFormattableKey ): string => { - const value = stakePool[key]; - if (value === '-') return value; + if (isNil(stakePool[key])) return '-'; if (isNumberWithUnitKey(key)) { return formatNumberWithUnit(stakePool[key]); @@ -45,5 +45,5 @@ export const getFormattedStakePoolProp = ( return `${stakePool[key]}%`; } - return stakePool[key]; + return stakePool[key] ?? '-'; }; diff --git a/packages/staking/src/features/Drawer/StakePoolDetail.tsx b/packages/staking/src/features/Drawer/StakePoolDetail.tsx index 22c98e265..8518e93b8 100644 --- a/packages/staking/src/features/Drawer/StakePoolDetail.tsx +++ b/packages/staking/src/features/Drawer/StakePoolDetail.tsx @@ -85,10 +85,15 @@ export const StakePoolDetail = ({ popupView }: { popupView?: boolean }): React.R const metricsData = useMemo(() => { const metrics = [ - { t: metricsTranslations.activeStake, testId: 'active-stake', unit: activeStake.unit, value: activeStake.number }, - { t: metricsTranslations.liveStake, testId: 'live-stake', unit: liveStake.unit, value: liveStake.number }, - { t: metricsTranslations.delegators, testId: 'delegators', value: delegators || '-' }, - { t: metricsTranslations.ros, testId: 'ros', unit: '%', value: ros || '-' }, + { + t: metricsTranslations.activeStake, + testId: 'active-stake', + unit: activeStake?.unit, + value: activeStake?.number, + }, + { t: metricsTranslations.liveStake, testId: 'live-stake', unit: liveStake?.unit, value: liveStake?.number }, + { t: metricsTranslations.delegators, testId: 'delegators', value: delegators }, + { t: metricsTranslations.ros, testId: 'ros', unit: '%', value: ros }, { t: metricsTranslations.blocks, testId: 'blocks', value: blocks }, { t: metricsTranslations.cost, testId: 'cost', unit: cost.unit, value: cost.number }, { t: metricsTranslations.pledge, testId: 'pledge', unit: pledge.unit, value: pledge.number }, @@ -96,20 +101,20 @@ export const StakePoolDetail = ({ popupView }: { popupView?: boolean }): React.R ]; if (popupView) { - metrics.push({ t: metricsTranslations.saturation, testId: 'saturation', unit: '%', value: saturation || '-' }); + metrics.push({ t: metricsTranslations.saturation, testId: 'saturation', unit: '%', value: saturation }); } return metrics; }, [ - activeStake.number, - activeStake.unit, + activeStake?.number, + activeStake?.unit, ros, blocks, delegators, cost.number, cost.unit, - liveStake.number, - liveStake.unit, + liveStake?.number, + liveStake?.unit, margin, metricsTranslations.activeStake, metricsTranslations.ros, @@ -174,7 +179,7 @@ export const StakePoolDetail = ({ popupView }: { popupView?: boolean }): React.R {t('drawer.details.information')}
- {description} + {description || '-'}
{socialNetworks.some((sns) => sns.href) && ( diff --git a/packages/staking/src/features/Drawer/preferences/PoolDetailsCard/PoolDetailsCard.tsx b/packages/staking/src/features/Drawer/preferences/PoolDetailsCard/PoolDetailsCard.tsx index 4eb65fed2..cdb33d98c 100644 --- a/packages/staking/src/features/Drawer/preferences/PoolDetailsCard/PoolDetailsCard.tsx +++ b/packages/staking/src/features/Drawer/preferences/PoolDetailsCard/PoolDetailsCard.tsx @@ -55,7 +55,7 @@ export const PoolDetailsCard = ({ - {name} + {name || '-'} { const displayData = draftPortfolio.map((draftPool, i) => { const { - displayData: { name, ros, saturation }, + displayData: { name = '', ros, saturation }, id, sliderIntegerPercentage, } = draftPool; @@ -127,7 +127,7 @@ export const StepPreferencesContent = () => { { ({ + distribution={displayData.map(({ color, name, onChainPercentage, ros, saturation }) => ({ color, - name, + name: name || '-', percentage: onChainPercentage, ros: ros ? String(ros) : undefined, saturation: saturation ? String(saturation) : undefined, diff --git a/packages/staking/src/features/overview/OverviewPopup.tsx b/packages/staking/src/features/overview/OverviewPopup.tsx index aac636fde..a79a7fb91 100644 --- a/packages/staking/src/features/overview/OverviewPopup.tsx +++ b/packages/staking/src/features/overview/OverviewPopup.tsx @@ -95,9 +95,9 @@ export const OverviewPopup = () => { balance={compactNumber(balancesBalance.available.coinBalance)} cardanoCoinSymbol={walletStoreWalletUICardanoCoin.symbol} arrangement="vertical" - distribution={displayData.map(({ color, name = '-', onChainPercentage, ros, saturation }) => ({ + distribution={displayData.map(({ color, name, onChainPercentage, ros, saturation }) => ({ color, - name, + name: name || '-', percentage: onChainPercentage, ros: ros ? String(ros) : undefined, saturation: saturation ? String(saturation) : undefined, diff --git a/packages/staking/src/features/overview/StakingInfoCard/StakingInfoCard.tsx b/packages/staking/src/features/overview/StakingInfoCard/StakingInfoCard.tsx index ffa247b4b..a9276fa36 100644 --- a/packages/staking/src/features/overview/StakingInfoCard/StakingInfoCard.tsx +++ b/packages/staking/src/features/overview/StakingInfoCard/StakingInfoCard.tsx @@ -77,7 +77,7 @@ export const StakingInfoCard = ({ id, logo, margin, - name, + name = '', totalRewards, lastReward, ros, diff --git a/packages/staking/src/features/overview/helpers/mapPortfolioToDisplayData.ts b/packages/staking/src/features/overview/helpers/mapPortfolioToDisplayData.ts index da44b8169..b871609f3 100644 --- a/packages/staking/src/features/overview/helpers/mapPortfolioToDisplayData.ts +++ b/packages/staking/src/features/overview/helpers/mapPortfolioToDisplayData.ts @@ -22,7 +22,7 @@ export const mapPortfolioToDisplayData = ({ fee: Wallet.util.lovelacesToAdaString(item.displayData.stakePool.cost.toString()), fiat: cardanoPrice, lastReward: Wallet.util.lovelacesToAdaString(item.displayData.lastReward.toString()), - name: item.displayData.name || '-', + name: item.displayData.name, status: ((): 'retired' | 'saturated' | 'retiring' | undefined => { if (item.stakePool.status === 'retired') return 'retired'; if (item.stakePool.status === 'retiring') return 'retiring'; diff --git a/packages/staking/src/features/store/delegationPortfolioStore/mapStakePoolToDisplayData.ts b/packages/staking/src/features/store/delegationPortfolioStore/mapStakePoolToDisplayData.ts index cfc6e1c4f..b7da05b76 100644 --- a/packages/staking/src/features/store/delegationPortfolioStore/mapStakePoolToDisplayData.ts +++ b/packages/staking/src/features/store/delegationPortfolioStore/mapStakePoolToDisplayData.ts @@ -3,39 +3,35 @@ import { formatPercentages, getNumberWithUnit, getRandomIcon } from '@lace/commo import BigNumber from 'bignumber.js'; import { StakePoolDetails } from './types'; -// TODO: try to remove data existense checks. in most cases that data is present according to StakePool type // eslint-disable-next-line complexity export const mapStakePoolToDisplayData = ({ stakePool }: { stakePool: Wallet.Cardano.StakePool }): StakePoolDetails => { const { margin, cost, hexId, pledge, owners, status, metadata, id, metrics } = stakePool; - // TODO do not use '-' as a fallback value; https://input-output.atlassian.net/browse/LW-10001 return { - activeStake: metrics - ? getNumberWithUnit(Wallet.util.lovelacesToAdaString(metrics.stake.active.toString())) - : { number: '-', unit: '' }, - blocks: metrics ? new BigNumber(metrics.blocksCreated).toFormat() : '-', + ...(metrics && { + activeStake: getNumberWithUnit(Wallet.util.lovelacesToAdaString(metrics.stake.active.toString())), + blocks: new BigNumber(metrics.blocksCreated).toFormat(), + delegators: new BigNumber(metrics.delegators).toFormat(), + liveStake: getNumberWithUnit(Wallet.util.lovelacesToAdaString(metrics.stake.live.toString())), + ros: formatPercentages(metrics.ros.valueOf()), + saturation: formatPercentages(metrics.saturation), + }), contact: { primary: metadata?.homepage, ...metadata?.ext?.pool.contact, }, cost: getNumberWithUnit(Wallet.util.lovelacesToAdaString(cost.toString())), - delegators: metrics ? new BigNumber(metrics.delegators).toFormat() : '-', - description: metadata?.description || '-', + description: metadata?.description, hexId, id: id.toString(), - liveStake: metrics - ? getNumberWithUnit(Wallet.util.lovelacesToAdaString(metrics.stake.live.toString())) - : { number: '-', unit: '' }, logo: metadata?.ext?.pool.media_assets?.icon_png_64x64 || getRandomIcon({ id: id.toString(), size: 30 }), margin: formatPercentages(margin.numerator / margin.denominator), - name: metadata?.name || '-', + name: metadata?.name, owners: owners ? owners.map((owner: Wallet.Cardano.RewardAccount) => owner.toString()) : [], pledge: getNumberWithUnit(Wallet.util.lovelacesToAdaString(pledge.toString())), retired: status === Wallet.Cardano.StakePoolStatus.Retired, - ros: metrics ? formatPercentages(metrics.ros.valueOf()) : '-', - saturation: metrics ? formatPercentages(metrics.saturation) : '-', stakePool, status, - ticker: metadata?.ticker || '-', + ticker: metadata?.ticker, }; }; diff --git a/packages/staking/src/features/store/delegationPortfolioStore/types.ts b/packages/staking/src/features/store/delegationPortfolioStore/types.ts index fc7d85c62..61e3e95e6 100644 --- a/packages/staking/src/features/store/delegationPortfolioStore/types.ts +++ b/packages/staking/src/features/store/delegationPortfolioStore/types.ts @@ -24,26 +24,26 @@ export type DelegationPortfolioStore = DelegationPortfolioState & { }; export type StakePoolDetails = { - activeStake: { number: string; unit: string }; - blocks: string; + activeStake?: { number: string; unit: string }; + blocks?: string; contact: Wallet.Cardano.PoolContactData; cost: { number: string; unit: string }; - delegators: string; - description: string; + delegators?: string; + description?: string; hexId: Wallet.Cardano.PoolIdHex; id: string; - liveStake: { number: string; unit: string }; + liveStake?: { number: string; unit: string }; logo: string; margin: string; - name: string; + name?: string; owners: string[]; pledge: { number: string; unit: string }; retired: boolean; - ros: string; - saturation: string; + ros?: string; + saturation?: string; stakePool: Wallet.Cardano.StakePool; status: Wallet.Cardano.StakePoolStatus; - ticker: string; + ticker?: string; }; // TODO consider using SDK type; https://input-output.atlassian.net/browse/LW-9242 From 985486aa794e968a19500ecc27b07b1e93ea07f7 Mon Sep 17 00:00:00 2001 From: John Oshalusi Date: Wed, 3 Apr 2024 14:54:23 +0100 Subject: [PATCH 22/74] fix: send alias during wallet creation (#1017) * fix: send alias during wallet creation * fix: properly position remove wallet event * test(extension): unblock e2e tests --------- Co-authored-by: januszjanus --- .../features/settings/components/SettingsRemoveWallet.tsx | 4 ++-- .../features/wallet-setup/components/WalletSetupWizard.tsx | 4 ++-- .../src/features/analytics/AnalyticsSettingsExtended.feature | 3 +-- .../src/features/analytics/AnalyticsSettingsPopup.feature | 3 +-- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx index c5986c127..2049d66cd 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/settings/components/SettingsRemoveWallet.tsx @@ -29,13 +29,13 @@ export const SettingsRemoveWallet = ({ popupView }: { popupView?: boolean }): Re setIsRemoveWalletAlertVisible(!isRemoveWalletAlertVisible); analytics.sendEventToPostHog( - isRemoveWalletAlertVisible ? PostHogAction.SettingsHoldUpBackClick : PostHogAction.SettingsHoldUpRemoveWalletClick + isRemoveWalletAlertVisible ? PostHogAction.SettingsHoldUpBackClick : PostHogAction.SettingsRemoveWalletClick ); }; const removeWallet = async () => { setDeletingWallet(true); - await analytics.sendEventToPostHog(PostHogAction.SettingsRemoveWalletClick, { + await analytics.sendEventToPostHog(PostHogAction.SettingsHoldUpRemoveWalletClick, { // eslint-disable-next-line camelcase $set: { wallet_accounts_quantity: await getWalletAccountsQtyString(walletRepository) } }); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx index cc1d61aa1..64b5c296f 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupWizard.tsx @@ -163,7 +163,6 @@ export const WalletSetupWizard = ({ const goToMyWallet = useCallback( async (cardanoWallet: Wallet.CardanoWallet = walletInstance) => { if (enhancedAnalyticsStatus === EnhancedAnalyticsOptInStatus.OptedIn) { - void analytics.sendAliasEvent(); const addresses = await firstValueFrom(cardanoWallet?.wallet?.addresses$.pipe(filter((a) => a.length > 0))); const hdWalletDiscovered = addresses.some((addr) => !isScriptAddress(addr) && addr.index > 0); if (hdWalletDiscovered) { @@ -213,8 +212,9 @@ export const WalletSetupWizard = ({ ); const handleSubmit = async (result: { password: string; walletName: string }) => { - sendAnalytics(postHogOnboardingActions[setupType]?.ENTER_WALLET); + void sendAnalytics(postHogOnboardingActions[setupType]?.ENTER_WALLET); await handleCompleteCreation(result.walletName, result.password); + void analytics.sendAliasEvent(); }; const handleMnemonicVerification = () => { diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature index 74de55cab..8d39b763a 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsExtended.feature @@ -170,8 +170,7 @@ Feature: Analytics - Settings - Extended View Then I validate that 2 analytics event(s) have been sent # this test should be executed as the last one in this suite - @LW-8559 @Pending - @issue=LW-10150 + @LW-8559 Scenario: Analytics - Extended View - Settings - Wallet removal events - Remove wallet When I open settings from header menu And I set up request interception for posthog analytics request(s) diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature index 5b472fd64..081aa9303 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSettingsPopup.feature @@ -187,8 +187,7 @@ Feature: Analytics - Settings - Popup View Then I validate that 2 analytics event(s) have been sent # this test should be executed as the last one in this suite - @LW-8571 @Pending - @issue=LW-10150 + @LW-8571 Scenario: Analytics - Popup View - Settings - Wallet removal events - Remove wallet Given I am on Tokens popup page When I open settings from header menu From caff1b8dcfa47409efbd48edc172d5db1cd408ec Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Thu, 4 Apr 2024 08:45:37 +0200 Subject: [PATCH 23/74] chore: remove code coverage integration (#1001) --- .github/workflows/build-dev-preview.yml | 4 +-- .github/workflows/ci.yml | 10 ++----- .github/workflows/packages-staking.yml | 7 +---- .github/workflows/post-integration.yml | 9 ++----- apps/browser-extension-wallet/package.json | 1 - .../test/jest.config.js | 16 ------------ package.json | 1 - packages/cardano/package.json | 3 +-- packages/cardano/test/jest.config.js | 26 ------------------- packages/common/package.json | 3 +-- packages/common/test/jest.config.js | 6 ----- packages/core/package.json | 3 +-- packages/core/test/jest.config.js | 7 ----- packages/e2e-tests/package.json | 1 - packages/icons/package.json | 3 +-- packages/staking/package.json | 3 +-- packages/ui/package.json | 1 - 17 files changed, 12 insertions(+), 92 deletions(-) diff --git a/.github/workflows/build-dev-preview.yml b/.github/workflows/build-dev-preview.yml index 944fc35fb..ef36f41f2 100644 --- a/.github/workflows/build-dev-preview.yml +++ b/.github/workflows/build-dev-preview.yml @@ -22,11 +22,11 @@ jobs: PRODUCTION_MODE_TRACKING: 'false' - name: Check for linter issues run: yarn lint - - name: Run unit tests, generate test coverage report + - name: Run unit tests env: AVAILABLE_CHAINS: 'Preprod,Preview,Mainnet' DEFAULT_CHAIN: 'Preprod' - run: yarn test:coverage --maxWorkers=2 --silent + run: yarn test --maxWorkers=2 --silent - name: Upload build uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e13f39ee..e7df0d01e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,20 +28,14 @@ jobs: PRODUCTION_MODE_TRACKING: ${{ startsWith(github.ref, 'refs/heads/release') && 'true' || 'false' }} - name: Check for linter issues run: yarn lint - - name: Run unit tests, generate test coverage report + - name: Run unit tests env: AVAILABLE_CHAINS: 'Preprod,Preview,Mainnet' DEFAULT_CHAIN: 'Preprod' NODE_OPTIONS: '--max_old_space_size=8192' - run: yarn test:coverage --maxWorkers=2 --silent + run: yarn test --maxWorkers=2 --silent - name: Upload build uses: actions/upload-artifact@v4 with: name: lace path: apps/browser-extension-wallet/dist - - name: Upload Coveralls report - uses: coverallsapp/github-action@v2 - with: - github-token: ${{ secrets.COVERALLS_REPO_TOKEN }} - allow-empty: true - compare-ref: main diff --git a/.github/workflows/packages-staking.yml b/.github/workflows/packages-staking.yml index f6f721871..418ee20ab 100644 --- a/.github/workflows/packages-staking.yml +++ b/.github/workflows/packages-staking.yml @@ -43,12 +43,7 @@ jobs: - name: Build dependencies of Staking Center run: yarn staking build-deps - name: Run tests - run: yarn workspace @lace/staking test:unit --coverage - - name: Upload test coverage artifacts - uses: actions/upload-artifact@v4 - with: - name: staking-coverage - path: packages/staking/coverage + run: yarn workspace @lace/staking test:unit - name: Build Staking dist run: yarn workspace @lace/staking build # TODO fix ladle build diff --git a/.github/workflows/post-integration.yml b/.github/workflows/post-integration.yml index e6ac4d596..b7b7a4e54 100644 --- a/.github/workflows/post-integration.yml +++ b/.github/workflows/post-integration.yml @@ -20,10 +20,5 @@ jobs: with: name: lightwallet path: apps/browser-extension-wallet/dist - - name: Run unit tests, generate test coverage report - run: yarn test:coverage --maxWorkers=2 --silent - - name: Upload Coveralls report - uses: coverallsapp/github-action@v2 - with: - github-token: ${{ secrets.COVERALLS_REPO_TOKEN }} - allow-empty: true + - name: Run unit tests + run: yarn test --maxWorkers=2 --silent diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 909554ce5..25a40563d 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -34,7 +34,6 @@ "prepare": "ts-patch install -s", "prettier": "run -T prettier --write .", "test": "run -T jest --config test/jest.config.js", - "test:coverage": "NODE_OPTIONS=\"--max-old-space-size=8192\" run -T test --coverage --silent", "test:e2e": "yarn exec echo \"No e2e tests on this app yet!\"", "watch": "NODE_OPTIONS='--openssl-legacy-provider' rm -rf dist & run -T webpack --config webpack.sw.dev.js --progress --watch & run -T webpack --config webpack.app.dev.js --progress --watch" }, diff --git a/apps/browser-extension-wallet/test/jest.config.js b/apps/browser-extension-wallet/test/jest.config.js index 7d5a17e5a..26402d83c 100644 --- a/apps/browser-extension-wallet/test/jest.config.js +++ b/apps/browser-extension-wallet/test/jest.config.js @@ -31,21 +31,5 @@ module.exports = createJestConfig({ '/test/__mocks__/ResizeObserver.js', '/test/helpers/assertions.js' ], - collectCoverageFrom: [ - 'src/components/**/*.{ts,tsx}', - 'src/features/**/*.{ts,tsx}', - 'src/lib/**/*.{ts,tsx}', - 'src/hooks/**/*.{ts,tsx}', - 'src/provider/**/*.{ts,tsx}', - 'src/utils/**/*.{ts,tsx}', - '!src/lib/**/background.ts', - '!src/utils/mocks/*.{ts,tsx}', - '!src/utils/token-prices-lovelace-list.ts', - '!src/utils/test-ids.ts', - '!src/utils/get-polling-config.ts', - '!src/utils/test-helpers.tsx', - '!src/utils/test-utils.ts', - '!src/utils/fake-api-request.ts' - ], setupFilesAfterEnv: ['./test/jest.setup.js', 'jest-canvas-mock'] }); diff --git a/package.json b/package.json index a4717929b..b51180278 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ "prestorybook:build": "yarn build", "storybook:build": "ENV=development build-storybook --log-level error", "test": "yarn workspaces foreach run test", - "test:coverage": "yarn workspaces foreach run test:coverage", "version": "standard-version", "watch": "yarn base-command-for-build-scripts --done-criteria 'created|(webpack [\\d\\.]+ compiled)|(.*STAKING.*CJS.*Build success)' -c watch", "watch-deps": "EXCLUDE_APP=true yarn watch" diff --git a/packages/cardano/package.json b/packages/cardano/package.json index 5ab27b610..ca7d9eea8 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -27,14 +27,13 @@ ], "scripts": { "build": "run -T rollup -c rollup.config.js", - "cleanup": "yarn exec rm -rf dist node_modules coverage .rollup.cache src/dist", + "cleanup": "yarn exec rm -rf dist node_modules .rollup.cache src/dist", "lint": "cd ../.. && yarn cardano:lint", "prepack": "yarn build", "prepare": "ts-patch install -s", "prestart": "yarn build", "start": "node dist/index.js", "test": "NODE_ENV=test run -T jest -c ./test/jest.config.js", - "test:coverage": "yarn test --coverage --silent", "tsc:declarationOnly": "tsc --project ./src/tsconfig.declarationOnly.json", "watch": "yarn build --watch" }, diff --git a/packages/cardano/test/jest.config.js b/packages/cardano/test/jest.config.js index 3a07fbda5..ca53ca549 100644 --- a/packages/cardano/test/jest.config.js +++ b/packages/cardano/test/jest.config.js @@ -21,32 +21,6 @@ module.exports = createJestConfig({ roots: ['/src'], testTimeout: 60000, testEnvironment: 'jsdom', - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.stories.tsx', - '!src/**/*.test.{ts,tsx}', - '!src/**/*.d.ts', - '!src/**/types.ts', - '!src/**/index.{ts,tsx}', - '!src/wallet/test/mocks/*', - // files bellow fails to collect coverage as they are not imported in the tests - '!src/wallet/lib/config.ts', - '!src/wallet/util/calculate-deposit-reclaim.ts', - '!src/wallet/lib/get-total-minimum-coins.ts', - '!src/wallet/lib/build-transaction-props.ts', - '!src/wallet/util/observable.ts', - '!src/wallet/lib/providers.ts', - '!src/wallet/lib/set-missing-coins.ts', - '!src/ui/components/Voting/CatalystRegisterStep.tsx', - '!src/wallet/lib/hardware-wallet.ts', - '!src/ui/components/Voting/CurrentCatalystFund.tsx', - '!src/ui/components/Voting/DownloadCatalystStep.tsx', - '!src/ui/components/Voting/VotingParticipation.tsx', - '!src/ui/components/Voting/CatalystScanStep.tsx', - '!src/ui/components/Voting/CatalystPinStep.tsx', - '!src/ui/components/Voting/WaitForNextFundCard.tsx', - '!src/ui/components/Voting/CatalystConfirmationStep.tsx' - ], setupFilesAfterEnv: ['./test/jest.setup.js', 'jest-canvas-mock'], workerThreads: true }); diff --git a/packages/common/package.json b/packages/common/package.json index bf68e524e..97a33e306 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -27,14 +27,13 @@ ], "scripts": { "build": "run -T rollup -c rollup.config.js", - "cleanup": "yarn exec rm -rf dist node_modules coverage .rollup.cache src/dist", + "cleanup": "yarn exec rm -rf dist node_modules .rollup.cache src/dist", "lint": "cd ../.. && yarn common:lint", "prepack": "yarn build", "prepare": "ts-patch install -s", "prestart": "yarn build", "start": "node dist/index.js", "test": "NODE_ENV=test run -T jest -c ./test/jest.config.js", - "test:coverage": "yarn test --coverage --silent", "watch": "yarn build --watch" }, "dependencies": { diff --git a/packages/common/test/jest.config.js b/packages/common/test/jest.config.js index 268b67655..4cf1a9121 100644 --- a/packages/common/test/jest.config.js +++ b/packages/common/test/jest.config.js @@ -13,11 +13,5 @@ module.exports = createJestConfig({ roots: ['/src'], testTimeout: 60000, testEnvironment: 'jsdom', - collectCoverageFrom: [ - 'src/ui/components/**/*.{ts,tsx}', - 'src/ui/lib/**/*.{ts,tsx}', - 'src/ui/hooks/**/*.{ts,tsx}', - 'src/ui/utils/**/*.{ts,tsx}' - ], setupFilesAfterEnv: ['./test/jest.setup.js', 'jest-canvas-mock'] }); diff --git a/packages/core/package.json b/packages/core/package.json index 3fc35d77a..a5d78d027 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,14 +28,13 @@ "scripts": { "build": "run -T rollup -c rollup.config.js", "build-storybook": "NODE_OPTIONS=--openssl-legacy-provider; build-storybook", - "cleanup": "yarn exec rm -rf dist node_modules coverage .rollup.cache src/dist", + "cleanup": "yarn exec rm -rf dist node_modules .rollup.cache src/dist", "lint": "cd ../.. && yarn core:lint", "prepack": "yarn build", "prestart": "yarn build", "start": "node dist/index.js", "storybook": "NODE_OPTIONS=--openssl-legacy-provider; start-storybook -p 6006", "test": "NODE_ENV=test run -T jest -c ./test/jest.config.js", - "test:coverage": "yarn test --coverage --silent", "typecheck": "tsc --noEmit", "watch": "yarn build --watch" }, diff --git a/packages/core/test/jest.config.js b/packages/core/test/jest.config.js index 0dbae336f..4cf1a9121 100644 --- a/packages/core/test/jest.config.js +++ b/packages/core/test/jest.config.js @@ -13,12 +13,5 @@ module.exports = createJestConfig({ roots: ['/src'], testTimeout: 60000, testEnvironment: 'jsdom', - collectCoverageFrom: [ - 'src/ui/components/**/*.{ts,tsx}', - 'src/ui/hooks/**/*.{ts,tsx}', - 'src/ui/lib/**/*.{ts,tsx}', - 'src/ui/utils/**/*.{ts,tsx}', - 'src/wallet/**/*.{ts,tsx}' - ], setupFilesAfterEnv: ['./test/jest.setup.js', 'jest-canvas-mock'] }); diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index c0b8624df..bd6542460 100755 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -9,7 +9,6 @@ "test:local:chrome": "../../node_modules/.bin/wdio run wdio.conf.chrome.ts", "test:local:edge": "../../node_modules/.bin/wdio run wdio.conf.edge.ts", "cleanup": "rm -rf node_modules logs reports screenshots", - "test:coverage": "yarn exec echo 'disabled due to lack of a framebuffer. Please use test:local for local testing'", "lint": "run -T eslint -c .eslintrc.js .", "lint:fix": "run -T eslint -c .eslintrc.js . --fix", "pack-chrome-extension": "node ./src/utils/packExtension.ts", diff --git a/packages/icons/package.json b/packages/icons/package.json index 8b642fc47..9275e7bd2 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -21,8 +21,7 @@ "build:cleanup": "rm -rf ./tmp", "build:svgr": "svgr ./raw --out-dir ./tmp --typescript", "build:tsc": "tsc --project tsconfig.json", - "test": "echo \"@lace/icons: no test specified\"", - "test:coverage": "echo \"@lace/icons: no test specified\"" + "test": "echo \"@lace/icons: no test specified\"" }, "devDependencies": { "@babel/cli": "^7.22.10", diff --git a/packages/staking/package.json b/packages/staking/package.json index 2c24d3c56..812373483 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -43,8 +43,7 @@ "prettier:check": "yarn run -T prettier --check '**/*.{js,ts,jsx,tsx,html,json,md}'", "prettier:fix": "yarn run -T prettier --write --loglevel warn '**/*.{js,ts,jsx,tsx,html,json,md}'", "test": "echo 'Staking package runs tests in its own CI'", - "test:all": "yarn test:unit --coverage --silent && yarn test:vr", - "test:coverage": "yarn test --coverage --silent", + "test:all": "yarn test:unit && yarn test:vr", "test:unit": "vitest run", "watch": "tsup --watch" }, diff --git a/packages/ui/package.json b/packages/ui/package.json index 4a0df6817..986326574 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -25,7 +25,6 @@ "test": "echo \"@lace/ui: no test specified\"", "test-storybook": "test-storybook", "test-storybook:ci": "NODE_OPTIONS=--openssl-legacy-provider; export STORYBOOK_TEST=1; concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook && http-server storybook-static --port 6006\" \"wait-on http://127.0.0.1:6006/ && yarn test-storybook\"", - "test:coverage": "echo \"@lace/ui: no test specified\"", "typecheck": "tsc --noEmit", "watch": "yarn build --watch" }, From 91a9a963158bc73c411f168756a6b1c1bce36c4a Mon Sep 17 00:00:00 2001 From: Emir Hodzic Date: Thu, 4 Apr 2024 09:37:41 +0200 Subject: [PATCH 24/74] LW-10137 onboarding tech debt (#991) * test(extension): refactoring onboarding revamp * test(extension): fixes on comments * test(extension): fixes 2 * test(extension): fixes 3 * test(extension): removing unused code in e2e * test(extension): fixes on PR --------- Co-authored-by: januszjanus --- .../onboarding/onboardingCommonAssert.ts | 4 +- .../onboarding/onboardingLegalPageAssert.ts | 47 --- .../onboardingMnemonicPageAssert.ts | 125 -------- .../onboardingRecoveryPhrasePageAssert.ts | 22 ++ .../onboardingWalletCreationPageAssert.ts | 17 - packages/e2e-tests/src/elements/MainLoader.ts | 16 +- .../elements/onboarding/WalletCreationPage.ts | 18 -- .../onboarding/commonOnboardingElements.ts | 16 + .../src/elements/onboarding/legalPage.ts | 26 -- .../src/elements/onboarding/mainPage.ts | 28 ++ .../src/elements/onboarding/mnemonicPage.ts | 57 ---- .../onboarding/recoveryPhraseLengthPage.ts | 31 -- .../elements/onboarding/recoveryPhrasePage.ts | 74 +++++ .../elements/onboarding/walletSetupPage.ts | 20 ++ .../src/features/DAppConnector.feature | 1 + .../src/features/ForgotPassword.feature | 8 +- .../features/OnboardingCreateWallet.feature | 8 +- .../features/OnboardingRestoreWallet.feature | 4 +- .../analytics/AnalyticsForgotPassword.feature | 4 +- .../AnalyticsOnboardingEvents.feature | 30 +- .../src/pageobject/onboardingPageObject.ts | 292 ------------------ .../pageobject/onboardingRevampPageObject.ts | 84 ----- .../pageobject/settingsExtendedPageObject.ts | 4 +- .../src/steps/forgotPasswordSteps.ts | 18 -- .../src/steps/multidelegationSteps.ts | 5 +- .../e2e-tests/src/steps/onboardingSteps.ts | 169 ++-------- 26 files changed, 234 insertions(+), 894 deletions(-) delete mode 100644 packages/e2e-tests/src/assert/onboarding/onboardingLegalPageAssert.ts delete mode 100644 packages/e2e-tests/src/assert/onboarding/onboardingMnemonicPageAssert.ts delete mode 100644 packages/e2e-tests/src/assert/onboarding/onboardingWalletCreationPageAssert.ts delete mode 100644 packages/e2e-tests/src/elements/onboarding/WalletCreationPage.ts delete mode 100644 packages/e2e-tests/src/elements/onboarding/legalPage.ts delete mode 100644 packages/e2e-tests/src/elements/onboarding/mnemonicPage.ts delete mode 100644 packages/e2e-tests/src/elements/onboarding/recoveryPhraseLengthPage.ts delete mode 100644 packages/e2e-tests/src/pageobject/onboardingPageObject.ts delete mode 100644 packages/e2e-tests/src/pageobject/onboardingRevampPageObject.ts diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingCommonAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingCommonAssert.ts index d78f7239e..b725dd2f9 100644 --- a/packages/e2e-tests/src/assert/onboarding/onboardingCommonAssert.ts +++ b/packages/e2e-tests/src/assert/onboarding/onboardingCommonAssert.ts @@ -66,11 +66,11 @@ class OnboardingCommonAssert { await t('settings.legals.cookiePolicy') ); await this.commonOnboardingElements.privacyPolicyLink.waitForDisplayed(); - expect(await this.commonOnboardingElements.privacyPolicyLink.getText()).to.equalIgnoreCase( + expect(await this.commonOnboardingElements.privacyPolicyLink.getText()).to.equal( await t('settings.legals.privacyPolicy') ); await this.commonOnboardingElements.termsOfServiceLink.waitForDisplayed(); - expect(await this.commonOnboardingElements.termsOfServiceLink.getText()).to.equalIgnoreCase( + expect(await this.commonOnboardingElements.termsOfServiceLink.getText()).to.equal( await t('settings.legals.termsOfService') ); } diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingLegalPageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingLegalPageAssert.ts deleted file mode 100644 index dd5a5af04..000000000 --- a/packages/e2e-tests/src/assert/onboarding/onboardingLegalPageAssert.ts +++ /dev/null @@ -1,47 +0,0 @@ -import OnboardingLegalPage from '../../elements/onboarding/legalPage'; -import { t } from '../../utils/translationService'; -import { expect } from 'chai'; -import OnboardingCommonAssert from './onboardingCommonAssert'; -import { removeWhitespacesFromText } from '../../utils/textUtils'; -import { readFromFile } from '../../utils/fileUtils'; - -class OnboardingLegalPageAssert extends OnboardingCommonAssert { - async assertSeeLegalPageText() { - await OnboardingLegalPage.legalText.waitForDisplayed(); - const expectedTerms = await removeWhitespacesFromText( - readFromFile(__dirname, '../settings/termsAndConditions.txt') - ); - const actualTerms = await removeWhitespacesFromText( - (await OnboardingLegalPage.legalText.getText()).replace('I accept the Terms of Use', '') - ); - expect(actualTerms).to.equal(expectedTerms); - } - - async assertSeeTermsAndConditionsConfirmation() { - await OnboardingLegalPage.termsCheckbox.scrollIntoView(); - await OnboardingLegalPage.termsCheckbox.waitForEnabled(); - await OnboardingLegalPage.termsCheckboxDescription.waitForDisplayed(); - expect(await OnboardingLegalPage.termsCheckboxDescription.getText()).to.equal('I accept the Terms of Use'); - } - - async assertSeeLegalPage() { - await this.assertSeeStepTitle(await t('core.walletSetupLegalStep.title')); - await this.assertSeeLegalPageText(); - await this.assertSeeTermsAndConditionsConfirmation(); - await this.assertSeeBackButton(); - await this.assertSeeNextButton(); - await this.assertSeeLegalLinks(); - await this.assertSeeHelpAndSupportButton(); - } - - async assertNoTooltipVisible() { - await OnboardingLegalPage.termsTooltip.waitForDisplayed({ reverse: true }); - } - - async assertTooltipVisible() { - await OnboardingLegalPage.termsTooltip.waitForDisplayed(); - expect(await OnboardingLegalPage.termsTooltip.getText()).to.equal(await t('core.walletSetupLegalStep.toolTipText')); - } -} - -export default new OnboardingLegalPageAssert(); diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingMnemonicPageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingMnemonicPageAssert.ts deleted file mode 100644 index e47186205..000000000 --- a/packages/e2e-tests/src/assert/onboarding/onboardingMnemonicPageAssert.ts +++ /dev/null @@ -1,125 +0,0 @@ -/* eslint-disable no-undef */ -import OnboardingMnemonicPage from '../../elements/onboarding/mnemonicPage'; -import { t } from '../../utils/translationService'; -import { expect } from 'chai'; -import OnboardingCommonAssert from './onboardingCommonAssert'; -import { browser } from '@wdio/globals'; - -class OnboardingMnemonicPageAssert extends OnboardingCommonAssert { - async assertSeeMnemonicWords(expectedNumber: number) { - expect(await OnboardingMnemonicPage.mnemonicWords.length).to.equal(expectedNumber); - } - - async assertSeeMnemonicInputs(expectedNumber: number) { - expect(await OnboardingMnemonicPage.mnemonicInputs.length).to.equal(expectedNumber); - } - - async assertSeeMnemonicInputWithDisabledAutocomplete() { - const mnemonicInput = (await OnboardingMnemonicPage.mnemonicInputs[0]) as WebdriverIO.Element; - expect(await mnemonicInput?.getAttribute('autocomplete')).to.equal('off'); - } - - async assertSeeMnemonicInputWithText(mnemonicIndex: number, expectedText: string) { - const currentMnemonicInput = (await OnboardingMnemonicPage.mnemonicInputs[mnemonicIndex]) as WebdriverIO.Element; - expect(await currentMnemonicInput?.getValue()).to.equal(expectedText); - } - - async assertMnemonicInputLength(mnemonicIndex: number, expectedLength: number) { - const currentMnemonicInput = (await OnboardingMnemonicPage.mnemonicInputs[mnemonicIndex]) as WebdriverIO.Element; - const currentMnemonicInputLength = currentMnemonicInput ? (await currentMnemonicInput.getValue()).length : 0; - expect(currentMnemonicInputLength).to.equal(expectedLength); - } - - async assertSeeMnemonicAutocompleteOptions(expectedOptions: string[]) { - await OnboardingMnemonicPage.mnemonicAutocompleteDropdown.waitForDisplayed(); - const actualOptions = await OnboardingMnemonicPage.getMnemonicAutocompleteOptionsValues(); - expect(actualOptions).to.deep.equal(expectedOptions); - } - - async assertNotSeeMnemonicAutocompleteOptions() { - await OnboardingMnemonicPage.mnemonicAutocompleteDropdown.waitForDisplayed({ reverse: true, timeout: 1000 }); - } - - async assertSeeFooterValue(expectedFirstValue: number, expectedSecondValue = 24) { - const expectedString = `${expectedFirstValue} / ${expectedSecondValue}`; - await OnboardingMnemonicPage.stepInfoText.waitForDisplayed(); - expect(await OnboardingMnemonicPage.stepInfoText.getText()).to.equal(expectedString); - } - - async assertMnemonicWordsAreTheSame( - mnemonicWords: string[], - mnemonicWordsReference: string[], - shouldBeEqual: boolean - ) { - shouldBeEqual - ? expect(mnemonicWords).to.deep.equal(mnemonicWordsReference) - : expect(mnemonicWords).to.not.deep.equal(mnemonicWordsReference); - } - - async assertSeeFindOutMoreLink(): Promise { - await OnboardingMnemonicPage.findOutMoreLink.waitForDisplayed(); - expect(await OnboardingMnemonicPage.findOutMoreLink.getText()).to.equal( - await t('core.walletSetupMnemonicStep.passphraseInfo3') - ); - const expectedHref = 'https://www.lace.io/faq?question=what-happens-if-i-lose-my-recovery-phrase'; - const actualHref = await OnboardingMnemonicPage.findOutMoreLink.getAttribute('href'); - expect(actualHref).to.equal(expectedHref); - } - - async assertSeeMnemonicWriteDownPage(expectedFooterNumber: number) { - await this.assertSeeStepTitle(await t('core.walletSetupMnemonicStep.writePassphrase')); - const expectedSubtitle = `${await t('core.walletSetupMnemonicStep.passphraseInfo1')}\n${await t( - 'core.walletSetupMnemonicStep.passphraseInfo2' - )} ${await t('core.walletSetupMnemonicStep.passphraseInfo3')}`; - await this.assertSeeStepSubtitle(expectedSubtitle); - await this.assertSeeFindOutMoreLink(); - await this.assertSeeMnemonicWords(8); - await this.assertSeeFooterValue(expectedFooterNumber); - await this.assertSeeBackButton(); - await this.assertSeeNextButton(); - await this.assertSeeLegalLinks(); - await this.assertSeeHelpAndSupportButton(); - } - - async assertSeeMnemonicVerificationWordsPage(expectedFooterFirstNumber: number, expectedFooterSecondNumber = 24) { - await this.assertSeeStepTitle(await t('core.walletSetupMnemonicStep.enterPassphrase')); - const url = await browser.getUrl(); - const expectedSubtitle = url.endsWith('/create') - ? await t('core.walletSetupMnemonicStep.body') // create flow subtitle - : await t('core.walletSetupMnemonicStep.enterPassphraseDescription'); // restore flow subtitle - await this.assertSeeStepSubtitle(expectedSubtitle); - await this.assertSeeFooterValue(expectedFooterFirstNumber, expectedFooterSecondNumber); - // Check for LW-5757 - await this.assertSeeMnemonicInputWithDisabledAutocomplete(); - switch (expectedFooterSecondNumber) { - case 12: - Number(expectedFooterFirstNumber) === 8 - ? await this.assertSeeMnemonicInputs(8) - : await this.assertSeeMnemonicInputs(4); - break; - case 15: - Number(expectedFooterFirstNumber) === 8 - ? await this.assertSeeMnemonicInputs(8) - : await this.assertSeeMnemonicInputs(7); - break; - case 24: - await this.assertSeeMnemonicInputs(8); - break; - } - - await this.assertSeeBackButton(); - await this.assertSeeNextButton(); - await this.assertSeeLegalLinks(); - await this.assertSeeHelpAndSupportButton(); - } - - async assertSeeMnemonicError(shouldBeDisplayed: boolean) { - await OnboardingMnemonicPage.errorMessage.waitForDisplayed({ reverse: !shouldBeDisplayed }); - if (shouldBeDisplayed) { - const actualErrorText = await OnboardingMnemonicPage.errorMessage.getText(); - expect(actualErrorText).to.equal(await t('core.walletSetupMnemonicStep.passphraseError')); - } - } -} - -export default new OnboardingMnemonicPageAssert(); diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingRecoveryPhrasePageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingRecoveryPhrasePageAssert.ts index f2ec6fd7d..bd344d453 100644 --- a/packages/e2e-tests/src/assert/onboarding/onboardingRecoveryPhrasePageAssert.ts +++ b/packages/e2e-tests/src/assert/onboarding/onboardingRecoveryPhrasePageAssert.ts @@ -1,3 +1,4 @@ +/* eslint-disable no-undef */ import recoveryPhrasePage from '../../elements/onboarding/recoveryPhrasePage'; import { t } from '../../utils/translationService'; import { expect } from 'chai'; @@ -5,6 +6,27 @@ import OnboardingCommonAssert from './onboardingCommonAssert'; import { RecoveryPhrase } from '../../types/onboarding'; class OnboardingRecoveryPhrasePageAssert extends OnboardingCommonAssert { + async assertSeeMnemonicInputWithText(mnemonicIndex: number, expectedText: string) { + const currentMnemonicInput = (await recoveryPhrasePage.mnemonicInputs[mnemonicIndex]) as WebdriverIO.Element; + expect(await currentMnemonicInput?.getValue()).to.equal(expectedText); + } + + async assertMnemonicInputLength(mnemonicIndex: number, expectedLength: number) { + const currentMnemonicInput = (await recoveryPhrasePage.mnemonicInputs[mnemonicIndex]) as WebdriverIO.Element; + const currentMnemonicInputLength = currentMnemonicInput ? (await currentMnemonicInput.getValue()).length : 0; + expect(currentMnemonicInputLength).to.equal(expectedLength); + } + + async assertSeeMnemonicAutocompleteOptions(expectedOptions: string[]) { + await recoveryPhrasePage.mnemonicAutocompleteDropdown.waitForDisplayed(); + const actualOptions = await recoveryPhrasePage.getMnemonicAutocompleteOptionsValues(); + expect(actualOptions).to.deep.equal(expectedOptions); + } + + async assertNotSeeMnemonicAutocompleteOptions() { + await recoveryPhrasePage.mnemonicAutocompleteDropdown.waitForDisplayed({ reverse: true, timeout: 1000 }); + } + async assertSeeMnemonicVerificationPage(flowType: 'Create' | 'Restore', mnemonicWordsLength: RecoveryPhrase) { const subtitle = flowType === 'Create' diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingWalletCreationPageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingWalletCreationPageAssert.ts deleted file mode 100644 index 05c7c81a1..000000000 --- a/packages/e2e-tests/src/assert/onboarding/onboardingWalletCreationPageAssert.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { t } from '../../utils/translationService'; -import WalletCreationPage from '../../elements/onboarding/WalletCreationPage'; -import OnboardingCommonAssert from './onboardingCommonAssert'; - -class OnboardingWalletCreationPageAssert extends OnboardingCommonAssert { - async assertSeeCreatingWalletPage() { - await WalletCreationPage.walletLoader.waitForDisplayed(); - await this.assertSeeStepTitle(await t('core.walletSetupCreateStep.title')); - await this.assertSeeStepSubtitle(await t('core.walletSetupCreateStep.description')); - } - - async assertCreatingWalletDuration(duration: number) { - await WalletCreationPage.walletLoader.waitForDisplayed({ timeout: 1000 * duration, reverse: true }); - } -} - -export default new OnboardingWalletCreationPageAssert(); diff --git a/packages/e2e-tests/src/elements/MainLoader.ts b/packages/e2e-tests/src/elements/MainLoader.ts index 9fe99cc30..073665ba1 100644 --- a/packages/e2e-tests/src/elements/MainLoader.ts +++ b/packages/e2e-tests/src/elements/MainLoader.ts @@ -1,18 +1,20 @@ class MainLoader { private MAIN_LOADER_COMPONENT = '[data-testid="main-loader"]'; - private MAIN_LOADER_IMAGE = '[data-testid="main-image"]'; - private MAIN_LOADER_TEXT = '[data-testid="main-text"]'; + private MAIN_LOADER_TEXT = '[data-testid="main-loader-text"]'; - get component() { + get mainLoaderComponent() { return $(this.MAIN_LOADER_COMPONENT); } - get image() { - return $(this.MAIN_LOADER_IMAGE); + get mainLoaderText() { + return $(this.MAIN_LOADER_TEXT); } - get text() { - return $(this.MAIN_LOADER_TEXT); + async waitUntilLoaderDisappears() { + await browser.pause(500); + if (await this.mainLoaderComponent.isDisplayed()) { + await this.mainLoaderComponent.waitForDisplayed({ timeout: 150_000, reverse: true }); + } } } diff --git a/packages/e2e-tests/src/elements/onboarding/WalletCreationPage.ts b/packages/e2e-tests/src/elements/onboarding/WalletCreationPage.ts deleted file mode 100644 index 4cb9ec64c..000000000 --- a/packages/e2e-tests/src/elements/onboarding/WalletCreationPage.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable no-undef */ -import { ChainablePromiseElement } from 'webdriverio'; -import CommonOnboardingElements from './commonOnboardingElements'; - -class WalletCreationPage extends CommonOnboardingElements { - private WALLET_LOADER = '//div[@data-testid="wallet-create-loader"]'; - private MAIN_LOADER_TEXT = '//p[@data-testid="main-loader-text"]'; - - get walletLoader(): ChainablePromiseElement { - return $(this.WALLET_LOADER); - } - - get mainLoaderText(): ChainablePromiseElement { - return $(this.MAIN_LOADER_TEXT); - } -} - -export default new WalletCreationPage(); diff --git a/packages/e2e-tests/src/elements/onboarding/commonOnboardingElements.ts b/packages/e2e-tests/src/elements/onboarding/commonOnboardingElements.ts index 214208a3b..d1ef40b4e 100644 --- a/packages/e2e-tests/src/elements/onboarding/commonOnboardingElements.ts +++ b/packages/e2e-tests/src/elements/onboarding/commonOnboardingElements.ts @@ -57,4 +57,20 @@ export default class CommonOnboardingElements { await this.backButton.waitForClickable(); await this.backButton.click(); } + + async clickOnLegalLinkOnFooter(linkText: string): Promise { + switch (linkText) { + case 'Cookie policy': + await this.cookiePolicyLink.click(); + break; + case 'Privacy policy': + await this.privacyPolicyLink.click(); + break; + case 'Terms of service': + await this.termsOfServiceLink.click(); + break; + default: + throw new Error(`Unsupported legal link text - ${linkText}`); + } + } } diff --git a/packages/e2e-tests/src/elements/onboarding/legalPage.ts b/packages/e2e-tests/src/elements/onboarding/legalPage.ts deleted file mode 100644 index 5e5646b1d..000000000 --- a/packages/e2e-tests/src/elements/onboarding/legalPage.ts +++ /dev/null @@ -1,26 +0,0 @@ -import CommonOnboardingElements from './commonOnboardingElements'; - -class OnboardingLegalPage extends CommonOnboardingElements { - private LEGAL_TEXT = '//div[@data-testid="wallet-setup-legal-text"]'; - private TERMS_CHECKBOX = 'input[data-testid="wallet-setup-legal-terms-checkbox"]'; - private TERMS_CHECKBOX_DESCRIPTION = '[data-testid="wallet-setup-legal-terms-checkbox-description"]'; - private TERMS_TOOLTIP = '//div[@role="tooltip"]'; - - get legalText() { - return $(this.LEGAL_TEXT); - } - - get termsCheckbox() { - return $(this.TERMS_CHECKBOX); - } - - get termsCheckboxDescription() { - return $(this.TERMS_CHECKBOX_DESCRIPTION); - } - - get termsTooltip() { - return $(this.TERMS_TOOLTIP); - } -} - -export default new OnboardingLegalPage(); diff --git a/packages/e2e-tests/src/elements/onboarding/mainPage.ts b/packages/e2e-tests/src/elements/onboarding/mainPage.ts index 644a9edff..01b4cbb21 100644 --- a/packages/e2e-tests/src/elements/onboarding/mainPage.ts +++ b/packages/e2e-tests/src/elements/onboarding/mainPage.ts @@ -1,4 +1,8 @@ +import { TestWalletName, getTestWallet } from '../../support/walletConfiguration'; import CommonOnboardingElements from './commonOnboardingElements'; +import recoveryPhrasePage from './recoveryPhrasePage'; +import walletSetupPage from './walletSetupPage'; +import topNavigationAssert from '../../assert/topNavigationAssert'; class OnboardingMainPage extends CommonOnboardingElements { private LOGO_IMAGE = '[data-testid="wallet-setup-logo"]'; @@ -91,6 +95,30 @@ class OnboardingMainPage extends CommonOnboardingElements { get agreementPrivacyPolicyLink() { return $(this.AGREEMENT_PRIVACY_POLICY_LINK); } + + async clickOnLegalLink(linkText: string): Promise { + switch (linkText) { + case 'Privacy policy': + await this.agreementPrivacyPolicyLink.click(); + break; + case 'Terms of service': + await this.agreementTermsOfServiceLink.click(); + break; + default: + throw new Error(`Unsupported legal link text - ${linkText}`); + } + } + + async restoreWallet(): Promise { + await this.restoreWalletButton.click(); + await recoveryPhrasePage.enterMnemonicWords(getTestWallet(TestWalletName.TestAutomationWallet).mnemonic ?? []); + await recoveryPhrasePage.nextButton.click(); + await walletSetupPage.setWalletNameInput('ValidName'); + await walletSetupPage.setWalletPasswordInput('N_8J@bne87A'); + await walletSetupPage.setWalletPasswordConfirmInput('N_8J@bne87A'); + await walletSetupPage.clickEnterWalletButton(); + await topNavigationAssert.assertLogoPresent(); + } } export default new OnboardingMainPage(); diff --git a/packages/e2e-tests/src/elements/onboarding/mnemonicPage.ts b/packages/e2e-tests/src/elements/onboarding/mnemonicPage.ts deleted file mode 100644 index 4f04a1c15..000000000 --- a/packages/e2e-tests/src/elements/onboarding/mnemonicPage.ts +++ /dev/null @@ -1,57 +0,0 @@ -/* eslint-disable no-undef */ -import { ChainablePromiseArray } from 'webdriverio/build/types'; -import { ChainablePromiseElement } from 'webdriverio'; -import CommonOnboardingElements from './commonOnboardingElements'; - -export class OnboardingMnemonicPage extends CommonOnboardingElements { - private MNEMONIC_WORD = '[data-testid="mnemonic-word-writedown"]'; - private MNEMONIC_INPUT = '[data-testid="mnemonic-word-input"]'; - private MNEMONIC_WORD_AUTOCOMPLETE_OPTIONS = '.ant-select-item-option-content'; - private MNEMONIC_AUTOCOMPLETE_DROPDOWN = '.ant-select-dropdown'; - private MNEMONIC_ERROR_MESSAGE = '[data-testid="passphrase-error"]'; - private FIND_OUT_MORE_LINK = '[data-testid="find-out-more-link"]'; - private STEP_INFO_TEXT = '[data-testid="step-info-text"]'; - - get mnemonicAutocompleteDropdown(): ChainablePromiseElement { - return $(this.MNEMONIC_AUTOCOMPLETE_DROPDOWN); - } - - get mnemonicWords(): ChainablePromiseArray { - return $$(this.MNEMONIC_WORD); - } - - get mnemonicAutocompleteOptions(): ChainablePromiseArray { - return $$(this.MNEMONIC_WORD_AUTOCOMPLETE_OPTIONS); - } - - get mnemonicInputs(): ChainablePromiseArray { - return $$(this.MNEMONIC_INPUT); - } - - get errorMessage(): ChainablePromiseElement { - return $(this.MNEMONIC_ERROR_MESSAGE); - } - - get findOutMoreLink(): ChainablePromiseElement { - return $(this.FIND_OUT_MORE_LINK); - } - - get stepInfoText(): ChainablePromiseElement { - return $(this.STEP_INFO_TEXT); - } - - async getMnemonicAutocompleteOptionsValues(): Promise { - return this.mnemonicAutocompleteOptions.map(async (option) => await option.getText()); - } - - async getMnemonicWordTexts(): Promise { - const results: string[] = []; - const elements = await this.mnemonicWords; - for (const element of elements) { - results.push(await element.getText()); - } - return results; - } -} - -export default new OnboardingMnemonicPage(); diff --git a/packages/e2e-tests/src/elements/onboarding/recoveryPhraseLengthPage.ts b/packages/e2e-tests/src/elements/onboarding/recoveryPhraseLengthPage.ts deleted file mode 100644 index 838230bca..000000000 --- a/packages/e2e-tests/src/elements/onboarding/recoveryPhraseLengthPage.ts +++ /dev/null @@ -1,31 +0,0 @@ -import CommonOnboardingElements from './commonOnboardingElements'; - -class RecoveryPhraseLengthPage extends CommonOnboardingElements { - private RADIO_BUTTON_12_WORDS = '//input[@data-testid="12-word-passphrase-radio-button"]'; - private RADIO_BUTTON_15_WORDS = '//input[@data-testid="15-word-passphrase-radio-button"]'; - private RADIO_BUTTON_24_WORDS = '//input[@data-testid="24-word-passphrase-radio-button"]'; - private RADIO_BUTTON_12_WORDS_LABEL = '//input[@data-testid="12-word-passphrase-radio-button"]/../..'; - private RADIO_BUTTON_15_WORDS_LABEL = '//input[@data-testid="15-word-passphrase-radio-button"]/../..'; - private RADIO_BUTTON_24_WORDS_LABEL = '//input[@data-testid="24-word-passphrase-radio-button"]/../..'; - - get radioButton12wordsButton() { - return $(this.RADIO_BUTTON_12_WORDS); - } - get radioButton15wordsButton() { - return $(this.RADIO_BUTTON_15_WORDS); - } - get radioButton24wordsButton() { - return $(this.RADIO_BUTTON_24_WORDS); - } - get radioButton12wordsButtonLabel() { - return $(this.RADIO_BUTTON_12_WORDS_LABEL); - } - get radioButton15wordsButtonLabel() { - return $(this.RADIO_BUTTON_15_WORDS_LABEL); - } - get radioButton24wordsButtonLabel() { - return $(this.RADIO_BUTTON_24_WORDS_LABEL); - } -} - -export default new RecoveryPhraseLengthPage(); diff --git a/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts b/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts index a8333333e..7f4e3c019 100644 --- a/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts +++ b/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts @@ -3,10 +3,13 @@ import { ChainablePromiseArray } from 'webdriverio/build/types'; import { ChainablePromiseElement } from 'webdriverio'; import CommonOnboardingElements from './commonOnboardingElements'; import { RecoveryPhrase } from '../../types/onboarding'; +import { clearInputFieldValue, setInputFieldValue } from '../../utils/inputFieldUtils'; +import testContext from '../../utils/testContext'; class RecoveryPhrasePage extends CommonOnboardingElements { private MNEMONIC_WORD = '[data-testid="mnemonic-word-writedown"]'; private MNEMONIC_INPUT = '[data-testid="mnemonic-word-input"]'; + private MNEMONIC_AUTOCOMPLETE_DROPDOWN = '.ant-select-dropdown'; private MNEMONIC_WORD_AUTOCOMPLETE_OPTIONS = '.ant-select-item-option-content'; private MNEMONIC_ERROR_MESSAGE = '[data-testid="passphrase-error"]'; private MNEMONIC_LENGTH_SELECTOR_12 = '//p[@data-testid="wallet-setup-step-subtitle"]//div[@title="12"]'; @@ -15,6 +18,7 @@ class RecoveryPhrasePage extends CommonOnboardingElements { private WATCH_VIDEO_LINK = '[data-testid="watch-video-link"]'; private COPY_TO_CLIPBOARD_BUTTON = '[data-testid="copy-to-clipboard-button"]'; private PASTE_FROM_CLIPBOARD_BUTTON = '[data-testid="paste-from-clipboard-button"]'; + private mnemonicWordsList: string[] = []; get mnemonicWords(): ChainablePromiseArray { return $$(this.MNEMONIC_WORD); @@ -28,6 +32,10 @@ class RecoveryPhrasePage extends CommonOnboardingElements { return $$(this.MNEMONIC_INPUT); } + get mnemonicAutocompleteDropdown(): ChainablePromiseElement { + return $(this.MNEMONIC_AUTOCOMPLETE_DROPDOWN); + } + get errorMessage(): ChainablePromiseElement { return $(this.MNEMONIC_ERROR_MESSAGE); } @@ -72,6 +80,39 @@ class RecoveryPhrasePage extends CommonOnboardingElements { } } + async clickOnInput() { + const inputs = await this.mnemonicInputs; + await inputs[0].click(); + } + + async clickHeaderToLoseFocus() { + await this.stepHeader.click(); + } + async addCharToMnemonicField(characters: string, inputNumber: number) { + const inputs = await this.mnemonicInputs; + await inputs[inputNumber].addValue(characters); + } + + async changeRandomMnemonicField() { + const randomFieldNo = Math.floor(Math.random() * 8); + const inputs = await this.mnemonicInputs; + testContext.save('mnemonic', { index: randomFieldNo, value: await inputs[randomFieldNo].getValue() }); + await inputs[randomFieldNo].click(); + await browser.keys('.'); + await this.stepTitle.click(); // Click outside input fields to trigger validation + } + + async clearRandomMnemonicField() { + const randomFieldNo = Math.floor(Math.random() * 8); + const inputs = await this.mnemonicInputs; + await clearInputFieldValue(inputs[randomFieldNo]); + } + + async restorePreviousMnemonicWord() { + const mnemonic = testContext.load('mnemonic') as { value: string; index: number }; + await this.enterMnemonicWord(mnemonic.value, mnemonic.index); + } + async getMnemonicAutocompleteOptionsValues(): Promise { return this.mnemonicAutocompleteOptions.map(async (option) => await option.getText()); } @@ -84,6 +125,39 @@ class RecoveryPhrasePage extends CommonOnboardingElements { } return results; } + + async enterMnemonicWord(value: string, inputNumber = 0, shouldTriggerValidation = true) { + const inputs = await this.mnemonicInputs; + await setInputFieldValue(inputs[inputNumber], value); + if (shouldTriggerValidation) { + await this.stepTitle.click(); // Click outside input fields to trigger validation + } + } + + async enterMnemonicWords(mnemonicWordsList: string[] = []): Promise { + if (mnemonicWordsList.length > 0) { + this.mnemonicWordsList = mnemonicWordsList; + } + const mnemonicInputs = await this.mnemonicInputs; + for (let i = 0; i < this.mnemonicWordsList.length; i++) { + await clearInputFieldValue(mnemonicInputs[i]); + await mnemonicInputs[i].setValue(this.mnemonicWordsList[i]); + } + } + + async goToMnemonicVerificationPage( + flowType: 'Create' | 'Restore', + mnemonicWords: string[] = [], + fillValues = true + ): Promise { + if (flowType === 'Create') { + this.mnemonicWordsList = await this.getMnemonicWordTexts(); + await this.nextButton.click(); + } + if (fillValues) { + flowType === 'Create' ? await this.enterMnemonicWords() : await this.enterMnemonicWords(mnemonicWords); + } + } } export default new RecoveryPhrasePage(); diff --git a/packages/e2e-tests/src/elements/onboarding/walletSetupPage.ts b/packages/e2e-tests/src/elements/onboarding/walletSetupPage.ts index 546f0039d..d0f70ab66 100644 --- a/packages/e2e-tests/src/elements/onboarding/walletSetupPage.ts +++ b/packages/e2e-tests/src/elements/onboarding/walletSetupPage.ts @@ -1,5 +1,7 @@ import CommonOnboardingElements from './commonOnboardingElements'; import { setInputFieldValue } from '../../utils/inputFieldUtils'; +import recoveryPhrasePage from './recoveryPhrasePage'; +import onboardingWalletSetupPageAssert from '../../assert/onboarding/onboardingWalletSetupPageAssert'; class WalletSetupPage extends CommonOnboardingElements { private SUBTITLE = '[data-testid="wallet-setup-step-subtitle"]'; @@ -60,9 +62,27 @@ class WalletSetupPage extends CommonOnboardingElements { await setInputFieldValue(await this.walletPasswordConfirmInput, value); } + async clickEnterWalletButton(): Promise { + await onboardingWalletSetupPageAssert.assertEnterWalletButtonIsEnabled(); + await this.nextButton.click(); + } + getNumberOfActiveComplexityBars(): Promise { return this.activeComplexityBars.length; } + async goToWalletSetupPage( + flowType: 'Create' | 'Restore', + mnemonicWords: string[] = [], + fillValues = true + ): Promise { + await recoveryPhrasePage.goToMnemonicVerificationPage(flowType, mnemonicWords); + await recoveryPhrasePage.nextButton.click(); + if (fillValues) { + await this.setWalletNameInput('TestAutomationWallet'); + await this.setWalletPasswordInput('N_8J@bne87A'); + await this.setWalletPasswordConfirmInput('N_8J@bne87A'); + } + } } export default new WalletSetupPage(); diff --git a/packages/e2e-tests/src/features/DAppConnector.feature b/packages/e2e-tests/src/features/DAppConnector.feature index 0c643b6c9..ceef9f7ef 100644 --- a/packages/e2e-tests/src/features/DAppConnector.feature +++ b/packages/e2e-tests/src/features/DAppConnector.feature @@ -147,6 +147,7 @@ Feature: DAppConnector - Common And I switch to window with Lace And I close all remaining tabs except current one And I remove wallet + Then I accept analytics banner on "Get started" page And I restore a wallet And Wallet is synced And I switch network to: "Preprod" in extended mode diff --git a/packages/e2e-tests/src/features/ForgotPassword.feature b/packages/e2e-tests/src/features/ForgotPassword.feature index 1311fb696..970deaaa3 100644 --- a/packages/e2e-tests/src/features/ForgotPassword.feature +++ b/packages/e2e-tests/src/features/ForgotPassword.feature @@ -44,7 +44,7 @@ Feature: Forgot password And I click on "Proceed" button on "Forgot password?" modal And I switch to tab with restore wallet process And I go to "Wallet setup" page from "Restore" wallet flow and "not fill" values - When I enter password: "" and password confirmation: "" + # When I enter password: "" and password confirmation: "" Then Password recommendation: "", complexity bar level: "" and password confirmation error: "" are displayed Examples: | password | password_conf | passw_err | complex_bar_lvl | passw_conf_err | @@ -63,7 +63,7 @@ Feature: Forgot password And I click on "Proceed" button on "Forgot password?" modal And I switch to tab with restore wallet process Then "Wallet setup" page is displayed - When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" And I click "Next" button during wallet setup When I add characters "asd" in word 7 Then "Next" button is disabled during onboarding process @@ -77,7 +77,7 @@ Feature: Forgot password And I click on "Proceed" button on "Forgot password?" modal And I switch to tab with restore wallet process Then "Wallet setup" page is displayed - When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" And I click "Next" button during wallet setup Then "Mnemonic verification" page is displayed from "Forgot password" flow with 24 words And I enter 24 correct mnemonic words on "Mnemonic verification" page @@ -93,7 +93,7 @@ Feature: Forgot password And I click on "Proceed" button on "Forgot password?" modal And all wallet related data is removed And I switch to tab with restore wallet process - And I am on page of restoration flow + # And I am on page of restoration flow And I leave "Forgot password" flow And I accept analytics banner on "Get started" page Then "Get started" page is displayed diff --git a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature index b0c2f0057..d06de903f 100755 --- a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature @@ -16,9 +16,8 @@ Feature: Onboarding - Create wallet | Terms of service | - @LW-2433 @Pending - @issue=LW-10087 - Scenario: Create Wallet - Name your wallet - back button + @LW-2433 + Scenario: Create Wallet - Wallet setup page - back button Given I click "Create" button on wallet setup page And I go to "Wallet setup" page from "Create" wallet flow When I click "Back" button during wallet setup @@ -246,8 +245,7 @@ Feature: Onboarding - Create wallet | dark | | light | - @LW-8500 @Pending - @issue=LW-8890 + @LW-8500 Scenario: Create Wallet - Mnemonic verification - incorrect word Given I click "Create" button on wallet setup page And I go to "Mnemonic verification" page from "Create" wallet flow diff --git a/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature b/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature index 4b767898a..eb53d86e7 100755 --- a/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature @@ -147,7 +147,7 @@ Feature: Onboarding - Restore wallet Then I see LW homepage @LW-5835 - Scenario: Restore Wallet - "Recovery phrase length page" displayed + Scenario: Restore Wallet - "Recovery phrase page" displayed Given I click "Restore" button on wallet setup page And I go to "Mnemonic verification" page from "Restore" wallet flow and "not fill" values Then "Mnemonic verification" page is displayed from "Restore wallet" flow with 24 words @@ -160,7 +160,7 @@ Feature: Onboarding - Restore wallet When I click "Back" button during wallet setup Then "Get started" page is displayed - @LW-6080 @LW-5839 @LW-5838 @LW-5839 + @LW-6080 @LW-5839 @LW-5838 Scenario Outline: Restore Wallet - "Recovery phrase length page" restore words happy path Given I click "Restore" button and confirm And I select word passphrase length diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsForgotPassword.feature b/packages/e2e-tests/src/features/analytics/AnalyticsForgotPassword.feature index a8ae63aa4..f8d5a47f8 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsForgotPassword.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsForgotPassword.feature @@ -45,12 +45,12 @@ Feature: Analytics - Forgot Password And I close all remaining tabs except current one And I set up request interception for posthog analytics request(s) Then "Wallet setup" page is displayed - When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" And I click "Next" button during wallet setup Then I validate latest analytics single event "unlock wallet | forgot password? | set up your password | next | click" And I click "Next" button during wallet setup Then I validate latest analytics single event "unlock wallet | forgot password? | recovery phrase length | next | click" - And I am on "Mnemonic verification" last page from "Forgot password" and filled all words +# And I am on "Mnemonic verification" last page from "Forgot password" and filled all words And I validate latest analytics multiple events: | unlock wallet \| forgot password? \| enter passphrase #01 \| next \| click | | unlock wallet \| forgot password? \| enter passphrase #09 \| next \| click | diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature b/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature index b11ffe60f..ce79489fd 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature @@ -6,13 +6,13 @@ Feature: Analytics - Posthog - Onboarding - Extended View Scenario Outline: Analytics - Posthog events are enabled or disabled based on decision on Analytics page Given I set up request interception for posthog analytics request(s) When I click "Create" button on wallet setup page - When I accept "T&C" checkbox + # When I accept "T&C" checkbox And I click "Next" button during wallet setup # When "Help us improve your experience" page is displayed And I click "" button on Analytics page And I enter wallet name: "wallet" And I click "Next" button during wallet setup - When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" And I click "Next" button during wallet setup # When "Mnemonic info" page is displayed And I validate that analytics event(s) have been sent @@ -28,7 +28,7 @@ Feature: Analytics - Posthog - Onboarding - Extended View Given I set up request interception for posthog analytics request(s) When I click "Restore" button on wallet setup page When I click "OK" button on "Restoring a multi-address wallet?" modal - When I accept "T&C" checkbox + # When I accept "T&C" checkbox And I click "Next" button during wallet setup # When "Help us improve your experience" page is displayed And I click "Agree" button on Analytics page @@ -39,17 +39,17 @@ Feature: Analytics - Posthog - Onboarding - Extended View And I enter wallet name: "wallet" And I click "Next" button during wallet setup Then I validate latest analytics single event "onboarding | restore wallet | wallet name | next | click" - When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" And I click "Next" button during wallet setup Then I validate latest analytics single event "onboarding | restore wallet | wallet password | next | click" # When "Recovery phrase length page" is displayed and 24 words checkbox is checked And I click "Next" button during wallet setup Then I validate latest analytics single event "onboarding | restore wallet | recovery phrase length | next | click" - When I pass "Mnemonic verification" page with words 8 of 24 + # When I pass "Mnemonic verification" page with words 8 of 24 Then I validate latest analytics single event "onboarding | restore wallet | enter passphrase #01 | next | click" - When I pass "Mnemonic verification" page with words 16 of 24 + # When I pass "Mnemonic verification" page with words 16 of 24 Then I validate latest analytics single event "onboarding | restore wallet | enter passphrase #09 | next | click" - When I pass "Mnemonic verification" page with words 24 of 24 + # When I pass "Mnemonic verification" page with words 24 of 24 Then I validate latest analytics single event "onboarding | restore wallet | enter passphrase #17 | next | click" When I click "Go to my wallet" button on "All done" page And I validate latest analytics multiple events: @@ -63,7 +63,7 @@ Feature: Analytics - Posthog - Onboarding - Extended View Scenario: Analytics - Onboarding new wallet events Given I set up request interception for posthog analytics request(s) When I click "Create" button on wallet setup page - When I accept "T&C" checkbox + # When I accept "T&C" checkbox And I click "Next" button during wallet setup # When "Help us improve your experience" page is displayed And I click "Agree" button on Analytics page @@ -74,23 +74,23 @@ Feature: Analytics - Posthog - Onboarding - Extended View And I enter wallet name: "wallet" And I click "Next" button during wallet setup Then I validate latest analytics single event "onboarding | new wallet | wallet name | next | click" - When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" And I click "Next" button during wallet setup Then I validate latest analytics single event "onboarding | new wallet | wallet password | next | click" # When "Mnemonic info" page is displayed And I click "Next" button during wallet setup Then I validate latest analytics single event "onboarding | new wallet | passphrase intro | next | click" - When I pass "Mnemonic writedown" page with words 8 of 24 + # When I pass "Mnemonic writedown" page with words 8 of 24 Then I validate latest analytics single event "onboarding | new wallet | write passphrase #01 | next | click" - When I pass "Mnemonic writedown" page with words 16 of 24 + # When I pass "Mnemonic writedown" page with words 16 of 24 Then I validate latest analytics single event "onboarding | new wallet | write passphrase #09 | next | click" - When I pass "Mnemonic writedown" page with words 24 of 24 + # When I pass "Mnemonic writedown" page with words 24 of 24 Then I validate latest analytics single event "onboarding | new wallet | write passphrase #17 | next | click" - When I pass "Mnemonic verification" page with words 8 of 24 + # When I pass "Mnemonic verification" page with words 8 of 24 Then I validate latest analytics single event "onboarding | new wallet | enter passphrase #01 | next | click" - When I pass "Mnemonic verification" page with words 16 of 24 + # When I pass "Mnemonic verification" page with words 16 of 24 Then I validate latest analytics single event "onboarding | new wallet | enter passphrase #09 | next | click" - When I pass "Mnemonic verification" page with words 24 of 24 + # When I pass "Mnemonic verification" page with words 24 of 24 Then I validate latest analytics single event "onboarding | new wallet | enter passphrase #17 | next | click" When I click "Go to my wallet" button on "All done" page And I see LW homepage diff --git a/packages/e2e-tests/src/pageobject/onboardingPageObject.ts b/packages/e2e-tests/src/pageobject/onboardingPageObject.ts deleted file mode 100644 index 13c26ab77..000000000 --- a/packages/e2e-tests/src/pageobject/onboardingPageObject.ts +++ /dev/null @@ -1,292 +0,0 @@ -import OnboardingLegalPage from '../elements/onboarding/legalPage'; -import OnboardingMainPage from '../elements/onboarding/mainPage'; -import OnboardingMnemonicPage from '../elements/onboarding/mnemonicPage'; -import OnboardingWalletSetupPage from '../elements/onboarding/walletSetupPage'; -import RecoveryPhraseLengthPage from '../elements/onboarding/recoveryPhraseLengthPage'; -import { Logger } from '../support/logger'; -import { browser } from '@wdio/globals'; -import AnalyticsPage from '../elements/onboarding/analyticsPage'; -import Modal from '../elements/modal'; -import CommonOnboardingElements from '../elements/onboarding/commonOnboardingElements'; -import { getTestWallet, TestWalletName } from '../support/walletConfiguration'; -import testContext from '../utils/testContext'; -import { clearInputFieldValue } from '../utils/inputFieldUtils'; -import WalletCreationPage from '../elements/onboarding/WalletCreationPage'; -import OnboardingWalletSetupPageAssert from '../assert/onboarding/onboardingWalletSetupPageAssert'; - -class OnboardingPageObject { - public validPassword = 'N_8J@bne87A'; - - async openLegalPage() { - await this.acceptTCCheckbox(); - } - - async openAndAcceptTermsOfUsePage() { - await this.openLegalPage(); - await OnboardingLegalPage.nextButton.click(); - } - - async openNameYourWalletPage() { - await this.openAndAcceptTermsOfUsePage(); - await AnalyticsPage.nextButton.click(); - } - - async clickHeaderToLooseFocus() { - await OnboardingMnemonicPage.stepHeader.click(); - } - - async goToMnemonicWriteDownPage(length?: '12' | '15' | '24') { - await this.openNameYourWalletPage(); - await this.fillWalletNameInput('ValidWalletName'); - await OnboardingWalletSetupPage.nextButton.click(); - await this.fillPasswordInput(this.validPassword); - await this.fillPasswordConfirmationInput(this.validPassword); - await OnboardingWalletSetupPage.nextButton.click(); - if (length) { - await this.selectRecoveryPassphraseLength(length); - } - await RecoveryPhraseLengthPage.nextButton.click(); - await browser.pause(300); - } - - async openMnemonicWriteDownPage(expectedWordsPage: number): Promise { - const mnemonicWords: string[] = []; - mnemonicWords.push(...(await OnboardingMnemonicPage.getMnemonicWordTexts())); - - switch (Number(expectedWordsPage)) { - case 16: - await OnboardingMnemonicPage.nextButton.click(); - mnemonicWords.push(...(await OnboardingMnemonicPage.getMnemonicWordTexts())); - break; - case 24: - await OnboardingMnemonicPage.nextButton.click(); - mnemonicWords.push(...(await OnboardingMnemonicPage.getMnemonicWordTexts())); - await OnboardingMnemonicPage.nextButton.click(); - mnemonicWords.push(...(await OnboardingMnemonicPage.getMnemonicWordTexts())); - break; - default: - break; - } - Logger.log(`returning mnemonic: ${mnemonicWords.join(' ')}`); - return mnemonicWords; - } - - async passMnemonicWriteDownPage(): Promise { - const mnemonicWords: string[] = []; - mnemonicWords.push(...(await OnboardingMnemonicPage.getMnemonicWordTexts())); - await OnboardingMnemonicPage.nextButton.click(); - return mnemonicWords; - } - - async passMnemonicVerificationPage(mnemonicWords: string[], expectedWordsPage: number): Promise { - switch (Number(expectedWordsPage)) { - case 8: - await this.fillMnemonicFields(mnemonicWords, 0); - await OnboardingMnemonicPage.nextButton.click(); - break; - case 16: - await this.fillMnemonicFields(mnemonicWords, 8); - await OnboardingMnemonicPage.nextButton.click(); - break; - case 24: - await this.fillMnemonicFields(mnemonicWords, 16); - await OnboardingMnemonicPage.nextButton.click(); - break; - default: - break; - } - } - - async openMnemonicVerificationPage(expectedWordsPage: number): Promise { - const mnemonicWords: string[] = [...(await this.openMnemonicWriteDownPage(24))]; - await OnboardingMnemonicPage.nextButton.click(); - - switch (Number(expectedWordsPage)) { - case 16: - await this.fillMnemonicFields(mnemonicWords, 0); - await OnboardingMnemonicPage.nextButton.click(); - break; - case 24: - await this.fillMnemonicFields(mnemonicWords, 0); - await OnboardingMnemonicPage.nextButton.click(); - await this.fillMnemonicFields(mnemonicWords, 8); - await OnboardingMnemonicPage.nextButton.click(); - break; - default: - break; - } - return mnemonicWords; - } - - async openMnemonicVerificationLastPageFromWalletCreate() { - const mnemonicWords: string[] = await this.openMnemonicVerificationPage(24); - await this.fillMnemonicFields(mnemonicWords, 16); - } - - async openMnemonicVerificationLastPage(mnemonicWords?: string[], length?: '12' | '15' | '24') { - if (!mnemonicWords) { - mnemonicWords = await this.openMnemonicVerificationPage(24); - await this.fillMnemonicFields(mnemonicWords, 16); - } else { - await this.fillMnemonicFields(mnemonicWords, 0); - await OnboardingMnemonicPage.nextButton.click(); - await this.fillMnemonicFields(mnemonicWords, 8); - if (length === '24' || length === undefined) { - await OnboardingMnemonicPage.nextButton.click(); - await this.fillMnemonicFields(mnemonicWords, 16); - } - } - } - - async openConnectHardwareWalletPage() { - await this.openNameYourWalletPage(); - } - - async fillMnemonicFields(mnemonicWords: string[], offset: 0 | 8 | 16) { - const inputs = await OnboardingMnemonicPage.mnemonicInputs; - for (const [i, input] of inputs.entries()) { - await clearInputFieldValue(input); - await browser.keys(mnemonicWords[i + offset]); - } - await OnboardingMnemonicPage.stepTitle.click(); // Click outside input fields to trigger validation - } - - async fillMnemonicInput(value: string, inputNumber = 0, shouldTriggerValidation = true) { - const inputs = await OnboardingMnemonicPage.mnemonicInputs; - await clearInputFieldValue(inputs[inputNumber]); - await browser.keys(value); - if (shouldTriggerValidation) { - await OnboardingMnemonicPage.stepTitle.click(); // Click outside input fields to trigger validation - } - } - - async clickOnInput() { - const inputs = await OnboardingMnemonicPage.mnemonicInputs; - await inputs[0].click(); - } - - async addCharToMnemonicField(characters: string, inputNumber: number) { - const inputs = await OnboardingMnemonicPage.mnemonicInputs; - await inputs[inputNumber].addValue(characters); - } - - async openAllDonePage() { - await this.goToMnemonicWriteDownPage(); - await this.openMnemonicVerificationLastPageFromWalletCreate(); - await OnboardingMnemonicPage.nextButton.click(); - } - - async openAllDonePageFromWalletRestore(mnemonicWords: string[], length?: '12' | '15' | '24') { - await this.goToMnemonicWriteDownPage(); - await this.openMnemonicVerificationLastPage(mnemonicWords, length); - await OnboardingMnemonicPage.nextButton.click(); - } - - async acceptTCCheckbox() { - await OnboardingLegalPage.termsCheckbox.scrollIntoView(); - await OnboardingLegalPage.termsCheckbox.click(); - } - - async fillWalletNameInput(text: string) { - await OnboardingWalletSetupPage.setWalletNameInput(text); - } - - async fillPasswordInput(text: string) { - await OnboardingWalletSetupPage.walletPasswordInput.setValue(text); - } - - async fillPasswordConfirmationInput(text: string) { - await OnboardingWalletSetupPage.walletPasswordConfirmInput.setValue(text); - } - - async fillPasswordPage(password: string, passwordConfirmation: string) { - await this.fillPasswordInput(password); - await this.fillPasswordConfirmationInput(passwordConfirmation); - } - - async hoverOverNextButton() { - await OnboardingLegalPage.nextButton.moveTo(); - } - - async changeRandomMnemonicField() { - const randomFieldNo = Math.floor(Math.random() * 8); - const inputs = await OnboardingMnemonicPage.mnemonicInputs; - testContext.save('mnemonic', { index: randomFieldNo, value: await inputs[randomFieldNo].getValue() }); - await inputs[randomFieldNo].click(); - await browser.keys('.'); - await OnboardingMnemonicPage.stepTitle.click(); // Click outside input fields to trigger validation - } - - async restorePreviousMnemonicWord() { - const mnemonic = testContext.load('mnemonic') as { value: string; index: number }; - await this.fillMnemonicInput(mnemonic.value, mnemonic.index); - } - - async clearRandomMnemonicField() { - const randomFieldNo = Math.floor(Math.random() * 8); - const inputs = await OnboardingMnemonicPage.mnemonicInputs; - await clearInputFieldValue(inputs[randomFieldNo]); - } - - async clickOnLegalLink(link: string) { - switch (link) { - case 'Cookie policy': - await OnboardingMainPage.cookiePolicyLink.click(); - break; - case 'Privacy policy': - await OnboardingMainPage.privacyPolicyLink.click(); - break; - case 'Terms of service': - await OnboardingMainPage.termsOfServiceLink.click(); - break; - default: - throw new Error(`Unsupported legal link - ${link}`); - } - } - - async selectRecoveryPassphraseLength(length: '12' | '15' | '24') { - switch (length) { - case '12': - await RecoveryPhraseLengthPage.radioButton12wordsButton.click(); - break; - case '15': - await RecoveryPhraseLengthPage.radioButton15wordsButton.click(); - break; - case '24': - await RecoveryPhraseLengthPage.radioButton24wordsButton.click(); - } - } - - async restoreWallet() { - const commonOnboardingElements = new CommonOnboardingElements(); - - await OnboardingMainPage.restoreWalletButton.click(); - await Modal.confirmButton.waitForClickable(); - await Modal.confirmButton.click(); - await this.openAndAcceptTermsOfUsePage(); - await AnalyticsPage.nextButton.click(); - await OnboardingWalletSetupPageAssert.assertSeeWalletSetupPage(); - await this.fillWalletNameInput('ValidName'); - await commonOnboardingElements.nextButton.click(); - await this.fillPasswordPage(this.validPassword, this.validPassword); - await commonOnboardingElements.nextButton.click(); - await commonOnboardingElements.nextButton.click(); - await this.openMnemonicVerificationLastPage(getTestWallet(TestWalletName.TestAutomationWallet).mnemonic ?? []); - await commonOnboardingElements.nextButton.click(); - await commonOnboardingElements.nextButton.click(); - await Modal.cancelButton.waitForClickable(); - await Modal.cancelButton.click(); - } - - async waitUntilLoaderDisappears() { - await browser.pause(500); - if (await WalletCreationPage.walletLoader.isDisplayed()) { - await WalletCreationPage.walletLoader.waitForDisplayed({ timeout: 15_000, reverse: true }); - } - if (await WalletCreationPage.mainLoaderText.isDisplayed()) { - await WalletCreationPage.mainLoaderText.waitForDisplayed({ timeout: 255_000, reverse: true }); - } - } -} - -export default new OnboardingPageObject(); diff --git a/packages/e2e-tests/src/pageobject/onboardingRevampPageObject.ts b/packages/e2e-tests/src/pageobject/onboardingRevampPageObject.ts deleted file mode 100644 index 68b27cd63..000000000 --- a/packages/e2e-tests/src/pageobject/onboardingRevampPageObject.ts +++ /dev/null @@ -1,84 +0,0 @@ -import WalletSetupPage from '../elements/onboarding/walletSetupPage'; -import RecoveryPhrasePage from '../elements/onboarding/recoveryPhrasePage'; -import { setInputFieldValue } from '../utils/inputFieldUtils'; -import mainPage from '../elements/onboarding/mainPage'; -import onboardingWalletSetupPageAssert from '../assert/onboarding/onboardingWalletSetupPageAssert'; - -class OnboardingRevampPageObject { - private mnemonicWords: string[] = []; - - getMnemonicWords(): string[] { - return this.mnemonicWords; - } - - async clickOnLegalLink(link: string, isMainPage = false): Promise { - switch (link) { - case 'Cookie policy': - await mainPage.cookiePolicyLink.click(); - break; - case 'Privacy policy': - isMainPage ? await mainPage.agreementPrivacyPolicyLink.click() : await mainPage.privacyPolicyLink.click(); - break; - case 'Terms of service': - isMainPage ? await mainPage.agreementTermsOfServiceLink.click() : await mainPage.termsOfServiceLink.click(); - break; - default: - throw new Error(`Unsupported legal link - ${link}`); - } - } - - async goToMnemonicVerificationPage( - flowType: 'Create' | 'Restore', - mnemonicWords: string[] = [], - fillValues = true - ): Promise { - if (flowType === 'Create') { - await this.collectMnemonicWords(); - await RecoveryPhrasePage.nextButton.click(); - if (fillValues) await this.enterMnemonicWords(); - } else if (fillValues) await this.enterMnemonicWords(mnemonicWords); - } - - async goToWalletSetupPage( - flowType: 'Create' | 'Restore', - mnemonicWords: string[] = [], - fillValues = true - ): Promise { - await this.goToMnemonicVerificationPage(flowType, mnemonicWords); - await RecoveryPhrasePage.nextButton.click(); - if (fillValues) { - await WalletSetupPage.setWalletNameInput('TestAutomationWallet'); - await WalletSetupPage.setWalletPasswordInput('N_8J@bne87A'); - await WalletSetupPage.setWalletPasswordConfirmInput('N_8J@bne87A'); - } - } - - async collectMnemonicWords(): Promise { - this.mnemonicWords = await RecoveryPhrasePage.getMnemonicWordTexts(); - } - - async clickEnterWalletButton(): Promise { - await onboardingWalletSetupPageAssert.assertEnterWalletButtonIsEnabled(); - await WalletSetupPage.nextButton.click(); - } - - async enterMnemonicWord(value: string, inputNumber = 0, shouldTriggerValidation = true) { - const inputs = await RecoveryPhrasePage.mnemonicInputs; - await setInputFieldValue(inputs[inputNumber], value); - if (shouldTriggerValidation) { - await RecoveryPhrasePage.stepTitle.click(); // Click outside input fields to trigger validation - } - } - - async enterMnemonicWords(mnemonicWords: string[] = []): Promise { - if (mnemonicWords.length > 0) { - this.mnemonicWords = mnemonicWords; - } - const mnemonicInputs = await RecoveryPhrasePage.mnemonicInputs; - for (let i = 0; i < this.mnemonicWords.length; i++) { - await mnemonicInputs[i].setValue(this.mnemonicWords[i]); - } - } -} - -export default new OnboardingRevampPageObject(); diff --git a/packages/e2e-tests/src/pageobject/settingsExtendedPageObject.ts b/packages/e2e-tests/src/pageobject/settingsExtendedPageObject.ts index 3f6095f13..d1dc9f227 100644 --- a/packages/e2e-tests/src/pageobject/settingsExtendedPageObject.ts +++ b/packages/e2e-tests/src/pageobject/settingsExtendedPageObject.ts @@ -8,8 +8,8 @@ import { browser } from '@wdio/globals'; import ToastMessage from '../elements/toastMessage'; import { t } from '../utils/translationService'; import { Logger } from '../support/logger'; -import onboardingPageObject from './onboardingPageObject'; import { expect } from 'chai'; +import MainLoader from '../elements/MainLoader'; class SettingsExtendedPageObject { clickOnAbout = async () => { @@ -162,7 +162,7 @@ class SettingsExtendedPageObject { }; async waitUntilHdWalletSynced() { - await onboardingPageObject.waitUntilLoaderDisappears(); + await MainLoader.waitUntilLoaderDisappears(); await this.waitUntilSyncingModalDisappears(); await this.closeWalletSyncedToast(); await this.multiAddressModalConfirm(); diff --git a/packages/e2e-tests/src/steps/forgotPasswordSteps.ts b/packages/e2e-tests/src/steps/forgotPasswordSteps.ts index 04e01bc8e..8a1eaed76 100644 --- a/packages/e2e-tests/src/steps/forgotPasswordSteps.ts +++ b/packages/e2e-tests/src/steps/forgotPasswordSteps.ts @@ -2,15 +2,11 @@ import { Then, When } from '@cucumber/cucumber'; import WalletUnlockPage from '../elements/walletUnlockPage'; import ForgotPasswordModalAssert from '../assert/forgotPasswordModalAssert'; import ForgotPasswordModal from '../elements/ForgotPasswordModal'; -import OnboardingPageObject from '../pageobject/onboardingPageObject'; -import WalletSetupPage from '../elements/onboarding/walletSetupPage'; import LocalStorageAssert from '../assert/localStorageAssert'; import BackgroundStorageAssert from '../assert/backgroundStorageAssert'; import extendedView from '../page/extendedView'; import { browser } from '@wdio/globals'; -const validPassword = 'N_8J@bne87A'; - When(/^I click on "Forgot password\?" button on unlock screen$/, async () => { await WalletUnlockPage.forgotPassword.waitForClickable(); await WalletUnlockPage.forgotPassword.click(); @@ -47,20 +43,6 @@ Then(/^I switch to tab with restore wallet process$/, async () => { } }); -Then(/^I am on (.*) page of restoration flow$/, async (expectedPage: string) => { - switch (expectedPage) { - case 'password': - // nothing to do as user lands on Password Page by default - break; - case 'mnemonic verification': - await OnboardingPageObject.fillPasswordPage(validPassword, validPassword); - await WalletSetupPage.nextButton.click(); - break; - default: - throw new Error(`Unknown page: ${expectedPage}`); - } -}); - Then(/^all wallet related data is removed$/, async () => { await LocalStorageAssert.assertWalletIsDeleted(); await BackgroundStorageAssert.assertKeyAgentsByChainNotInBackgroundStorage(); diff --git a/packages/e2e-tests/src/steps/multidelegationSteps.ts b/packages/e2e-tests/src/steps/multidelegationSteps.ts index 2c20aeba3..8bbe8713c 100644 --- a/packages/e2e-tests/src/steps/multidelegationSteps.ts +++ b/packages/e2e-tests/src/steps/multidelegationSteps.ts @@ -29,9 +29,10 @@ import PortfolioBarAssert from '../assert/multidelegation/PortfolioBarAssert'; import ChangingStakingPreferencesModalAssert from '../assert/multidelegation/ChangingStakingPreferencesModalAssert'; import { StakePoolListColumnType, StakePoolSortingOptionType } from '../types/staking'; import SwitchingStakePoolModal from '../elements/staking/SwitchingStakePoolModal'; -import OnboardingPageObject from '../pageobject/onboardingPageObject'; import MoreOptionsComponentAssert from '../assert/multidelegation/MoreOptionsComponentAssert'; +const validPassword = 'N_8J@bne87A'; + Given(/^I open (Overview|Browse pools) tab$/, async (tabToClick: 'Overview' | 'Browse pools') => { await MultidelegationPage.openTab(tabToClick); }); @@ -188,7 +189,7 @@ When( let password; switch (type) { case 'newly created': - password = OnboardingPageObject.validPassword; + password = validPassword; break; case 'incorrect': password = 'somePassword'; diff --git a/packages/e2e-tests/src/steps/onboardingSteps.ts b/packages/e2e-tests/src/steps/onboardingSteps.ts index 224d56176..ed5f51fde 100644 --- a/packages/e2e-tests/src/steps/onboardingSteps.ts +++ b/packages/e2e-tests/src/steps/onboardingSteps.ts @@ -11,9 +11,6 @@ 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 OnboardingMnemonicPageAssert from '../assert/onboarding/onboardingMnemonicPageAssert'; -import OnboardingPageObject from '../pageobject/onboardingPageObject'; -import OnboardingWalletCreationPageAssert from '../assert/onboarding/onboardingWalletCreationPageAssert'; import OnboardingWalletSetupPage from '../elements/onboarding/walletSetupPage'; import settingsExtendedPageObject from '../pageobject/settingsExtendedPageObject'; import TokensPageAssert from '../assert/tokensPageAssert'; @@ -25,7 +22,6 @@ import SelectAccountPage from '../elements/onboarding/selectAccountPage'; import { browser } from '@wdio/globals'; import type { RecoveryPhrase } from '../types/onboarding'; import { generateRandomString } from '../utils/textUtils'; -import OnboardingRevampPageObject from '../pageobject/onboardingRevampPageObject'; import onboardingRecoveryPhrasePageAssert from '../assert/onboarding/onboardingRecoveryPhrasePageAssert'; import RecoveryPhrasePage from '../elements/onboarding/recoveryPhrasePage'; import onboardingWatchVideoModalAssert from '../assert/onboarding/onboardingWatchVideoModalAssert'; @@ -41,7 +37,6 @@ const twelveMnemonicWords: string[] = getTestWallet(TestWalletName.TwelveWordsMn const fifteenMnemonicWords: string[] = getTestWallet(TestWalletName.FifteenWordsMnemonic).mnemonic ?? []; const mnemonicWordsForReference: string[] = []; -const validPassword = 'N_8J@bne87A'; When( /^I click "(Create|Connect|Restore)" button on wallet setup page$/, @@ -124,7 +119,7 @@ Given(/^I click "Restore" button and confirm$/, async () => { }); When(/^I enter wallet name: "([^"]*)"$/, async (walletName: string) => { - await OnboardingPageObject.fillWalletNameInput(walletName === 'empty' ? '' : walletName); + await OnboardingWalletSetupPage.setWalletNameInput(walletName === 'empty' ? '' : walletName); }); When(/^I enter wallet name with size of: ([^"]*) characters$/, async (numberOfCharacters: number) => { @@ -132,16 +127,6 @@ When(/^I enter wallet name with size of: ([^"]*) characters$/, async (numberOfCh await OnboardingWalletSetupPage.setWalletNameInput(walletName); }); -When( - /^I enter password: "([^"]*)" and password confirmation: "([^"]*)"$/, - async (password: string, passwordConf: string) => { - await OnboardingPageObject.fillPasswordPage( - password === 'empty' ? '' : password, - passwordConf === 'empty' ? '' : passwordConf - ); - } -); - Then(/^Name error "([^"]*)" is displayed/, async (nameError: string) => { await OnboardingWalletSetupPageAssert.assertSeeWalletNameError(await t(nameError)); }); @@ -182,106 +167,24 @@ Then(/^I click Trezor wallet icon$/, async () => { await OnboardingConnectHardwareWalletPage.trezorButton.click(); }); -Then(/^"Creating wallet" page is displayed$/, async () => { - await OnboardingWalletCreationPageAssert.assertSeeCreatingWalletPage(); -}); - -Then(/^Creating wallet page finishes in < (\d*)s$/, async (duration: number) => { - await OnboardingWalletCreationPageAssert.assertCreatingWalletDuration(duration); -}); - Then(/^"Restoring a multi-address wallet\?" modal is displayed$/, async () => { await ModalAssert.assertSeeRestoringMultiAddressWalletModal(); }); -Then(/^I accept "T&C" checkbox$/, async () => { - await OnboardingPageObject.acceptTCCheckbox(); -}); - -Given(/^I am on "Lace terms of use" page and accept terms$/, async () => { - await OnboardingPageObject.openAndAcceptTermsOfUsePage(); -}); - -Given(/^I am on "Connect Hardware Wallet" page$/, async () => { - await OnboardingPageObject.openConnectHardwareWalletPage(); -}); - -Given(/^I am on "Mnemonic writedown" page with words (8|16|24) of 24$/, async (expectedWords: number) => { - mnemonicWords.length = 0; - await OnboardingPageObject.goToMnemonicWriteDownPage(); - mnemonicWords.push(...(await OnboardingPageObject.openMnemonicWriteDownPage(expectedWords))); - await OnboardingMnemonicPageAssert.assertSeeMnemonicWriteDownPage(expectedWords); -}); -Given(/^I pass "Mnemonic writedown" page with words (8|16|24) of 24$/, async (expectedWords: number) => { - if (String(expectedWords) === '8') { - mnemonicWords.length = 0; - } - mnemonicWords.push(...(await OnboardingPageObject.passMnemonicWriteDownPage())); -}); -Given(/^I pass "Mnemonic verification" page with words (8|16|24) of 24$/, async (expectedWords: number) => { - await OnboardingPageObject.passMnemonicVerificationPage(mnemonicWords, expectedWords); -}); - -Then(/^Words 1 - 8 (are|are not) the same$/, async (expectedMatch: string) => { - const shouldBeEqual: boolean = expectedMatch === 'are'; - await OnboardingMnemonicPageAssert.assertMnemonicWordsAreTheSame( - mnemonicWords, - mnemonicWordsForReference, - shouldBeEqual - ); -}); - -Given(/^I am on "Mnemonic verification" page with words (8|16|24) of 24$/, async (expectedWords: number) => { - mnemonicWords.length = 0; - await OnboardingPageObject.goToMnemonicWriteDownPage(); - mnemonicWords.push(...(await OnboardingPageObject.openMnemonicVerificationPage(expectedWords))); - await OnboardingMnemonicPageAssert.assertSeeMnemonicVerificationWordsPage(expectedWords); -}); - -Given( - /^I am on "Mnemonic verification" last page from "(Create wallet|Restore wallet|Forgot password)" and filled all words$/, - async (flow) => { - if (flow === 'Create wallet') { - await OnboardingPageObject.goToMnemonicWriteDownPage(); - await OnboardingPageObject.openMnemonicVerificationLastPageFromWalletCreate(); - } else { - await OnboardingPageObject.openMnemonicVerificationLastPage(mnemonicWords); - } - } -); - -Given(/^I am on "All done!" page from "Restore wallet" using "([^"]*)" wallet$/, async (walletName: string) => { - await OnboardingPageObject.openAllDonePageFromWalletRestore(getTestWallet(walletName).mnemonic ?? []); -}); - Then(/^I clear saved words$/, async () => { mnemonicWordsForReference.length = 0; }); -Then(/^I fill saved words (8|16|24) of 24$/, async (pageNumber: string) => { - switch (pageNumber) { - case '8': - await OnboardingPageObject.fillMnemonicFields(mnemonicWordsForReference, 0); - break; - case '16': - await OnboardingPageObject.fillMnemonicFields(mnemonicWordsForReference, 8); - break; - case '24': - await OnboardingPageObject.fillMnemonicFields(mnemonicWordsForReference, 16); - break; - } -}); - When(/^I fill mnemonic input with "([^"]*)"$/, async (value: string) => { - await OnboardingRevampPageObject.enterMnemonicWord(value, 0, false); + await RecoveryPhrasePage.enterMnemonicWord(value, 0, false); }); When(/^I click on mnemonic input$/, async () => { - await OnboardingPageObject.clickOnInput(); + await RecoveryPhrasePage.clickOnInput(); }); When(/^I add characters "([^"]*)" in word ([0-7])$/, async (characters: string, inputNumber: number) => { - await OnboardingPageObject.addCharToMnemonicField(characters, inputNumber); + await RecoveryPhrasePage.addCharToMnemonicField(characters, inputNumber); }); Then(/^I see LW homepage$/, async () => { @@ -305,39 +208,30 @@ Then(/^I see Lace extension main page in (extended|popup) mode$/, async (mode: ' }); Then(/^I change one random field$/, async () => { - await OnboardingPageObject.changeRandomMnemonicField(); + await RecoveryPhrasePage.changeRandomMnemonicField(); }); Then(/^I clear one random field$/, async () => { - await OnboardingPageObject.clearRandomMnemonicField(); -}); - -Then(/^I navigate to "Mnemonic info" page$/, async () => { - await OnboardingMainPage.createWalletButton.click(); - await OnboardingPageObject.openNameYourWalletPage(); - await OnboardingPageObject.fillWalletNameInput('ValidName'); - await OnboardingWalletSetupPage.nextButton.click(); - await OnboardingPageObject.fillPasswordPage(validPassword, validPassword); - await OnboardingWalletSetupPage.nextButton.click(); + await RecoveryPhrasePage.clearRandomMnemonicField(); }); Then(/^I see following autocomplete options:$/, async (options: DataTable) => { - await OnboardingMnemonicPageAssert.assertSeeMnemonicAutocompleteOptions(dataTableAsStringArray(options)); + await onboardingRecoveryPhrasePageAssert.assertSeeMnemonicAutocompleteOptions(dataTableAsStringArray(options)); }); Then(/^I click header to loose focus$/, async () => { - await OnboardingPageObject.clickHeaderToLooseFocus(); + await RecoveryPhrasePage.clickHeaderToLoseFocus(); }); Then(/^I do not see autocomplete options list$/, async () => { - await OnboardingMnemonicPageAssert.assertNotSeeMnemonicAutocompleteOptions(); + await onboardingRecoveryPhrasePageAssert.assertNotSeeMnemonicAutocompleteOptions(); }); Given(/^I create new wallet and save wallet information$/, async () => { await OnboardingMainPage.createWalletButton.click(); - await OnboardingRevampPageObject.goToWalletSetupPage('Create', mnemonicWords); + await OnboardingWalletSetupPage.goToWalletSetupPage('Create', mnemonicWords); await OnboardingWalletSetupPageAssert.assertSeeWalletSetupPage(); - await OnboardingRevampPageObject.clickEnterWalletButton(); + await OnboardingWalletSetupPage.clickEnterWalletButton(); await TopNavigationAssert.assertLogoPresent(); await settingsExtendedPageObject.switchNetworkAndCloseDrawer('Preprod', 'extended'); const newCreatedWallet = JSON.stringify(await getWalletsFromRepository()); @@ -345,28 +239,23 @@ Given(/^I create new wallet and save wallet information$/, async () => { }); Then(/^the mnemonic input contains the word "([^"]*)"$/, async (expectedWord: string) => { - await OnboardingMnemonicPageAssert.assertSeeMnemonicInputWithText(0, expectedWord); + await onboardingRecoveryPhrasePageAssert.assertSeeMnemonicInputWithText(0, expectedWord); }); Then(/^the word in mnemonic input has only ([^"]*) characters$/, async (expectedLength: string) => { - await OnboardingMnemonicPageAssert.assertMnemonicInputLength(0, Number(expectedLength)); + await onboardingRecoveryPhrasePageAssert.assertMnemonicInputLength(0, Number(expectedLength)); }); When(/^I click on Privacy Policy link$/, async () => { await OnboardingAnalyticsPage.privacyPolicyLinkWithinDescription.click(); }); -When( - /^I click on "(Cookie policy|Privacy policy|Terms of service)" legal link"$/, - async (link: 'Cookie policy' | 'Privacy policy' | 'Terms of service') => { - await OnboardingPageObject.clickOnLegalLink(link); - } -); - When( /^I click on "(Cookie policy|Privacy policy|Terms of service)" legal link(?: on "(Main page)")?$/, async (link: 'Cookie policy' | 'Privacy policy' | 'Terms of service', page?: 'Main page') => { - await OnboardingRevampPageObject.clickOnLegalLink(link, page === 'Main page'); + page === 'Main page' + ? await OnboardingMainPage.clickOnLegalLink(link) + : await new CommonOnboardingElements().clickOnLegalLinkOnFooter(link); } ); @@ -392,7 +281,13 @@ When(/^I click "Help and support" button during wallet setup$/, async () => { }); Given(/^I restore a wallet$/, async () => { - await OnboardingPageObject.restoreWallet(); + await OnboardingMainPage.restoreWalletButton.click(); + await OnboardingWalletSetupPage.goToWalletSetupPage( + 'Restore', + getTestWallet(TestWalletName.TestAutomationWallet).mnemonic ?? [] + ); + await OnboardingWalletSetupPage.clickEnterWalletButton(); + await TopNavigationAssert.assertLogoPresent(); }); Given(/^I see current onboarding page in (light|dark) mode$/, async (mode: 'light' | 'dark') => { @@ -409,7 +304,7 @@ Given( ); When(/^I restore previously changed mnemonic word$/, async () => { - await OnboardingPageObject.restorePreviousMnemonicWord(); + await RecoveryPhrasePage.restorePreviousMnemonicWord(); }); Given( @@ -425,10 +320,10 @@ Given( if (walletName) mnemonicsToUse = getTestWallet(walletName).mnemonic ?? []; switch (endPage) { case 'Mnemonic verification': - await OnboardingRevampPageObject.goToMnemonicVerificationPage(flowType, mnemonicsToUse, fillValues === 'fill'); + await RecoveryPhrasePage.goToMnemonicVerificationPage(flowType, mnemonicsToUse, fillValues === 'fill'); break; case 'Wallet setup': - await OnboardingRevampPageObject.goToWalletSetupPage(flowType, mnemonicsToUse, fillValues === 'fill'); + await OnboardingWalletSetupPage.goToWalletSetupPage(flowType, mnemonicsToUse, fillValues === 'fill'); break; default: throw new Error(`Unsupported page name: ${endPage}`); @@ -437,7 +332,7 @@ Given( ); Given(/^I click "Enter wallet" button$/, async () => { - await OnboardingRevampPageObject.clickEnterWalletButton(); + await OnboardingWalletSetupPage.clickEnterWalletButton(); }); Given( @@ -445,19 +340,17 @@ Given( async (mnemonicWordsLength: RecoveryPhrase, isCorrect: 'correct' | 'incorrect') => { switch (mnemonicWordsLength) { case '12': - await OnboardingRevampPageObject.enterMnemonicWords( + await RecoveryPhrasePage.enterMnemonicWords( isCorrect === 'correct' ? twelveMnemonicWords : invalidMnemonicWords.slice(0, 12) ); break; case '15': - await OnboardingRevampPageObject.enterMnemonicWords( + await RecoveryPhrasePage.enterMnemonicWords( isCorrect === 'correct' ? fifteenMnemonicWords : invalidMnemonicWords.slice(0, 15) ); break; case '24': - await OnboardingRevampPageObject.enterMnemonicWords( - isCorrect === 'correct' ? mnemonicWords : invalidMnemonicWords - ); + await RecoveryPhrasePage.enterMnemonicWords(isCorrect === 'correct' ? mnemonicWords : invalidMnemonicWords); break; } } @@ -476,7 +369,7 @@ When(/^I save mnemonic words$/, async () => { }); When(/^I enter saved mnemonic words$/, async () => { - await OnboardingRevampPageObject.enterMnemonicWords(mnemonicWordsForReference); + await RecoveryPhrasePage.enterMnemonicWords(mnemonicWordsForReference); }); Then(/^"Mnemonic writedown" page is displayed with (12|15|24) words$/, async (mnemonicWordsLength: RecoveryPhrase) => { From a522d32f50ee4b73a653e3ce9e639e6ea90ae434 Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Thu, 4 Apr 2024 10:12:14 +0200 Subject: [PATCH 25/74] chore: fix unit test run in post integration workflow (#1022) --- .github/workflows/post-integration.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/post-integration.yml b/.github/workflows/post-integration.yml index b7b7a4e54..7edb209a4 100644 --- a/.github/workflows/post-integration.yml +++ b/.github/workflows/post-integration.yml @@ -21,4 +21,8 @@ jobs: name: lightwallet path: apps/browser-extension-wallet/dist - name: Run unit tests + env: + AVAILABLE_CHAINS: 'Preprod,Preview,Mainnet' + DEFAULT_CHAIN: 'Preprod' + NODE_OPTIONS: '--max_old_space_size=8192' run: yarn test --maxWorkers=2 --silent From 3dc5e5ec9c839215932078a3478d0cce1356b2fe Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Thu, 4 Apr 2024 12:14:03 +0200 Subject: [PATCH 26/74] chore: update cardano sdk packages LW-10204 (#1019) --- apps/browser-extension-wallet/package.json | 8 +- packages/cardano/package.json | 8 +- packages/staking/package.json | 12 +-- yarn.lock | 98 +++++++++++----------- 4 files changed, 63 insertions(+), 63 deletions(-) diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 25a40563d..12c5c408a 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -42,11 +42,11 @@ "@cardano-sdk/cardano-services-client": "0.18.0", "@cardano-sdk/core": "0.30.0", "@cardano-sdk/dapp-connector": "0.12.14", - "@cardano-sdk/input-selection": "0.12.25", - "@cardano-sdk/tx-construction": "0.18.1", + "@cardano-sdk/input-selection": "0.12.26", + "@cardano-sdk/tx-construction": "0.18.2", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.1", - "@cardano-sdk/web-extension": "0.26.0", + "@cardano-sdk/wallet": "0.35.2", + "@cardano-sdk/web-extension": "0.26.1", "@emurgo/cip14-js": "~3.0.1", "@koralabs/handles-public-api-interfaces": "^1.6.6", "@lace/cardano": "0.1.0", diff --git a/packages/cardano/package.json b/packages/cardano/package.json index ca7d9eea8..d78f11bcd 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -41,12 +41,12 @@ "@cardano-sdk/cardano-services-client": "0.18.0", "@cardano-sdk/core": "0.30.0", "@cardano-sdk/crypto": "0.1.22", - "@cardano-sdk/hardware-ledger": "0.8.18", - "@cardano-sdk/hardware-trezor": "0.4.18", + "@cardano-sdk/hardware-ledger": "0.8.19", + "@cardano-sdk/hardware-trezor": "0.4.19", "@cardano-sdk/key-management": "0.20.1", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.1", - "@cardano-sdk/web-extension": "0.26.0", + "@cardano-sdk/wallet": "0.35.2", + "@cardano-sdk/web-extension": "0.26.1", "@lace/common": "0.1.0", "@stablelib/chacha20poly1305": "1.0.1", "bignumber.js": "9.0.1", diff --git a/packages/staking/package.json b/packages/staking/package.json index 812373483..d1147173e 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -72,8 +72,8 @@ "devDependencies": { "@babel/core": "^7.21.0", "@cardano-sdk/core": "0.30.0", - "@cardano-sdk/input-selection": "0.12.25", - "@cardano-sdk/tx-construction": "0.18.1", + "@cardano-sdk/input-selection": "0.12.26", + "@cardano-sdk/tx-construction": "0.18.2", "@cardano-sdk/util": "0.15.0", "@storybook/addon-actions": "^7.6.7", "@storybook/addon-essentials": "^7.6.7", @@ -119,11 +119,11 @@ "wait-on": "^7.0.1" }, "peerDependencies": { - "@cardano-sdk/input-selection": "0.12.25", - "@cardano-sdk/tx-construction": "0.18.1", + "@cardano-sdk/input-selection": "0.12.26", + "@cardano-sdk/tx-construction": "0.18.2", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.1", - "@cardano-sdk/web-extension": "0.26.0", + "@cardano-sdk/wallet": "0.35.2", + "@cardano-sdk/web-extension": "0.26.1", "@lace/cardano": "^0.1.0", "@lace/common": "^0.1.0", "@lace/core": "0.1.0", diff --git a/yarn.lock b/yarn.lock index efd6f939c..fabcfe5b5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7756,46 +7756,46 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/hardware-ledger@npm:0.8.18, @cardano-sdk/hardware-ledger@npm:~0.8.18": - version: 0.8.18 - resolution: "@cardano-sdk/hardware-ledger@npm:0.8.18" +"@cardano-sdk/hardware-ledger@npm:0.8.19, @cardano-sdk/hardware-ledger@npm:~0.8.19": + version: 0.8.19 + resolution: "@cardano-sdk/hardware-ledger@npm:0.8.19" dependencies: "@cardano-foundation/ledgerjs-hw-app-cardano": ^6.0.0 "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/key-management": ~0.20.1 - "@cardano-sdk/tx-construction": ~0.18.1 + "@cardano-sdk/tx-construction": ~0.18.2 "@cardano-sdk/util": ~0.15.0 "@ledgerhq/hw-transport": ^6.28.1 "@ledgerhq/hw-transport-node-hid-noevents": ^6.27.12 "@ledgerhq/hw-transport-webhid": ^6.27.12 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 29010d85913440bda40f0ab0e0f0510f840007f0172e5d7d1e5951dea959d39b1c686a6f3bfef8e497f992b97b0f9bb3edcfd6e9f6e5e91a9c6a33d13f487862 + checksum: ac2cb838d10de6cde097a5c1536fbc0f783fe2594bfa9ff00b00a0bf1d98c0dfb919a2439929e6b3969bf17fdd245c919fb3de26834027f29613d10886284cce languageName: node linkType: hard -"@cardano-sdk/hardware-trezor@npm:0.4.18, @cardano-sdk/hardware-trezor@npm:~0.4.18": - version: 0.4.18 - resolution: "@cardano-sdk/hardware-trezor@npm:0.4.18" +"@cardano-sdk/hardware-trezor@npm:0.4.19, @cardano-sdk/hardware-trezor@npm:~0.4.19": + version: 0.4.19 + resolution: "@cardano-sdk/hardware-trezor@npm:0.4.19" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/key-management": ~0.20.1 - "@cardano-sdk/tx-construction": ~0.18.1 + "@cardano-sdk/tx-construction": ~0.18.2 "@cardano-sdk/util": ~0.15.0 "@trezor/connect": 9.1.6 "@trezor/connect-web": 9.1.6 lodash: ^4.17.21 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: b37d0093a499170f225699607d578cd9ad6040aa1441184c9372d41ac2da709693571f5e08ca99bbd43ecadaa15cff3575590fd5d7b6806af9991fda7c0873b3 + checksum: 51e32f95d39bb3b4d3f1af50178a00ac6142b43f840070b0d0e78dd372644586040ac4b60b60b362519a68e59729d34101aa9ef851b9f5946136a007cda71f67 languageName: node linkType: hard -"@cardano-sdk/input-selection@npm:0.12.25, @cardano-sdk/input-selection@npm:~0.12.25": - version: 0.12.25 - resolution: "@cardano-sdk/input-selection@npm:0.12.25" +"@cardano-sdk/input-selection@npm:0.12.26, @cardano-sdk/input-selection@npm:~0.12.26": + version: 0.12.26 + resolution: "@cardano-sdk/input-selection@npm:0.12.26" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/key-management": ~0.20.1 @@ -7803,7 +7803,7 @@ __metadata: bignumber.js: ^9.1.1 lodash: ^4.17.21 ts-custom-error: ^3.2.0 - checksum: c035b2aad5a7a584594efc7efa99dd8bb9ecaac2c68025df730914d146a5f3334f07da2dddfe8349180d51b09229f5c599315f7fa5609b02723515478bebdec7 + checksum: 0bc9f3bf84cb061196d61c38bc3590f284c9313f932a88c69207c1ab8919d08b47a76f59f1b16ce92c45a9ccf0c4cb3112b0f9cc5829166a5f803e99d9df8811 languageName: node linkType: hard @@ -7828,13 +7828,13 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/tx-construction@npm:0.18.1, @cardano-sdk/tx-construction@npm:~0.18.1": - version: 0.18.1 - resolution: "@cardano-sdk/tx-construction@npm:0.18.1" +"@cardano-sdk/tx-construction@npm:0.18.2, @cardano-sdk/tx-construction@npm:~0.18.2": + version: 0.18.2 + resolution: "@cardano-sdk/tx-construction@npm:0.18.2" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 - "@cardano-sdk/input-selection": ~0.12.25 + "@cardano-sdk/input-selection": ~0.12.26 "@cardano-sdk/key-management": ~0.20.1 "@cardano-sdk/util": ~0.15.0 "@cardano-sdk/util-rxjs": ~0.7.9 @@ -7843,7 +7843,7 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: 231e150ccaddb5d68eb91b675aff4356e5de2fe2fda518b2539baa212c93f46fd0b49e35f3015e5b7609b0b4603448de26214fa794562274c6f568b8574cc559 + checksum: 9b3e7d319421a2c8237afd289ef241a429e81a49e20b466c436919116536115da5fe62c4124fb1d452a4d90a3b4262468dc1150f402fcf21a74f01e3d7433bc7 languageName: node linkType: hard @@ -7895,18 +7895,18 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/wallet@npm:0.35.1, @cardano-sdk/wallet@npm:~0.35.1": - version: 0.35.1 - resolution: "@cardano-sdk/wallet@npm:0.35.1" +"@cardano-sdk/wallet@npm:0.35.2, @cardano-sdk/wallet@npm:~0.35.2": + version: 0.35.2 + resolution: "@cardano-sdk/wallet@npm:0.35.2" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/dapp-connector": ~0.12.14 - "@cardano-sdk/hardware-ledger": ~0.8.18 - "@cardano-sdk/hardware-trezor": ~0.4.18 - "@cardano-sdk/input-selection": ~0.12.25 + "@cardano-sdk/hardware-ledger": ~0.8.19 + "@cardano-sdk/hardware-trezor": ~0.4.19 + "@cardano-sdk/input-selection": ~0.12.26 "@cardano-sdk/key-management": ~0.20.1 - "@cardano-sdk/tx-construction": ~0.18.1 + "@cardano-sdk/tx-construction": ~0.18.2 "@cardano-sdk/util": ~0.15.0 "@cardano-sdk/util-rxjs": ~0.7.9 backoff-rxjs: ^7.0.0 @@ -7918,24 +7918,24 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.3 - checksum: d22698c52b61d1407746ea971ec825f7a86aff0ef43bbdb64ad74c01f651c15fbfd3eefcb4bd6e536c675dd70d9b05d202072dc37d61c86488fd5df4deeeb38f + checksum: af592b22f6290c6640b71234a28781c251f6c4b7ca2f8fb6cb608bb4e9581fb386ba75b1698bb83a81f638202ab55a300f7a395e2138eb96cc96ba9490c2c69d languageName: node linkType: hard -"@cardano-sdk/web-extension@npm:0.26.0": - version: 0.26.0 - resolution: "@cardano-sdk/web-extension@npm:0.26.0" +"@cardano-sdk/web-extension@npm:0.26.1": + version: 0.26.1 + resolution: "@cardano-sdk/web-extension@npm:0.26.1" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/dapp-connector": ~0.12.14 - "@cardano-sdk/hardware-ledger": ~0.8.18 - "@cardano-sdk/hardware-trezor": ~0.4.18 + "@cardano-sdk/hardware-ledger": ~0.8.19 + "@cardano-sdk/hardware-trezor": ~0.4.19 "@cardano-sdk/key-management": ~0.20.1 - "@cardano-sdk/tx-construction": ~0.18.1 + "@cardano-sdk/tx-construction": ~0.18.2 "@cardano-sdk/util": ~0.15.0 "@cardano-sdk/util-rxjs": ~0.7.9 - "@cardano-sdk/wallet": ~0.35.1 + "@cardano-sdk/wallet": ~0.35.2 backoff-rxjs: ^7.0.0 lodash: ^4.17.21 rxjs: ^7.4.0 @@ -7943,7 +7943,7 @@ __metadata: ts-log: ^2.2.3 uuid: ^8.3.2 webextension-polyfill: ^0.8.0 - checksum: 54c356067e0c63c88af6cb7131349106f71a35790826a9d60c562f2bb7af6f19330142615fa71989753a852ddbae41dccb85f1818b2c5fe72f1c8e11b668bced + checksum: 3362f8fedb1af6ad2b167763edc640eb879c87d25ac27ec3f12be6506c61951d02fea4ad3de9f9817528333d7e6cb11298309b81f450cd58eac35d7a302f5476 languageName: node linkType: hard @@ -10580,11 +10580,11 @@ __metadata: "@cardano-sdk/cardano-services-client": 0.18.0 "@cardano-sdk/core": 0.30.0 "@cardano-sdk/dapp-connector": 0.12.14 - "@cardano-sdk/input-selection": 0.12.25 - "@cardano-sdk/tx-construction": 0.18.1 + "@cardano-sdk/input-selection": 0.12.26 + "@cardano-sdk/tx-construction": 0.18.2 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/wallet": 0.35.1 - "@cardano-sdk/web-extension": 0.26.0 + "@cardano-sdk/wallet": 0.35.2 + "@cardano-sdk/web-extension": 0.26.1 "@emurgo/cardano-message-signing-asmjs": 1.0.1 "@emurgo/cip14-js": ~3.0.1 "@koralabs/handles-public-api-interfaces": ^1.6.6 @@ -10650,13 +10650,13 @@ __metadata: "@cardano-sdk/cardano-services-client": 0.18.0 "@cardano-sdk/core": 0.30.0 "@cardano-sdk/crypto": 0.1.22 - "@cardano-sdk/hardware-ledger": 0.8.18 - "@cardano-sdk/hardware-trezor": 0.4.18 + "@cardano-sdk/hardware-ledger": 0.8.19 + "@cardano-sdk/hardware-trezor": 0.4.19 "@cardano-sdk/key-management": 0.20.1 "@cardano-sdk/util": 0.15.0 "@cardano-sdk/util-dev": 0.19.18 - "@cardano-sdk/wallet": 0.35.1 - "@cardano-sdk/web-extension": 0.26.0 + "@cardano-sdk/wallet": 0.35.2 + "@cardano-sdk/web-extension": 0.26.1 "@emurgo/cardano-message-signing-browser": 1.0.1 "@lace/common": 0.1.0 "@stablelib/chacha20poly1305": 1.0.1 @@ -10818,8 +10818,8 @@ __metadata: "@ant-design/icons": ^4.7.0 "@babel/core": ^7.21.0 "@cardano-sdk/core": 0.30.0 - "@cardano-sdk/input-selection": 0.12.25 - "@cardano-sdk/tx-construction": 0.18.1 + "@cardano-sdk/input-selection": 0.12.26 + "@cardano-sdk/tx-construction": 0.18.2 "@cardano-sdk/util": 0.15.0 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 @@ -10883,11 +10883,11 @@ __metadata: wait-on: ^7.0.1 zustand: ^4.4.1 peerDependencies: - "@cardano-sdk/input-selection": 0.12.25 - "@cardano-sdk/tx-construction": 0.18.1 + "@cardano-sdk/input-selection": 0.12.26 + "@cardano-sdk/tx-construction": 0.18.2 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/wallet": 0.35.1 - "@cardano-sdk/web-extension": 0.26.0 + "@cardano-sdk/wallet": 0.35.2 + "@cardano-sdk/web-extension": 0.26.1 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0 From e89d70fced97ed9cf32fc2d20ec938a89ae1600a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Thu, 4 Apr 2024 16:32:56 +0200 Subject: [PATCH 27/74] test(extension): maintenance 4th Apr 2024 (#1025) * test(extension): fix LW-2338, LW-6661, LW-6662 * test(extension): block LW-2343 due to bug * test(extension): block LW-2541 due to bug * test(extension): fix LW-8501 --- .../e2e-tests/src/assert/educationalListAssert.ts | 15 +++++++++++++-- packages/e2e-tests/src/assert/tokensPageAssert.ts | 2 +- packages/e2e-tests/src/elements/tokensPage.ts | 5 +++++ .../src/features/OnboardingCreateWallet.feature | 5 +++-- .../src/features/TokensPagePopup.feature | 3 ++- .../src/features/TransactionsPopup.feature | 3 ++- packages/e2e-tests/src/steps/onboardingSteps.ts | 6 ++++++ 7 files changed, 32 insertions(+), 7 deletions(-) diff --git a/packages/e2e-tests/src/assert/educationalListAssert.ts b/packages/e2e-tests/src/assert/educationalListAssert.ts index 566026910..842dd1a69 100644 --- a/packages/e2e-tests/src/assert/educationalListAssert.ts +++ b/packages/e2e-tests/src/assert/educationalListAssert.ts @@ -36,12 +36,23 @@ class EducationalListAssert { const glossaryTranslation = await t(this.glossaryTranslationPath); const videoTranslation = await t(this.videoTranslationPath); const expectedTitle = await t('browserView.sidePanel.aboutYourWallet'); - const expectedTitles = [glossaryTranslation, faqTranslation, videoTranslation, videoTranslation]; + const expectedTitles = [ + glossaryTranslation, + faqTranslation, + videoTranslation, + videoTranslation, + faqTranslation, + faqTranslation, + faqTranslation + ]; const expectedSubtitles = [ await t('educationalBanners.subtitle.whatIsADigitalAsset'), await t('educationalBanners.subtitle.howToSendReceiveFunds'), await t('educationalBanners.subtitle.secureSelfCustody'), - await t('educationalBanners.subtitle.connectingDApps') + await t('educationalBanners.subtitle.connectingDApps'), + await t('educationalBanners.subtitle.conwayEra'), + await t('educationalBanners.subtitle.governanceFeatures'), + await t('educationalBanners.subtitle.governanceActions') ]; await this.assertSeeWidget(expectedTitle, expectedTitles, expectedSubtitles); } diff --git a/packages/e2e-tests/src/assert/tokensPageAssert.ts b/packages/e2e-tests/src/assert/tokensPageAssert.ts index fe85c0d4a..c03d4466b 100644 --- a/packages/e2e-tests/src/assert/tokensPageAssert.ts +++ b/packages/e2e-tests/src/assert/tokensPageAssert.ts @@ -29,7 +29,7 @@ class TokensPageAssert { assertCounterNumberMatchesWalletTokens = async () => { const tokensCounterValue = Number((await TokensPage.counter.getText()).slice(1, -1)); if (tokensCounterValue > 0) await TokensPage.coinGeckoCredits.scrollIntoView(); - await browser.pause(1000); + await TokensPage.tokenRowSkeleton.waitForDisplayed({ reverse: true, timeout: 60_000 }); const rowsNumber = (await TokensPage.getRows()).length; expect(rowsNumber).to.equal(tokensCounterValue); }; diff --git a/packages/e2e-tests/src/elements/tokensPage.ts b/packages/e2e-tests/src/elements/tokensPage.ts index d561e2ad3..f992e9347 100644 --- a/packages/e2e-tests/src/elements/tokensPage.ts +++ b/packages/e2e-tests/src/elements/tokensPage.ts @@ -20,6 +20,7 @@ class TokensPage { private CLOSED_EYE_ICON = '[data-testid="closed-eye-icon"]'; private OPENED_EYE_ICON = '[data-testid="opened-eye-icon"]'; private VIEW_ALL_BUTTON = '[data-testid="view-all-button"]'; + private TOKEN_ROW_SKELETON = '.ant-skeleton'; get sendButtonPopupMode(): ChainablePromiseElement { return $(this.SEND_BUTTON_POPUP_MODE); @@ -98,6 +99,10 @@ class TokensPage { return $(this.OPENED_EYE_ICON); } + get tokenRowSkeleton(): ChainablePromiseElement { + return $(this.TOKEN_ROW_SKELETON); + } + async getRows(): Promise { return $$(this.TOKENS_TABLE_ROW); } diff --git a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature index d06de903f..a1546e3da 100755 --- a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature @@ -260,11 +260,12 @@ Feature: Onboarding - Create wallet Scenario: Create Wallet - Mnemonic verification - incorrect word order Given I click "Create" button on wallet setup page Then "Mnemonic writedown" page is displayed with 24 words + And I save mnemonic words And I click "Next" button during wallet setup - When I enter 24 incorrect mnemonic words on "Mnemonic verification" page + When I fill passphrase fields using saved 24 words mnemonic in incorrect order Then I see incorrect passphrase error displayed And "Next" button is disabled during onboarding process - When I enter 24 correct mnemonic words on "Mnemonic verification" page + When I enter saved mnemonic words Then "Next" button is enabled during onboarding process @LW-10138 diff --git a/packages/e2e-tests/src/features/TokensPagePopup.feature b/packages/e2e-tests/src/features/TokensPagePopup.feature index e00e5ca12..fc0214081 100644 --- a/packages/e2e-tests/src/features/TokensPagePopup.feature +++ b/packages/e2e-tests/src/features/TokensPagePopup.feature @@ -4,7 +4,8 @@ Feature: LW: Tokens tab - popup view Background: Given Wallet is synced - @LW-2343 @Testnet @Mainnet + @LW-2343 @Testnet @Mainnet @Pending + @issue=LW-10211 Scenario: Tokens title and counter When I see Tokens counter with total number of tokens displayed Then Tokens counter matches the number of wallet tokens diff --git a/packages/e2e-tests/src/features/TransactionsPopup.feature b/packages/e2e-tests/src/features/TransactionsPopup.feature index c1c954d0a..e90dd5c6f 100644 --- a/packages/e2e-tests/src/features/TransactionsPopup.feature +++ b/packages/e2e-tests/src/features/TransactionsPopup.feature @@ -9,7 +9,8 @@ Feature: Transactions - Popup view When I navigate to Transactions popup page Then Transactions section is displayed - @LW-2541 @LW-2543 @LW-2544 @Testnet + @LW-2541 @LW-2543 @LW-2544 @Testnet @Pending + @issue=LW-101212 Scenario: Popup View - Transactions tab - Counter matches the number of rows, transactions are loaded and skeleton disappears When I navigate to Transactions popup page And Transactions section is displayed diff --git a/packages/e2e-tests/src/steps/onboardingSteps.ts b/packages/e2e-tests/src/steps/onboardingSteps.ts index ed5f51fde..628672b68 100644 --- a/packages/e2e-tests/src/steps/onboardingSteps.ts +++ b/packages/e2e-tests/src/steps/onboardingSteps.ts @@ -30,6 +30,7 @@ import analyticsBanner from '../elements/analyticsBanner'; import { getWalletsFromRepository } from '../fixture/walletRepositoryInitializer'; import OnboardingWalletSetupPageAssert from '../assert/onboarding/onboardingWalletSetupPageAssert'; import OnboardingAnalyticsBannerAssert from '../assert/onboarding/onboardingAnalyticsBannerAssert'; +import { shuffle } from '../utils/arrayUtils'; const mnemonicWords: string[] = getTestWallet(TestWalletName.TestAutomationWallet).mnemonic ?? []; const invalidMnemonicWords: string[] = getTestWallet(TestWalletName.InvalidMnemonic).mnemonic ?? []; @@ -408,3 +409,8 @@ Then(/^I see Analytics banner displayed correctly$/, async () => { Then(/^I (see|do not see) Analytics banner$/, async (shouldSee: 'see' | 'do not see') => { await OnboardingAnalyticsBannerAssert.assertBannerIsVisible(shouldSee === 'see'); }); + +When(/^I fill passphrase fields using saved 24 words mnemonic in incorrect order$/, async () => { + const shuffledWords = shuffle([...mnemonicWordsForReference]); + await RecoveryPhrasePage.enterMnemonicWords(shuffledWords); +}); From f81c2727f985a251319e06b18239e4303a638c6c Mon Sep 17 00:00:00 2001 From: John Oshalusi Date: Fri, 5 Apr 2024 14:33:26 +0100 Subject: [PATCH 28/74] fix: resolve broken validation on hw onboarding (#1029) --- .../WalletSetupSelectAccountsStepRevamp.module.scss | 4 ++++ .../WalletSetupSelectAccountsStepRevamp.tsx | 2 +- packages/e2e-tests/src/assert/transactionDetailsAssert.ts | 6 ++++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.module.scss b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.module.scss index d0aaf62dc..1d9992840 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.module.scss +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.module.scss @@ -6,3 +6,7 @@ color: var(--text-color-primary) !important; margin-bottom: size_unit(3); } + +.formError { + color: var(--data-pink); +} diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx index e1a5029ca..69a31ace8 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx @@ -48,7 +48,7 @@ export const WalletSetupSelectAccountsStepRevamp = ({ title={t('core.walletSetupSelectAccountsStep.setupLaceWallet')} onBack={onBack} onNext={() => onSubmit(Number(selectedAccount), walletName)} - isNextEnabled={Number(selectedAccount) >= 0} + isNextEnabled={Number(selectedAccount) >= 0 && !!walletName && isNameValid} nextLabel={t('core.walletSetupSelectAccountsStep.enterWallet')} description={
{t('core.walletSetupSelectAccountsStep.chooseWalletName')}
} currentTimelineStep={WalletTimelineSteps.WALLET_SETUP} diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index 564ab041e..03ab88a3b 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -113,9 +113,11 @@ class TransactionsDetailsAssert { await TransactionDetailsPage.transactionDetailsTimestamp.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsInputsSection.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsOutputsSection.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsFeeADA.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsFeeFiat.waitForDisplayed(); const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); + if (!txType.includes('Stake Key Registration')) { + await TransactionDetailsPage.transactionDetailsFeeADA.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsFeeFiat.waitForDisplayed(); + } if (txType.includes('Delegation')) { await TransactionDetailsPage.transactionDetailsStakepoolName.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsStakepoolTicker.waitForDisplayed(); From 09b7da468ce2ae8d9e980cacc299b61f599711dd Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Mon, 8 Apr 2024 10:03:12 +0200 Subject: [PATCH 29/74] chore: skip chromatic deployment for draft PRs (#1024) --- .github/workflows/core-chromatic.yml | 2 ++ .github/workflows/git-checks.yml | 5 ++++- .github/workflows/staking-chromatic.yml | 2 ++ .github/workflows/ui-toolkit-chromatic.yml | 2 ++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/core-chromatic.yml b/.github/workflows/core-chromatic.yml index 29f9840e0..134784f84 100644 --- a/.github/workflows/core-chromatic.yml +++ b/.github/workflows/core-chromatic.yml @@ -2,6 +2,7 @@ name: Lace Core Chromatic on: pull_request: + types: [opened, synchronize, reopened, ready_for_review] paths: - packages/core/** push: @@ -12,6 +13,7 @@ on: jobs: chromatic-deployment: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/git-checks.yml b/.github/workflows/git-checks.yml index f98aa6d6e..1131680cf 100644 --- a/.github/workflows/git-checks.yml +++ b/.github/workflows/git-checks.yml @@ -1,9 +1,12 @@ name: Git Checks -on: [pull_request] +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] jobs: block-fixup: + if: github.event.pull_request.draft == false runs-on: ubuntu-20.04 steps: diff --git a/.github/workflows/staking-chromatic.yml b/.github/workflows/staking-chromatic.yml index 1d018617c..cb33e393f 100644 --- a/.github/workflows/staking-chromatic.yml +++ b/.github/workflows/staking-chromatic.yml @@ -2,6 +2,7 @@ name: Lace Staking Chromatic on: pull_request: + types: [opened, synchronize, reopened, ready_for_review] paths: - packages/staking/** push: @@ -12,6 +13,7 @@ on: jobs: chromatic-deployment: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Checkout repository diff --git a/.github/workflows/ui-toolkit-chromatic.yml b/.github/workflows/ui-toolkit-chromatic.yml index 18754166e..fccebcf1e 100644 --- a/.github/workflows/ui-toolkit-chromatic.yml +++ b/.github/workflows/ui-toolkit-chromatic.yml @@ -2,6 +2,7 @@ name: Lace UI Toolkit on: pull_request: + types: [opened, synchronize, reopened, ready_for_review] paths: - packages/ui/** push: @@ -12,6 +13,7 @@ on: jobs: chromatic-deployment: + if: github.event.pull_request.draft == false runs-on: ubuntu-latest steps: - name: Checkout repository From 2a0b19de3c0d10a459a3f8a2a842a77401977761 Mon Sep 17 00:00:00 2001 From: Janusz Janus Date: Mon, 8 Apr 2024 16:25:31 +0700 Subject: [PATCH 30/74] test(extension): unblock pending tests blocked by revamp onboarding (#1036) --- .../elements/onboarding/recoveryPhrasePage.ts | 1 + .../AnalyticsOnboardingEvents.feature | 125 ++++++------------ 2 files changed, 40 insertions(+), 86 deletions(-) diff --git a/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts b/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts index 7f4e3c019..7b57bafba 100644 --- a/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts +++ b/packages/e2e-tests/src/elements/onboarding/recoveryPhrasePage.ts @@ -88,6 +88,7 @@ class RecoveryPhrasePage extends CommonOnboardingElements { async clickHeaderToLoseFocus() { await this.stepHeader.click(); } + async addCharToMnemonicField(characters: string, inputNumber: number) { const inputs = await this.mnemonicInputs; await inputs[inputNumber].addValue(characters); diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature b/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature index ce79489fd..2dca235f8 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsOnboardingEvents.feature @@ -1,103 +1,56 @@ @OnboardingCreateWallet @Analytics @Testnet Feature: Analytics - Posthog - Onboarding - Extended View - @LW-8311 @Pending - @issue=@LW-10109 + @LW-8311 Scenario Outline: Analytics - Posthog events are enabled or disabled based on decision on Analytics page - Given I set up request interception for posthog analytics request(s) - When I click "Create" button on wallet setup page - # When I accept "T&C" checkbox - And I click "Next" button during wallet setup -# When "Help us improve your experience" page is displayed - And I click "" button on Analytics page - And I enter wallet name: "wallet" - And I click "Next" button during wallet setup - # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" - And I click "Next" button during wallet setup -# When "Mnemonic info" page is displayed - And I validate that analytics event(s) have been sent - + Given "Get started" page is displayed + When I enable showing Analytics consent banner + And I set up request interception for posthog analytics request(s) + And I analytics banner on "Get started" page + And I click "Create" button on wallet setup page + And I go to "Wallet setup" page from "Create" wallet flow and "not fill" values + Then I validate that analytics event(s) have been sent Examples: | enable_analytics | number_of_events | - | Agree | 4 | - | Skip | 0 | + | accept | 4 | + | reject | 1 | - @LW-7363 @Pending - @issue=@LW-10109 + @LW-7363 Scenario: Analytics - Restore wallet events / check that alias event is assigning same id in posthog Given I set up request interception for posthog analytics request(s) When I click "Restore" button on wallet setup page - When I click "OK" button on "Restoring a multi-address wallet?" modal - # When I accept "T&C" checkbox - And I click "Next" button during wallet setup -# When "Help us improve your experience" page is displayed - And I click "Agree" button on Analytics page - And I validate latest analytics multiple events: - | wallet \| session start \| pageview | - | onboarding \| restore wallet \| analytics \| agree \| click | - When "Wallet setup" page is displayed - And I enter wallet name: "wallet" - And I click "Next" button during wallet setup - Then I validate latest analytics single event "onboarding | restore wallet | wallet name | next | click" - # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" - And I click "Next" button during wallet setup - Then I validate latest analytics single event "onboarding | restore wallet | wallet password | next | click" -# When "Recovery phrase length page" is displayed and 24 words checkbox is checked - And I click "Next" button during wallet setup - Then I validate latest analytics single event "onboarding | restore wallet | recovery phrase length | next | click" - # When I pass "Mnemonic verification" page with words 8 of 24 - Then I validate latest analytics single event "onboarding | restore wallet | enter passphrase #01 | next | click" - # When I pass "Mnemonic verification" page with words 16 of 24 - Then I validate latest analytics single event "onboarding | restore wallet | enter passphrase #09 | next | click" - # When I pass "Mnemonic verification" page with words 24 of 24 - Then I validate latest analytics single event "onboarding | restore wallet | enter passphrase #17 | next | click" - When I click "Go to my wallet" button on "All done" page - And I validate latest analytics multiple events: - | onboarding \| restore wallet \| all done \| go to my wallet \| click | - | $create_alias | + And I go to "Mnemonic verification" page from "Restore" wallet flow + Then I validate latest analytics single event "onboarding | restore wallet revamp | restore | click" + When I click "Next" button during wallet setup + Then "Wallet setup" page is displayed + And I validate latest analytics single event "onboarding | restore wallet revamp | enter your recovery phrase | next | click" + When I enter wallet name: "ValidName", password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + And I click "Enter wallet" button + Then I validate latest analytics multiple events: + | onboarding \| restore wallet revamp \| let's set up your new wallet \| enter wallet \| click | + | $create_alias | And I validate that alias event has assigned same user id "5b3ca1f1f7a14aad1e79f46213e2777d" in posthog - And I validate that 10 analytics event(s) have been sent - @LW-7365 @Pending - @issue=@LW-10109 + @LW-7365 Scenario: Analytics - Onboarding new wallet events - Given I set up request interception for posthog analytics request(s) + Given "Get started" page is displayed + When I enable showing Analytics consent banner + And I set up request interception for posthog analytics request(s) + And I accept analytics banner on "Get started" page + Then I validate latest analytics single event "wallet | onboarding | analytics banner | agree | click" When I click "Create" button on wallet setup page - # When I accept "T&C" checkbox - And I click "Next" button during wallet setup -# When "Help us improve your experience" page is displayed - And I click "Agree" button on Analytics page - And I validate latest analytics multiple events: - | wallet \| session start \| pageview | - | onboarding \| new wallet \| analytics \| agree \| click | - When "Wallet setup" page is displayed - And I enter wallet name: "wallet" - And I click "Next" button during wallet setup - Then I validate latest analytics single event "onboarding | new wallet | wallet name | next | click" - # When I enter password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" - And I click "Next" button during wallet setup - Then I validate latest analytics single event "onboarding | new wallet | wallet password | next | click" -# When "Mnemonic info" page is displayed - And I click "Next" button during wallet setup - Then I validate latest analytics single event "onboarding | new wallet | passphrase intro | next | click" - # When I pass "Mnemonic writedown" page with words 8 of 24 - Then I validate latest analytics single event "onboarding | new wallet | write passphrase #01 | next | click" - # When I pass "Mnemonic writedown" page with words 16 of 24 - Then I validate latest analytics single event "onboarding | new wallet | write passphrase #09 | next | click" - # When I pass "Mnemonic writedown" page with words 24 of 24 - Then I validate latest analytics single event "onboarding | new wallet | write passphrase #17 | next | click" - # When I pass "Mnemonic verification" page with words 8 of 24 - Then I validate latest analytics single event "onboarding | new wallet | enter passphrase #01 | next | click" - # When I pass "Mnemonic verification" page with words 16 of 24 - Then I validate latest analytics single event "onboarding | new wallet | enter passphrase #09 | next | click" - # When I pass "Mnemonic verification" page with words 24 of 24 - Then I validate latest analytics single event "onboarding | new wallet | enter passphrase #17 | next | click" - When I click "Go to my wallet" button on "All done" page - And I see LW homepage - And I validate latest analytics multiple events: - | onboarding \| new wallet \| all done \| go to my wallet \| click | - | $create_alias | - And I validate that 13 analytics event(s) have been sent + Then I validate latest analytics single event "onboarding | new wallet revamp | create | click" + When I go to "Mnemonic verification" page from "Create" wallet flow + Then I validate latest analytics single event "onboarding | new wallet revamp | save your recovery phrase | next | click" + When I click "Next" button during wallet setup + Then "Wallet setup" page is displayed + And I validate latest analytics single event "onboarding | new wallet revamp | enter your recovery phrase | next | click" + When I enter wallet name: "ValidName", password: "N_8J@bne87A" and password confirmation: "N_8J@bne87A" + And I click "Enter wallet" button + Then I validate latest analytics multiple events: + | onboarding \| new wallet revamp \| let's set up your new wallet \| enter wallet \| click | + | $create_alias | + And I validate that 6 analytics event(s) have been sent @LW-7364 @Pending # Disabled as user is opted out until he decision about tracking. From 02a21724e83cafdfa344df4e8149e1035ca0c873 Mon Sep 17 00:00:00 2001 From: Tom Mayel <143514860+tommayeliog@users.noreply.github.com> Date: Mon, 8 Apr 2024 10:52:26 +0100 Subject: [PATCH 31/74] fix(extension): fix infinite scroll on PopupView (#1034) [LW-10211] --- .../src/components/Layout/MainLayout.module.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss b/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss index 984549d0d..1d6d8f2d1 100644 --- a/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss +++ b/apps/browser-extension-wallet/src/components/Layout/MainLayout.module.scss @@ -21,7 +21,6 @@ display: flex; min-height: 0; width: 100%; - flex-direction: column; > * { width: 100%; } From 1b24ad577a4681836ec4736350c3e1c6780355e4 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:21:03 +0100 Subject: [PATCH 32/74] fix(extension): [LW-10227] fix 2nd dapp transaction overwriting first (#1032) --- .../DappTransactionContainer.tsx | 33 ++----------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx index 73a523c67..5654c2393 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx @@ -7,9 +7,6 @@ import { useViewsFlowContext } from '@providers/ViewFlowProvider'; import { Wallet } from '@lace/cardano'; import { withAddressBookContext } from '@src/features/address-book/context'; import { useWalletStore } from '@stores'; -import { exposeApi, RemoteApiPropertyType } from '@cardano-sdk/web-extension'; -import { DAPP_CHANNELS } from '@src/utils/constants'; -import { runtime } from 'webextension-polyfill'; import { useFetchCoinPrice, useChainHistoryProvider } from '@hooks'; import { createTxInspector, @@ -21,11 +18,9 @@ import { } from '@cardano-sdk/core'; import { createWalletAssetProvider } from '@cardano-sdk/wallet'; import { Skeleton } from 'antd'; -import type { UserPromptService } from '@lib/scripts/background/services'; -import { of, take } from 'rxjs'; import { useCurrencyStore, useAppSettingsContext } from '@providers'; -import { logger, signingCoordinator } from '@lib/wallet-api-ui'; +import { logger } from '@lib/wallet-api-ui'; import { useComputeTxCollateral } from '@hooks/useComputeTxCollateral'; import { utxoAndBackendChainHistoryResolver } from '@src/utils/utxo-chain-history-resolver'; @@ -36,7 +31,7 @@ interface DappTransactionContainerProps { export const DappTransactionContainer = withAddressBookContext( ({ errorMessage }: DappTransactionContainerProps): React.ReactElement => { const { - signTxRequest: { request: req, set: setSignTxRequest }, + signTxRequest: { request: req }, dappInfo } = useViewsFlowContext(); @@ -78,30 +73,6 @@ export const DappTransactionContainer = withAddressBookContext( const tx = useMemo(() => req?.transaction.toCore(), [req?.transaction]); const txCollateral = useComputeTxCollateral(walletState, tx); - useEffect(() => { - const subscription = signingCoordinator.transactionWitnessRequest$.pipe(take(1)).subscribe(async (r) => { - setSignTxRequest(r); - }); - - const api = exposeApi>( - { - api$: of({ - async readyToSignTx(): Promise { - return Promise.resolve(true); - } - }), - baseChannel: DAPP_CHANNELS.userPrompt, - properties: { readyToSignTx: RemoteApiPropertyType.MethodReturningPromise } - }, - { logger: console, runtime } - ); - - return () => { - subscription.unsubscribe(); - api.shutdown(); - }; - }, [setSignTxRequest]); - const userAddresses = useMemo(() => walletInfo.addresses.map((v) => v.address), [walletInfo.addresses]); const userRewardAccounts = useObservable(inMemoryWallet.delegation.rewardAccounts$); const rewardAccountsAddresses = useMemo(() => userRewardAccounts?.map((key) => key.address), [userRewardAccounts]); From 7001d6c0c862b1ac9b54b1b6fbabceed1df1919c Mon Sep 17 00:00:00 2001 From: Janusz Janus Date: Mon, 8 Apr 2024 20:29:42 +0700 Subject: [PATCH 33/74] test(extension): e2e fixes (#1038) --- .../src/assert/transactionDetailsAssert.ts | 23 +++++++++++-------- .../src/elements/onboarding/mainPage.ts | 2 +- .../features/OnboardingCreateWallet.feature | 4 ++-- .../SendTransactionBundlesExtended.feature | 2 +- ...endTransactionSimpleExtended.part1.feature | 2 +- ...endTransactionSimpleExtended.part2.feature | 3 ++- .../SendTransactionSimplePopup.part1.feature | 2 +- .../src/features/SettingsPageExtended.feature | 2 +- .../src/features/TokensPageExtended.feature | 3 ++- 9 files changed, 25 insertions(+), 18 deletions(-) diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index 03ab88a3b..64d8c4f84 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -25,6 +25,8 @@ export type TransactionData = { assets?: string[]; }; +const stakeKeyRegistration = 'Stake Key Registration'; + class TransactionsDetailsAssert { waitForTransactionsLoaded = async () => { await browser.waitUntil(async () => (await TransactionsPage.rows).length > 1, { @@ -114,7 +116,7 @@ class TransactionsDetailsAssert { await TransactionDetailsPage.transactionDetailsInputsSection.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsOutputsSection.waitForDisplayed(); const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); - if (!txType.includes('Stake Key Registration')) { + if (!txType.includes(stakeKeyRegistration)) { await TransactionDetailsPage.transactionDetailsFeeADA.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsFeeFiat.waitForDisplayed(); } @@ -164,6 +166,7 @@ class TransactionsDetailsAssert { const txDetailsInputADAValueString = await TransactionDetailsPage.transactionDetailsInputAdaAmount.getText(); const txDetailsInputADAValue = Number(txDetailsInputADAValueString.split(' ', 1)); + const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); const txDetailsInputFiatValueString = await TransactionDetailsPage.transactionDetailsInputFiatAmount.getText(); const txDetailsInputFiatValue = Number(txDetailsInputFiatValueString.slice(1).split(' ', 1)); @@ -174,18 +177,20 @@ class TransactionsDetailsAssert { const txDetailsOutputFiatValueString = await TransactionDetailsPage.transactionDetailsOutputFiatAmount.getText(); const txDetailsOutputFiatValue = Number(txDetailsOutputFiatValueString.slice(1).split(' ', 1)); - const txDetailsFeeADAValueString = await TransactionDetailsPage.transactionDetailsFeeADA.getText(); - const txDetailsFeeADAValue = Number(txDetailsFeeADAValueString.split(' ', 1)); + if (!txType.includes(stakeKeyRegistration)) { + const txDetailsFeeADAValueString = await TransactionDetailsPage.transactionDetailsFeeADA.getText(); + const txDetailsFeeADAValue = Number(txDetailsFeeADAValueString.split(' ', 1)); + expect(txDetailsFeeADAValue).to.be.greaterThan(0); - const txDetailsFeeFiatValueString = await TransactionDetailsPage.transactionDetailsFeeFiat.getText(); - const txDetailsFeeFiatValue = Number(txDetailsFeeFiatValueString.slice(1).split(' ', 1)); + const txDetailsFeeFiatValueString = await TransactionDetailsPage.transactionDetailsFeeFiat.getText(); + const txDetailsFeeFiatValue = Number(txDetailsFeeFiatValueString.slice(1).split(' ', 1)); + expect(txDetailsFeeFiatValue).to.be.greaterThan(0); + } expect(txDetailsInputADAValue).to.be.greaterThan(0); expect(txDetailsInputFiatValue).to.be.greaterThan(0); expect(txDetailsOutputADAValue).to.be.greaterThan(0); expect(txDetailsOutputFiatValue).to.be.greaterThan(0); - expect(txDetailsFeeADAValue).to.be.greaterThan(0); - expect(txDetailsFeeFiatValue).to.be.greaterThan(0); await TransactionDetailsPage.closeActivityDetails(mode); } @@ -221,8 +226,8 @@ class TransactionsDetailsAssert { if ((await TransactionsPage.transactionsTableItemType(i).getText()) !== 'Self Transaction') { await TransactionsPage.clickOnTransactionRow(i); await TransactionDetailsPage.transactionDetailsDescription.waitForClickable({ timeout: 15_000 }); - const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); - if (!txType.includes('Delegation')) { + const txType = (await TransactionDetailsPage.transactionDetailsDescription.getText()).split('\n')[0]; + if (!['Delegation', stakeKeyRegistration].includes(txType)) { const tokensAmountSummary = (await TransactionDetailsPage.getTransactionSentTokensWithoutDuplicates()).length + 1; let tokensDescriptionAmount = await TransactionDetailsPage.transactionDetailsAmountOfTokens.getText(); diff --git a/packages/e2e-tests/src/elements/onboarding/mainPage.ts b/packages/e2e-tests/src/elements/onboarding/mainPage.ts index 01b4cbb21..7bf4fe4e2 100644 --- a/packages/e2e-tests/src/elements/onboarding/mainPage.ts +++ b/packages/e2e-tests/src/elements/onboarding/mainPage.ts @@ -1,4 +1,4 @@ -import { TestWalletName, getTestWallet } from '../../support/walletConfiguration'; +import { getTestWallet, TestWalletName } from '../../support/walletConfiguration'; import CommonOnboardingElements from './commonOnboardingElements'; import recoveryPhrasePage from './recoveryPhrasePage'; import walletSetupPage from './walletSetupPage'; diff --git a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature index a1546e3da..c8011b42b 100755 --- a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature @@ -210,9 +210,9 @@ Feature: Onboarding - Create wallet | 21 | is | disabled | @LW-5844 - Scenario Outline: "Get started" page - Legal links - click on link + Scenario Outline: "Get started" page - Legal links in footer - click on link When "Get started" page is displayed - And I click on "" legal link on "Main page" + And I click on "" legal link Then "" is displayed in new tab Examples: | legal_link | diff --git a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature index cbc8651be..6023d7fa2 100644 --- a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature +++ b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature @@ -139,7 +139,7 @@ Feature: Send - Extended Browser View (Advanced Tx) And click on the coin selector for "tADA" asset in bundle 2 And click on an token with name: "LaceCoin" And I enter a value of: 1 to the "LaceCoin1" asset in bundle 2 - Then transaction fee is around 0.19 ADA and Ada allocation cost is around 1.07 ADA + Then transaction fee is around 0.25 ADA and Ada allocation cost is around 1.07 ADA @LW-4505 Scenario: Extended-view - Cancel transaction with multiple bundles on Summary page diff --git a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature index f78bc4fec..6ca3d9e44 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature @@ -189,7 +189,7 @@ Feature: LW-484: Send & Receive - Extended Browser View (Simple Tx) Then I verify transaction costs amount is around 0.00 ADA And I enter a valid "shelley" address in the bundle 1 recipient's address When I enter a value of: 2 to the "tADA" asset - Then I verify transaction costs amount is around 0.18 ADA + Then I verify transaction costs amount is around 0.25 ADA @LW-2370 @Testnet @Mainnet Scenario: Extended-view - Tx summary page is displayed diff --git a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature index ce211a140..78f261130 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature @@ -176,7 +176,8 @@ Feature: LW-484: Send & Receive - Extended Browser View (Simple Tx) When I hover over the value for "tADA" asset in bundle 1 Then the "MAX" button is not displayed - @LW-4762 @Testnet @Mainnet + @LW-4762 @Testnet @Mainnet @Pending + @issue=LW-10242 Scenario: Extended View - Send flow - Enter and Escape buttons support When I click "Send" button on page header And I press keyboard Enter button diff --git a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature index 3326ed2c5..15bbe09cf 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature @@ -182,7 +182,7 @@ Feature: LW-484: Send & Receive - Popup View (Simple Tx) Then I verify transaction costs amount is around 0.00 ADA And I enter a valid "shelley" address in the bundle 1 recipient's address When I enter a value of: 2 to the "tADA" asset in bundle 1 - Then I verify transaction costs amount is around 0.18 ADA + Then I verify transaction costs amount is around 0.25 ADA @LW-2404 @Testnet @Mainnet Scenario: Popup-view - Tx summary page is displayed - single asset (ADA) diff --git a/packages/e2e-tests/src/features/SettingsPageExtended.feature b/packages/e2e-tests/src/features/SettingsPageExtended.feature index a0beecdd9..fc96c00e0 100644 --- a/packages/e2e-tests/src/features/SettingsPageExtended.feature +++ b/packages/e2e-tests/src/features/SettingsPageExtended.feature @@ -2,7 +2,7 @@ Feature: General Settings - Extended Browser View Background: - Given Lace is ready for test + Given Wallet is synced @LW-2324 @Smoke @Mainnet @Testnet Scenario: Extended View - Visibility of Settings page and its content diff --git a/packages/e2e-tests/src/features/TokensPageExtended.feature b/packages/e2e-tests/src/features/TokensPageExtended.feature index b35e0b267..c59877f53 100644 --- a/packages/e2e-tests/src/features/TokensPageExtended.feature +++ b/packages/e2e-tests/src/features/TokensPageExtended.feature @@ -71,7 +71,8 @@ Feature: LW: Tokens tab - extended view | FAQ | What type of governance features are supported in Lace using the GovTool in the current SanchoNet test environment? | | FAQ | What type of governance actions are supported by Lace? | - @LW-4878 @Testnet @Mainnet + @LW-4878 @Testnet @Mainnet @Pending + @issue=LW-10242 Scenario: Extended-view - Tokens details - Enter and Escape buttons support And I click token with name: "Cardano" And The Token details screen is displayed for token "Cardano" with ticker "tADA" in extended mode From d7bff3ef626ee99f2f698f821f9138d79ff6dc85 Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Mon, 8 Apr 2024 16:37:52 +0200 Subject: [PATCH 34/74] chore: setup GitHub action for SonarCloud analysis (#977) --- .github/workflows/sonar-cloud.yml | 26 ++++++++++++++++++++++++++ sonar-project.properties | 11 +++++++++++ 2 files changed, 37 insertions(+) create mode 100644 .github/workflows/sonar-cloud.yml create mode 100644 sonar-project.properties diff --git a/.github/workflows/sonar-cloud.yml b/.github/workflows/sonar-cloud.yml new file mode 100644 index 000000000..8523a93a8 --- /dev/null +++ b/.github/workflows/sonar-cloud.yml @@ -0,0 +1,26 @@ +name: SonarCloud +on: + push: + branches: + - main + - release/**/* + - feat/* + - feat-* + - fix/* + - fix-* + - lw-* + pull_request: + types: [opened, synchronize, reopened] +jobs: + sonarcloud: + name: SonarCloud Code Analysis + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 000000000..d6f79f961 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,11 @@ +sonar.projectKey=input-output-hk_lace +sonar.organization=input-output-hk + +sonar.projectName=lace +sonar.projectVersion=1.0 + +sonar.inclusions=apps/**/*,packages/**/* +sonar.exclusions=packages/e2e-tests/**/*,**/test/**/*,**/__tests__/**/*,**/*.stories.tsx,**/*.test.ts,**/*.test.tsx +sonar.tests.inclusions=apps/**/__tests__/**/*,packages/**/__tests__/**/*,**/*.test.ts,**/*.test.tsx + +sonar.sourceEncoding=UTF-8 From 320522cb932e6f19aeced803ac79b21d03c055c8 Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 8 Apr 2024 19:03:37 -0300 Subject: [PATCH 35/74] fix: add button type and on submit handler (#1041) --- .../EnableAccountPasswordPrompt.tsx | 4 ++++ .../password-box/password-box-button.component.tsx | 7 ++++++- .../design-system/password-box/password-box.component.tsx | 4 +++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/core/src/ui/components/Account/EnableAccountPasswordPrompt/EnableAccountPasswordPrompt.tsx b/packages/core/src/ui/components/Account/EnableAccountPasswordPrompt/EnableAccountPasswordPrompt.tsx index 8f7c39179..5c7476f2a 100644 --- a/packages/core/src/ui/components/Account/EnableAccountPasswordPrompt/EnableAccountPasswordPrompt.tsx +++ b/packages/core/src/ui/components/Account/EnableAccountPasswordPrompt/EnableAccountPasswordPrompt.tsx @@ -78,6 +78,10 @@ export const EnableAccountPasswordPrompt = ({ label={translations.passwordPlaceholder} data-testid="enable-account-password-input" onChange={(e) => setCurrentPassword(e.target.value)} + onSubmit={(event): void => { + event.preventDefault(); + onConfirm(Buffer.from(currentPassword)); + }} errorMessage={wasPasswordIncorrect ? translations.wrongPassword : undefined} rootStyle={{ width: '100%' }} /> diff --git a/packages/ui/src/design-system/password-box/password-box-button.component.tsx b/packages/ui/src/design-system/password-box/password-box-button.component.tsx index 9f9822d67..7fbb1bd71 100644 --- a/packages/ui/src/design-system/password-box/password-box-button.component.tsx +++ b/packages/ui/src/design-system/password-box/password-box-button.component.tsx @@ -18,7 +18,12 @@ export const PasswordInputButton = ({ isPasswordVisible, }: Readonly): JSX.Element => { return ( - )} diff --git a/packages/core/src/ui/components/ActivityDetail/Collateral.tsx b/packages/core/src/ui/components/ActivityDetail/Collateral.tsx index 631484ae7..affab932e 100644 --- a/packages/core/src/ui/components/ActivityDetail/Collateral.tsx +++ b/packages/core/src/ui/components/ActivityDetail/Collateral.tsx @@ -33,9 +33,9 @@ export const Collateral = ({ switch (status) { case 'review': case 'error': - return t('package.core.activityDetails.collateral.tooltip.info'); + return t('core.activityDetails.collateral.tooltip.info'); case 'success': - return t('package.core.activityDetails.collateral.tooltip.success'); + return t('core.activityDetails.collateral.tooltip.success'); } return ''; @@ -46,14 +46,14 @@ export const Collateral = ({ {status === CollateralStatus.ERROR && ( - } message={t('package.core.activityDetails.collateral.error')} /> + } message={t('core.activityDetails.collateral.error')} /> )} diff --git a/packages/core/src/ui/components/ActivityDetail/RewardsDetails.tsx b/packages/core/src/ui/components/ActivityDetail/RewardsDetails.tsx index 497752b27..07861f074 100644 --- a/packages/core/src/ui/components/ActivityDetail/RewardsDetails.tsx +++ b/packages/core/src/ui/components/ActivityDetail/RewardsDetails.tsx @@ -40,14 +40,14 @@ export const RewardsDetails = ({ }: RewardsDetailsProps): React.ReactElement => { const { t } = useTranslate(); const poolRewards = rewards.rewards.filter((reward) => !!reward.pool); - const tooltipContent = t('package.core.activityDetails.rewardsDescription'); + const tooltipContent = t('core.activityDetails.rewardsDescription'); return (
-
{t('package.core.activityDetails.header')}
-

{t('package.core.activityDetails.summary')}

+
{t('core.activityDetails.header')}
+

{t('core.activityDetails.summary')}

@@ -68,7 +68,7 @@ export const RewardsDetails = ({ {poolRewards.length > 0 && (
-
{t('package.core.activityDetails.pools')}
+
{t('core.activityDetails.pools')}
{poolRewards.map(({ pool, amount }) => (
@@ -107,17 +107,17 @@ export const RewardsDetails = ({ )}
-
{t('package.core.activityDetails.status')}
+
{t('core.activityDetails.status')}
{`${status .charAt(0) .toUpperCase()}${status.slice(1)}`}
-
{t('package.core.activityDetails.epoch')}
+
{t('core.activityDetails.epoch')}
{`${rewards.spendableEpoch}`}
-
{t('package.core.activityDetails.timestamp')}
+
{t('core.activityDetails.timestamp')}
{includedDate}  {includedTime} diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx index 2a1848aa7..9fba039d6 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx @@ -153,7 +153,7 @@ export const TransactionDetails = ({ ...detail, ...('title' in detail && detail.title === 'certificateType' && { - details: [t(`package.core.assetActivityItem.entry.name.${detail.details[0]}`)] + details: [t(`core.assetActivityItem.entry.name.${detail.details[0]}`)] }), ...('title' in detail && detail.title === 'anchorURL' && { @@ -173,7 +173,7 @@ export const TransactionDetails = ({ ...p, ...('title' in p && p.title === 'type' && { - details: [t(`package.core.activityDetails.governanceActions.${p.details[0]}`)] + details: [t(`core.activityDetails.governanceActions.${p.details[0]}`)] }), ...('title' in p && p.title === 'anchorURL' && { @@ -189,7 +189,7 @@ export const TransactionDetails = ({ ${Number(detail.details[0])} ${t( // eslint-disable-next-line sonarjs/no-nested-template-literals - `package.core.activityDetails.${ + `core.activityDetails.${ Number(detail.details[0]) === 0 || Number(detail.details[0]) > 1 ? 'epochs' : 'epoch' }` )} @@ -215,7 +215,7 @@ export const TransactionDetails = ({ ...p, ...('title' in p && ['voterType', 'voteTypes'].includes(p.title) && { - details: [t(`package.core.activityDetails.${p.title}.${p.details[0]}`)] + details: [t(`core.activityDetails.${p.title}.${p.details[0]}`)] }), ...('title' in p && p.title === 'anchorURL' && { @@ -257,12 +257,12 @@ export const TransactionDetails = ({
- {t('package.core.activityDetails.header')} + {t('core.activityDetails.header')}
-
{t('package.core.activityDetails.transactionID')}
+
{t('core.activityDetails.transactionID')}
{isSending ? ( - + ) : ( hash )} @@ -282,10 +282,10 @@ export const TransactionDetails = ({
-

{t('package.core.activityDetails.summary')}

+

{t('core.activityDetails.summary')}

{pools?.length > 0 && (
-
{t('package.core.activityDetails.pools')}
+
{t('core.activityDetails.pools')}
{pools?.map((pool) => (
@@ -341,7 +341,7 @@ export const TransactionDetails = ({
- {t(`package.core.activityDetails.${name.toLowerCase() === 'sent' ? 'to' : 'from'}`)} + {t(`core.activityDetails.${name.toLowerCase() === 'sent' ? 'to' : 'from'}`)}
{summary.addr.length > 1 && ( @@ -349,7 +349,7 @@ export const TransactionDetails = ({ data-testid="tx-to-detail-multiple-addresses" className={cn(styles.detail, styles.detailTitle)} > - {t('package.core.activityDetails.multipleAddresses')} + {t('core.activityDetails.multipleAddresses')}
)} {(summary.addr as string[]).map((addr) => { @@ -377,7 +377,7 @@ export const TransactionDetails = ({
))}
-
{t('package.core.activityDetails.status')}
+
{t('core.activityDetails.status')}
{status && (
{`${status.charAt(0).toUpperCase()}${status.slice( 1 @@ -385,7 +385,7 @@ export const TransactionDetails = ({ )}
-
{t('package.core.activityDetails.timestamp')}
+
{t('core.activityDetails.timestamp')}
{includedDate}  {includedTime} @@ -404,34 +404,34 @@ export const TransactionDetails = ({ {fee && fee !== '-' && ( )} - {deposit && renderDepositValueSection({ value: deposit, label: t('package.core.activityDetails.deposit') })} + {deposit && renderDepositValueSection({ value: deposit, label: t('core.activityDetails.deposit') })} {depositReclaim && renderDepositValueSection({ value: depositReclaim, - label: t('package.core.activityDetails.depositReclaim') + label: t('core.activityDetails.depositReclaim') })}
{votingProcedures?.length > 0 && ( testId="voting-procedures" - title={t('package.core.activityDetails.votingProcedures')} - subTitle={t('package.core.activityDetails.votingProcedure')} + title={t('core.activityDetails.votingProcedures')} + subTitle={t('core.activityDetails.votingProcedure')} lists={translatedVotingProcedures} translations={{ - voterType: t('package.core.activityDetails.votingProcedureTitles.voterType'), - drepId: t('package.core.activityDetails.votingProcedureTitles.drepId'), - voterCredential: t('package.core.activityDetails.votingProcedureTitles.voterCredential'), - voteTypes: t('package.core.activityDetails.votingProcedureTitles.voteTypes'), - anchorHash: t('package.core.activityDetails.votingProcedureTitles.anchorHash'), - anchorURL: t('package.core.activityDetails.votingProcedureTitles.anchorURL') + voterType: t('core.activityDetails.votingProcedureTitles.voterType'), + drepId: t('core.activityDetails.votingProcedureTitles.drepId'), + voterCredential: t('core.activityDetails.votingProcedureTitles.voterCredential'), + voteTypes: t('core.activityDetails.votingProcedureTitles.voteTypes'), + anchorHash: t('core.activityDetails.votingProcedureTitles.anchorHash'), + anchorURL: t('core.activityDetails.votingProcedureTitles.anchorURL') }} withSeparatorLine /> @@ -439,187 +439,153 @@ export const TransactionDetails = ({ {proposalProcedures?.length > 0 && ( testId="proposal-procedures" - title={t('package.core.activityDetails.proposalProcedures')} - subTitle={t('package.core.activityDetails.proposalProcedure')} + title={t('core.activityDetails.proposalProcedures')} + subTitle={t('core.activityDetails.proposalProcedure')} lists={translatedProposalProcedures} withSeparatorLine translations={{ - type: t('package.core.activityDetails.proposalProcedureTitles.type'), - deposit: t('package.core.activityDetails.proposalProcedureTitles.deposit'), - rewardAccount: t('package.core.activityDetails.proposalProcedureTitles.rewardAccount'), - anchorHash: t('package.core.activityDetails.proposalProcedureTitles.anchorHash'), - anchorURL: t('package.core.activityDetails.proposalProcedureTitles.anchorURL'), - governanceActionID: t('package.core.activityDetails.proposalProcedureTitles.governanceActionID'), - actionIndex: t('package.core.activityDetails.proposalProcedureTitles.actionIndex'), - newQuorumThreshold: t('package.core.activityDetails.proposalProcedureTitles.newQuorumThreshold'), - withdrawal: t('package.core.activityDetails.proposalProcedureTitles.withdrawal'), - withdrawalRewardAccount: t( - 'package.core.activityDetails.proposalProcedureTitles.withdrawalRewardAccount' - ), - withdrawalAmount: t('package.core.activityDetails.proposalProcedureTitles.withdrawalAmount'), - constitutionAnchorURL: t('package.core.activityDetails.proposalProcedureTitles.constitutionAnchorURL'), - constitutionScriptHash: t('package.core.activityDetails.proposalProcedureTitles.constitutionScriptHash'), - coldCredentialHash: t('package.core.activityDetails.proposalProcedureTitles.coldCredentialHash'), - epoch: t('package.core.activityDetails.proposalProcedureTitles.epoch'), - membersToBeAdded: t('package.core.activityDetails.proposalProcedureTitles.membersToBeAdded'), - hash: t('package.core.activityDetails.proposalProcedureTitles.hash'), - membersToBeRemoved: t('package.core.activityDetails.proposalProcedureTitles.membersToBeRemoved'), - protocolVersionMajor: t('package.core.activityDetails.proposalProcedureTitles.protocolVersionMajor'), - protocolVersionMinor: t('package.core.activityDetails.proposalProcedureTitles.protocolVersionMinor'), - protocolVersionPatch: t('package.core.activityDetails.proposalProcedureTitles.protocolVersionPatch'), - maxTxExUnits: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxExUnits' - ), + type: t('core.activityDetails.proposalProcedureTitles.type'), + deposit: t('core.activityDetails.proposalProcedureTitles.deposit'), + rewardAccount: t('core.activityDetails.proposalProcedureTitles.rewardAccount'), + anchorHash: t('core.activityDetails.proposalProcedureTitles.anchorHash'), + anchorURL: t('core.activityDetails.proposalProcedureTitles.anchorURL'), + governanceActionID: t('core.activityDetails.proposalProcedureTitles.governanceActionID'), + actionIndex: t('core.activityDetails.proposalProcedureTitles.actionIndex'), + newQuorumThreshold: t('core.activityDetails.proposalProcedureTitles.newQuorumThreshold'), + withdrawal: t('core.activityDetails.proposalProcedureTitles.withdrawal'), + withdrawalRewardAccount: t('core.activityDetails.proposalProcedureTitles.withdrawalRewardAccount'), + withdrawalAmount: t('core.activityDetails.proposalProcedureTitles.withdrawalAmount'), + constitutionAnchorURL: t('core.activityDetails.proposalProcedureTitles.constitutionAnchorURL'), + constitutionScriptHash: t('core.activityDetails.proposalProcedureTitles.constitutionScriptHash'), + coldCredentialHash: t('core.activityDetails.proposalProcedureTitles.coldCredentialHash'), + epoch: t('core.activityDetails.proposalProcedureTitles.epoch'), + membersToBeAdded: t('core.activityDetails.proposalProcedureTitles.membersToBeAdded'), + hash: t('core.activityDetails.proposalProcedureTitles.hash'), + membersToBeRemoved: t('core.activityDetails.proposalProcedureTitles.membersToBeRemoved'), + protocolVersionMajor: t('core.activityDetails.proposalProcedureTitles.protocolVersionMajor'), + protocolVersionMinor: t('core.activityDetails.proposalProcedureTitles.protocolVersionMinor'), + protocolVersionPatch: t('core.activityDetails.proposalProcedureTitles.protocolVersionPatch'), + maxTxExUnits: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxExUnits'), maxBlockExUnits: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBlockExUnits' - ), - networkGroup: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.title'), - economicGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.title' - ), - technicalGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.title' - ), - costModels: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.costModels' - ), - PlutusV1: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.PlutusV1' - ), - PlutusV2: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.PlutusV2' - ), - governanceGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.title' - ), + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBlockExUnits' + ), + networkGroup: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.title'), + economicGroup: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.title'), + technicalGroup: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.title'), + costModels: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.costModels'), + PlutusV1: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.PlutusV1'), + PlutusV2: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.PlutusV2'), + governanceGroup: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.title'), dRepVotingThresholds: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.title' - ), - memory: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.memory'), - step: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.step'), - maxBBSize: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBBSize' - ), - maxTxSize: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxSize' - ), - maxBHSize: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBHSize' - ), - maxValSize: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxValSize' - ), + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.title' + ), + memory: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.memory'), + step: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.step'), + maxBBSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBBSize'), + maxTxSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxTxSize'), + maxBHSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxBHSize'), + maxValSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxValSize'), maxCollateralInputs: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxCollateralInputs' - ), - minFeeA: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeA'), - minFeeB: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeB'), - keyDeposit: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.keyDeposit' - ), - poolDeposit: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.poolDeposit' - ), - rho: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.rho'), - tau: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tau'), - minPoolCost: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minPoolCost' - ), + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.networkGroup.maxCollateralInputs' + ), + minFeeA: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeA'), + minFeeB: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minFeeB'), + keyDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.keyDeposit'), + poolDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.poolDeposit'), + rho: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.rho'), + tau: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.tau'), + minPoolCost: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.minPoolCost'), coinsPerUTxOByte: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.coinsPerUTxOByte' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.coinsPerUTxOByte' ), - a0: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.a0'), - eMax: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.eMax'), - nOpt: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.nOpt'), + a0: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.a0'), + eMax: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.eMax'), + nOpt: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.nOpt'), collateralPercentage: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.collateralPercentage' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.technicalGroup.collateralPercentage' ), - prices: t('package.core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.prices'), + prices: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.economicGroup.prices'), govActionLifetime: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.govActionLifetime' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.govActionLifetime' ), govActionDeposit: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.govActionDeposit' - ), - drepDeposit: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.drepDeposit' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.govActionDeposit' ), + drepDeposit: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.drepDeposit'), drepActivity: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.drepActivity' - ), - ccMinSize: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.ccMinSize' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.drepActivity' ), + ccMinSize: t('core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.ccMinSize'), ccMaxTermLength: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.ccMaxTermLength' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.ccMaxTermLength' ), motionNoConfidence: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.motionNoConfidence' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.motionNoConfidence' ), committeeNormal: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.committeeNormal' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.committeeNormal' ), committeeNoConfidence: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.committeeNoConfidence' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.committeeNoConfidence' ), updateConstitution: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.updateConstitution' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.updateConstitution' ), hardForkInitiation: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.hardForkInitiation' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.hardForkInitiation' ), ppNetworkGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppNetworkGroup' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppNetworkGroup' ), ppEconomicGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppEconomicGroup' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppEconomicGroup' ), ppTechnicalGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppTechnicalGroup' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppTechnicalGroup' ), ppGovernanceGroup: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppGovernanceGroup' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.ppGovernanceGroup' ), treasuryWithdrawal: t( - 'package.core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.treasuryWithdrawal' + 'core.ProposalProcedure.governanceAction.protocolParamUpdate.governanceGroup.dRepVotingThresholds.treasuryWithdrawal' ) }} /> )} {certificates?.length > 0 && ( - title={t('package.core.activityDetails.certificates')} - subTitle={t('package.core.activityDetails.certificate')} + title={t('core.activityDetails.certificates')} + subTitle={t('core.activityDetails.certificate')} testId="certificates" lists={translatedCertificates} withSeparatorLine translations={{ - certificate: t('package.core.activityDetails.certificateTitles.certificate'), - certificateType: t('package.core.activityDetails.certificateTitles.certificateType'), - coldCredential: t('package.core.activityDetails.certificateTitles.coldCredential'), - hotCredential: t('package.core.activityDetails.certificateTitles.hotCredential'), - stakeKey: t('package.core.activityDetails.certificateTitles.stakeKey'), - drepId: t('package.core.activityDetails.certificateTitles.drepId'), - anchorURL: t('package.core.activityDetails.certificateTitles.anchorURL'), - anchorHash: t('package.core.activityDetails.certificateTitles.anchorHash'), - poolId: t('package.core.activityDetails.certificateTitles.poolId'), - drep: t('package.core.activityDetails.certificateTitles.drep'), - depositPaid: t('package.core.activityDetails.certificateTitles.depositPaid'), - depositPaidInfo: t('package.core.activityDetails.certificateTitles.depositPaidInfo'), - depositReturned: t('package.core.activityDetails.certificateTitles.depositReturned'), - depositReturnedInfo: t('package.core.activityDetails.certificateTitles.depositReturnedInfo') + certificate: t('core.activityDetails.certificateTitles.certificate'), + certificateType: t('core.activityDetails.certificateTitles.certificateType'), + coldCredential: t('core.activityDetails.certificateTitles.coldCredential'), + hotCredential: t('core.activityDetails.certificateTitles.hotCredential'), + stakeKey: t('core.activityDetails.certificateTitles.stakeKey'), + drepId: t('core.activityDetails.certificateTitles.drepId'), + anchorURL: t('core.activityDetails.certificateTitles.anchorURL'), + anchorHash: t('core.activityDetails.certificateTitles.anchorHash'), + poolId: t('core.activityDetails.certificateTitles.poolId'), + drep: t('core.activityDetails.certificateTitles.drep'), + depositPaid: t('core.activityDetails.certificateTitles.depositPaid'), + depositPaidInfo: t('core.activityDetails.certificateTitles.depositPaidInfo'), + depositReturned: t('core.activityDetails.certificateTitles.depositReturned'), + depositReturnedInfo: t('core.activityDetails.certificateTitles.depositReturnedInfo') }} /> )} {addrInputs?.length > 0 && ( 0 && ( 0 && (
-
{t('package.core.activityDetails.metadata')}
+
{t('core.activityDetails.metadata')}
{metadata?.map((item) => (
diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx index 13eb8b50f..670347915 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionFee.tsx @@ -30,7 +30,7 @@ export const TransactionFee = ({ - {t('package.core.assetInput.maxButton')} + {t('core.assetInput.maxButton')}
)} diff --git a/packages/core/src/ui/components/AssetSelector/AssetSelectorOverlay.tsx b/packages/core/src/ui/components/AssetSelector/AssetSelectorOverlay.tsx index 3f33a29d1..9093712c5 100644 --- a/packages/core/src/ui/components/AssetSelector/AssetSelectorOverlay.tsx +++ b/packages/core/src/ui/components/AssetSelector/AssetSelectorOverlay.tsx @@ -54,8 +54,8 @@ const getTokensContent = ( - {t('package.core.assetSelectorOverlay.youDonthaveAnyTokens')} -
{t('package.core.assetSelectorOverlay.justAddSomeDigitalAssetsToGetStarted')} + {t('core.assetSelectorOverlay.youDonthaveAnyTokens')} +
{t('core.assetSelectorOverlay.justAddSomeDigitalAssetsToGetStarted')} } icon="sad-face" @@ -64,9 +64,9 @@ const getTokensContent = ( switch (true) { case (!params.tokens || params.tokens?.length === 0) && !params.hasUsedAllTokens: - return ; + return ; case params.hasUsedAllTokens: - return ; + return ; default: return params.tokens.map(({ id, ...item }, idx) => ( - {t('package.core.assetSelectorOverlay.noNFTs')} -
{t('package.core.assetSelectorOverlay.addFundsToStartYourWeb3Journey')} + {t('core.assetSelectorOverlay.noNFTs')} +
{t('core.assetSelectorOverlay.addFundsToStartYourWeb3Journey')} } icon="sad-face" @@ -112,9 +112,9 @@ const getNftsContent = ( switch (true) { case (!nftList || nftList.length === 0) && !params.hasUsedAllNFTs: - return ; + return ; case params.hasUsedAllNFTs: - return ; + return ; default: return ; } diff --git a/packages/core/src/ui/components/AssetTable/AssetTable.tsx b/packages/core/src/ui/components/AssetTable/AssetTable.tsx index bf02b73be..1706d9042 100644 --- a/packages/core/src/ui/components/AssetTable/AssetTable.tsx +++ b/packages/core/src/ui/components/AssetTable/AssetTable.tsx @@ -122,17 +122,17 @@ export const AssetTable = ({ const columns: ColumnsType = [ { - title: popupView ? '' : t('package.core.assetTable.columns.token'), + title: popupView ? '' : t('core.assetTable.columns.token'), dataIndex: 'token', key: 'token', width: '45%' }, - { title: popupView ? '' : t('package.core.assetTable.columns.balance'), dataIndex: 'balance', key: 'balance' } + { title: popupView ? '' : t('core.assetTable.columns.balance'), dataIndex: 'balance', key: 'balance' } ]; if (!popupView) columns.splice(1, 0, { - title: t('package.core.assetTable.columns.price'), + title: t('core.assetTable.columns.price'), dataIndex: 'price', key: 'price' }); diff --git a/packages/core/src/ui/components/AuthorizeDapp/AuthorizeDapp.tsx b/packages/core/src/ui/components/AuthorizeDapp/AuthorizeDapp.tsx index 6acdef077..b0b0292e9 100644 --- a/packages/core/src/ui/components/AuthorizeDapp/AuthorizeDapp.tsx +++ b/packages/core/src/ui/components/AuthorizeDapp/AuthorizeDapp.tsx @@ -16,14 +16,14 @@ export const AuthorizeDapp = ({ dappInfo, warningBanner }: AuthorizeDappProps): {warningBanner}
- {t('package.core.authorizeDapp.title')}: + {t('core.authorizeDapp.title')}:
    -
  • {t('package.core.authorizeDapp.seeNetwork')}
  • -
  • {t('package.core.authorizeDapp.seeWalletUtxo')}
  • -
  • {t('package.core.authorizeDapp.seeWalletBalance')}
  • -
  • {t('package.core.authorizeDapp.seeWalletAddresses')}
  • +
  • {t('core.authorizeDapp.seeNetwork')}
  • +
  • {t('core.authorizeDapp.seeWalletUtxo')}
  • +
  • {t('core.authorizeDapp.seeWalletBalance')}
  • +
  • {t('core.authorizeDapp.seeWalletAddresses')}
diff --git a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx index e11b7c79b..93c0a18a2 100644 --- a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx +++ b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx @@ -105,17 +105,17 @@ export const DappAddressSections = ({ }: DappAddressSectionProps): React.ReactElement => { const { t } = useTranslate(); - const itemsCountCopy = t('package.core.dappTransaction.items'); + const itemsCountCopy = t('core.dappTransaction.items'); return ( <> - +
{[...groupedFromAddresses.entries()].map(([address, addressData]) => ( <>
- {t('package.core.dappTransaction.address')} + {t('core.dappTransaction.address')} @@ -127,7 +127,7 @@ export const DappAddressSections = ({ <>
- {t('package.core.dappTransaction.tokens')} + {t('core.dappTransaction.tokens')} -{getTokenQuantity(addressData.tokens, addressData.coins)} {itemsCountCopy} @@ -148,7 +148,7 @@ export const DappAddressSections = ({ <> <div className={styles.tokenCount}> <Title level={5} data-testid="dapp-transaction-nfts-title"> - {t('package.core.dappTransaction.nfts')} + {t('core.dappTransaction.nfts')} -{addressData.nfts.length} {itemsCountCopy} @@ -162,13 +162,13 @@ export const DappAddressSections = ({ </div> </SummaryExpander> - <SummaryExpander title={t('package.core.dappTransaction.toAddress')} disabled={!isToAddressesEnabled}> + <SummaryExpander title={t('core.dappTransaction.toAddress')} disabled={!isToAddressesEnabled}> <div> {[...groupedToAddresses.entries()].map(([address, addressData]) => ( <> <div key={address} className={styles.address}> <Text className={styles.label} data-testid="dapp-transaction-to-address-title"> - {t('package.core.dappTransaction.address')} + {t('core.dappTransaction.address')} </Text> <Text className={styles.value} data-testid="dapp-transaction-to-address"> <Tooltip label={address}> @@ -180,7 +180,7 @@ export const DappAddressSections = ({ <> <div className={styles.tokenCount}> <Title level={5} className={styles.label} data-testid="dapp-transaction-tokens-title"> - {t('package.core.dappTransaction.tokens')} + {t('core.dappTransaction.tokens')} {getTokenQuantity(addressData.tokens, addressData.coins)} {itemsCountCopy} @@ -201,7 +201,7 @@ export const DappAddressSections = ({ <> <div className={styles.tokenCount}> <Title level={5} className={styles.label} data-testid="dapp-transaction-nfts-title"> - {t('package.core.dappTransaction.nfts')} + {t('core.dappTransaction.nfts')} {addressData.nfts.length} {itemsCountCopy} diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx index 86e5673d8..b4621b7b8 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx @@ -126,7 +126,7 @@ export const DappTransaction = ({ <div data-testid="dapp-transaction-container" className={styles.details}> <DappTransactionHeader transactionType={getTxType(coins)} name={dappInfo.name} /> <DappTransactionSummary - title={t('package.core.dappTransaction.transactionSummary')} + title={t('core.dappTransaction.transactionSummary')} cardanoSymbol={coinSymbol} transactionAmount={Wallet.util.lovelacesToAdaString(coins.toString())} /> @@ -166,7 +166,7 @@ export const DappTransaction = ({ <TransactionFee fee={Wallet.util.lovelacesToAdaString(returnedDeposit.toString())} testId="returned-deposit" - label={t('package.core.dappTransaction.returnedDeposit')} + label={t('core.dappTransaction.returnedDeposit')} coinSymbol={coinSymbol} className={styles.depositContainer} displayFiat={false} @@ -180,7 +180,7 @@ export const DappTransaction = ({ <TransactionFee testId="deposit" fee={Wallet.util.lovelacesToAdaString(deposit.toString())} - label={t('package.core.dappTransaction.deposit')} + label={t('core.dappTransaction.deposit')} coinSymbol={coinSymbol} className={styles.depositContainer} displayFiat={false} diff --git a/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx b/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx index 0e38fc19a..49e77b386 100644 --- a/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx +++ b/packages/core/src/ui/components/DappTransactionHeader/DappTransactionHeader.tsx @@ -30,8 +30,8 @@ export const DappTransactionHeader = ({ transactionType, name }: DappTransaction return ( <div data-testid="transaction-type-container"> - <TransactionType label={t('package.core.dappTransaction.transaction')} transactionType={transactionType} /> - <SummaryExpander title={t('package.core.dappTransaction.origin')}> + <TransactionType label={t('core.dappTransaction.transaction')} transactionType={transactionType} /> + <SummaryExpander title={t('core.dappTransaction.origin')}> <Card.Outlined className={styles.dappInfoContainer}> <Text className={styles.dappInfo}> <span data-testid="dapp-transaction-origin">{name}</span> diff --git a/packages/core/src/ui/components/WalletAddresses/WalletAddressItem.tsx b/packages/core/src/ui/components/WalletAddresses/WalletAddressItem.tsx index 897962812..1d0d19fcb 100644 --- a/packages/core/src/ui/components/WalletAddresses/WalletAddressItem.tsx +++ b/packages/core/src/ui/components/WalletAddresses/WalletAddressItem.tsx @@ -68,7 +68,7 @@ export const WalletAddressItem = ({ </div> <div className={cn(styles.listItemBlock, styles.addressBox)}> {isAddressWarningVisible && ( - <Tooltip title={t('package.core.addressBook.addressHandleTooltip')}> + <Tooltip title={t('core.addressBook.addressHandleTooltip')}> <MissingIcon data-testid="address-list-item-warning" className={cn(styles.listItemWarning)} /> </Tooltip> )} diff --git a/packages/core/src/ui/components/WalletSetup/AnalyticsConfirmationBanner.tsx b/packages/core/src/ui/components/WalletSetup/AnalyticsConfirmationBanner.tsx index dbcbf950e..deb6c2bee 100644 --- a/packages/core/src/ui/components/WalletSetup/AnalyticsConfirmationBanner.tsx +++ b/packages/core/src/ui/components/WalletSetup/AnalyticsConfirmationBanner.tsx @@ -38,10 +38,10 @@ export const AnalyticsConfirmationBanner = ({ <div className={styles.confirmationBannerMessage}>{message}</div> <div className={styles.buttons}> <Button onClick={handleConfirm} className={styles.secondaryButton} data-testid="analytics-accept-button"> - {t('package.core.confirmationBanner.agree')} + {t('core.confirmationBanner.agree')} </Button> <Button onClick={handleReject} data-testid="analytics-reject-button"> - {t('package.core.confirmationBanner.reject')} + {t('core.confirmationBanner.reject')} </Button> </div> </div> diff --git a/packages/core/src/ui/components/WalletSetup/WalletSetupNamePasswordStep/utils.ts b/packages/core/src/ui/components/WalletSetup/WalletSetupNamePasswordStep/utils.ts index 1cf8afa23..92322594b 100644 --- a/packages/core/src/ui/components/WalletSetup/WalletSetupNamePasswordStep/utils.ts +++ b/packages/core/src/ui/components/WalletSetup/WalletSetupNamePasswordStep/utils.ts @@ -6,9 +6,9 @@ export const MINIMUM_PASSWORD_LEVEL_REQUIRED = 3; export const WALLET_NAME_INPUT_MAX_LENGTH = 30; export const passwordStrengthFeedbackMap: Record<number, string> = { - 0: 'package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback', - 1: 'package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback', - 2: 'package.core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback' + 0: 'core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback', + 1: 'core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback', + 2: 'core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback' }; export const nameShouldHaveRightLengthRegex = /^.{1,20}$/; diff --git a/packages/core/src/ui/components/WalletSetup/WalletSetupStepLayout.tsx b/packages/core/src/ui/components/WalletSetup/WalletSetupStepLayout.tsx index ff8374845..988a37280 100644 --- a/packages/core/src/ui/components/WalletSetup/WalletSetupStepLayout.tsx +++ b/packages/core/src/ui/components/WalletSetup/WalletSetupStepLayout.tsx @@ -48,17 +48,17 @@ const removeLegalAndAnalyticsStep = ( const getTimelineSteps = (currentStep: WalletTimelineSteps, isHardwareWallet: boolean, flow: WalletSetupFlow) => { const inMemoryWalletSteps = [ - { key: WalletTimelineSteps.LEGAL_AND_ANALYTICS, name: i18n.t('package.core.walletSetupStep.legalAndAnalytics') }, - { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('package.core.walletSetupStep.walletSetup') }, - { key: WalletTimelineSteps.RECOVERY_PHRASE, name: i18n.t('package.core.walletSetupStep.recoveryPhrase') }, - { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('package.core.walletSetupStep.allDone') } + { key: WalletTimelineSteps.LEGAL_AND_ANALYTICS, name: i18n.t('core.walletSetupStep.legalAndAnalytics') }, + { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('core.walletSetupStep.walletSetup') }, + { key: WalletTimelineSteps.RECOVERY_PHRASE, name: i18n.t('core.walletSetupStep.recoveryPhrase') }, + { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('core.walletSetupStep.allDone') } ]; const hardwareWalletSteps = [ - { key: WalletTimelineSteps.LEGAL_AND_ANALYTICS, name: i18n.t('package.core.walletSetupStep.legalAndAnalytics') }, - { key: WalletTimelineSteps.CONNECT_WALLET, name: i18n.t('package.core.walletSetupStep.connectWallet') }, - { key: WalletTimelineSteps.NAME_WALLET, name: i18n.t('package.core.walletSetupStep.nameWallet') }, - { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('package.core.walletSetupStep.allDone') } + { key: WalletTimelineSteps.LEGAL_AND_ANALYTICS, name: i18n.t('core.walletSetupStep.legalAndAnalytics') }, + { key: WalletTimelineSteps.CONNECT_WALLET, name: i18n.t('core.walletSetupStep.connectWallet') }, + { key: WalletTimelineSteps.NAME_WALLET, name: i18n.t('core.walletSetupStep.nameWallet') }, + { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('core.walletSetupStep.allDone') } ]; const walletSteps = isHardwareWallet ? hardwareWalletSteps : inMemoryWalletSteps; @@ -100,9 +100,9 @@ export const WalletSetupStepLayout = ({ const flow = useWalletSetupFlow(); const defaultLabel = { - next: t('package.core.walletSetupStep.next'), - back: t('package.core.walletSetupStep.back'), - skip: t('package.core.walletSetupStep.skip') + next: t('core.walletSetupStep.next'), + back: t('core.walletSetupStep.back'), + skip: t('core.walletSetupStep.skip') }; const timelineSteps = getTimelineSteps(currentTimelineStep, isHardwareWallet, flow); diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx index 1d51c14d9..333732cc2 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx @@ -38,15 +38,15 @@ const removeLegalAndAnalyticsStep = ( const getTimelineSteps = (currentStep: WalletTimelineSteps, isHardwareWallet: boolean, flow: WalletSetupFlow) => { const inMemoryWalletSteps = [ - { key: WalletTimelineSteps.RECOVERY_PHRASE, name: i18n.t('package.core.walletSetupStep.recoveryPhrase') }, - { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('package.core.walletSetupStep.walletSetup') }, - { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('package.core.walletSetupStep.enterWallet') } + { key: WalletTimelineSteps.RECOVERY_PHRASE, name: i18n.t('core.walletSetupStep.recoveryPhrase') }, + { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('core.walletSetupStep.walletSetup') }, + { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('core.walletSetupStep.enterWallet') } ]; const hardwareWalletSteps = [ - { key: WalletTimelineSteps.CONNECT_WALLET, name: i18n.t('package.core.walletSetupStep.connectWallet') }, - { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('package.core.walletSetupStep.walletSetup') }, - { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('package.core.walletSetupStep.enterWallet') } + { key: WalletTimelineSteps.CONNECT_WALLET, name: i18n.t('core.walletSetupStep.connectWallet') }, + { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('core.walletSetupStep.walletSetup') }, + { key: WalletTimelineSteps.ALL_DONE, name: i18n.t('core.walletSetupStep.enterWallet') } ]; const walletSteps = isHardwareWallet ? hardwareWalletSteps : inMemoryWalletSteps; @@ -86,9 +86,9 @@ export const WalletSetupStepLayoutRevamp = ({ const flow = useWalletSetupFlow(); const defaultLabel = { - next: t('package.core.walletSetupStep.next'), - back: t('package.core.walletSetupStep.back'), - skip: t('package.core.walletSetupStep.skip') + next: t('core.walletSetupStep.next'), + back: t('core.walletSetupStep.back'), + skip: t('core.walletSetupStep.skip') }; const timelineSteps = getTimelineSteps(currentTimelineStep, isHardwareWallet, flow); diff --git a/packages/core/src/ui/lib/translations/en.json b/packages/core/src/ui/lib/translations/en.json index 54da6476e..d0ff55cb1 100644 --- a/packages/core/src/ui/lib/translations/en.json +++ b/packages/core/src/ui/lib/translations/en.json @@ -1,5 +1,5 @@ { - "package.core": { + "core": { "addressBook": { "addressHandleTooltip": "The linked address to this handle has changed, click to update or delete." }, diff --git a/packages/e2e-tests/src/assert/addressBook/AddressBookPageAssert.ts b/packages/e2e-tests/src/assert/addressBook/AddressBookPageAssert.ts index 3cee74fd8..a4608f157 100644 --- a/packages/e2e-tests/src/assert/addressBook/AddressBookPageAssert.ts +++ b/packages/e2e-tests/src/assert/addressBook/AddressBookPageAssert.ts @@ -70,7 +70,7 @@ class AddressBookPageAssert { assertSeeHandleWarningTooltip = async () => { await AddressBookPage.warningTooltip.waitForDisplayed(); expect(await AddressBookPage.warningTooltip.getText()).to.equal( - await t('package.core.addressBook.addressHandleTooltip', 'core') + await t('core.addressBook.addressHandleTooltip', 'core') ); }; diff --git a/packages/e2e-tests/src/assert/coinConfigureAssert.ts b/packages/e2e-tests/src/assert/coinConfigureAssert.ts index 6bf63b52a..0eddf45b2 100644 --- a/packages/e2e-tests/src/assert/coinConfigureAssert.ts +++ b/packages/e2e-tests/src/assert/coinConfigureAssert.ts @@ -27,9 +27,7 @@ class CoinConfigureAssert { const coinConfigure = new CoinConfigure(index); await coinConfigure.assetMaxButton.waitForDisplayed({ reverse: !shouldSee }); if (shouldSee) { - expect(await coinConfigure.assetMaxButton.getText()).to.equal( - await t('package.core.assetInput.maxButton', 'core') - ); + expect(await coinConfigure.assetMaxButton.getText()).to.equal(await t('core.assetInput.maxButton', 'core')); } } } diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index 9fe8c158c..2eff34bdf 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -112,17 +112,17 @@ class DAppConnectorAssert { async assertSeeAuthorizePagePermissions() { await AuthorizeDAppPage.permissionsTitle.waitForDisplayed(); expect(await AuthorizeDAppPage.permissionsTitle.getText()).to.equal( - `${await t('package.core.authorizeDapp.title', 'core')}:` + `${await t('core.authorizeDapp.title', 'core')}:` ); await AuthorizeDAppPage.permissionsList.waitForDisplayed(); const currentTexts = await AuthorizeDAppPage.permissionsListItems.map(async (option) => await option.getText()); const expectedTexts = [ - await t('package.core.authorizeDapp.seeNetwork', 'core'), - await t('package.core.authorizeDapp.seeWalletUtxo', 'core'), - await t('package.core.authorizeDapp.seeWalletBalance', 'core'), - await t('package.core.authorizeDapp.seeWalletAddresses', 'core') + await t('core.authorizeDapp.seeNetwork', 'core'), + await t('core.authorizeDapp.seeWalletUtxo', 'core'), + await t('core.authorizeDapp.seeWalletBalance', 'core'), + await t('core.authorizeDapp.seeWalletAddresses', 'core') ]; expect(currentTexts).to.have.all.members(expectedTexts); @@ -263,14 +263,14 @@ class DAppConnectorAssert { await this.assertSeeHeader(); await ConfirmTransactionPage.transactionTypeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionTypeTitle.getText()).to.equal( - await t('package.core.dappTransaction.transaction') + await t('core.dappTransaction.transaction') ); await ConfirmTransactionPage.transactionType.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionType.getText()).to.equal(expectedTransactionData.typeOfTransaction); await ConfirmTransactionPage.transactionFeeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionFeeTitle.getText()).to.equal( - await t('package.core.activityDetails.transactionFee', 'core') + await t('core.activityDetails.transactionFee', 'core') ); await ConfirmTransactionPage.transactionFeeValueAda.waitForDisplayed(); const fee = Number((await ConfirmTransactionPage.transactionFeeValueAda.getText()).split(' ')[0]); diff --git a/packages/e2e-tests/src/assert/nftCreateFolderAssert.ts b/packages/e2e-tests/src/assert/nftCreateFolderAssert.ts index 53a800f58..5c8d506da 100644 --- a/packages/e2e-tests/src/assert/nftCreateFolderAssert.ts +++ b/packages/e2e-tests/src/assert/nftCreateFolderAssert.ts @@ -181,7 +181,7 @@ class NftCreateFolderAssert { await NftSelectNftsPage.sadFaceIcon.waitForDisplayed(); await NftSelectNftsPage.noResultsMessage.waitForDisplayed(); expect(await NftSelectNftsPage.noResultsMessage.getText()).to.equal( - await t('package.core.assetSelectorOverlay.noMatchingResult') + await t('core.assetSelectorOverlay.noMatchingResult') ); } diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingAnalyticsBannerAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingAnalyticsBannerAssert.ts index 211e72850..b99612d83 100644 --- a/packages/e2e-tests/src/assert/onboarding/onboardingAnalyticsBannerAssert.ts +++ b/packages/e2e-tests/src/assert/onboarding/onboardingAnalyticsBannerAssert.ts @@ -10,13 +10,9 @@ class OnboardingAnalyticsBannerAssert { async assertBannerIsDisplayedCorrectly() { await AnalyticsBanner.container.waitForDisplayed(); await AnalyticsBanner.agreeButton.waitForDisplayed(); - expect(await AnalyticsBanner.agreeButton.getText()).to.equal( - await t('package.core.confirmationBanner.agree', 'core') - ); + expect(await AnalyticsBanner.agreeButton.getText()).to.equal(await t('core.confirmationBanner.agree', 'core')); await AnalyticsBanner.rejectButton.waitForDisplayed(); - expect(await AnalyticsBanner.rejectButton.getText()).to.equal( - await t('package.core.confirmationBanner.reject', 'core') - ); + expect(await AnalyticsBanner.rejectButton.getText()).to.equal(await t('core.confirmationBanner.reject', 'core')); await AnalyticsBanner.message.waitForDisplayed(); expect(await AnalyticsBanner.message.getText()).to.equal(await t('analyticsConfirmationBanner.message')); await AnalyticsBanner.learnMore.waitForDisplayed(); diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingWalletSetupPageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingWalletSetupPageAssert.ts index 5fae1a041..9c3f19eb1 100644 --- a/packages/e2e-tests/src/assert/onboarding/onboardingWalletSetupPageAssert.ts +++ b/packages/e2e-tests/src/assert/onboarding/onboardingWalletSetupPageAssert.ts @@ -18,7 +18,7 @@ class OnboardingWalletSetupPageAssert extends OnboardingCommonAssert { async assertSeeEnterWalletButton() { await walletSetupPage.enterWalletButton.waitForDisplayed(); - await this.assertNextButtonTextEquals(await t('package.core.walletNameAndPasswordSetupStep.enterWallet')); + await this.assertNextButtonTextEquals(await t('core.walletNameAndPasswordSetupStep.enterWallet')); } async assertSeePasswordRecommendation(expectedMessage: string, shouldSee: boolean) { @@ -51,8 +51,8 @@ class OnboardingWalletSetupPageAssert extends OnboardingCommonAssert { } async assertSeeWalletSetupPage() { - await this.assertSeeStepTitle(await t('package.core.walletNameAndPasswordSetupStep.title')); - await this.assertSeeStepSubtitle(await t('package.core.walletNameAndPasswordSetupStep.description')); + await this.assertSeeStepTitle(await t('core.walletNameAndPasswordSetupStep.title')); + await this.assertSeeStepSubtitle(await t('core.walletNameAndPasswordSetupStep.description')); await this.assertSeeWalletNameInput(); await this.assertSeePasswordInput(); await this.assertSeeBackButton(); diff --git a/packages/e2e-tests/src/assert/transaction/transactionAssetSelectionAssert.ts b/packages/e2e-tests/src/assert/transaction/transactionAssetSelectionAssert.ts index a655b223e..e0e6159f5 100644 --- a/packages/e2e-tests/src/assert/transaction/transactionAssetSelectionAssert.ts +++ b/packages/e2e-tests/src/assert/transaction/transactionAssetSelectionAssert.ts @@ -45,7 +45,7 @@ class TransactionAssetSelectionAssert { await TokenSelectionPage.neutralFaceIcon.waitForDisplayed({ reverse: !shouldSee }); if (shouldSee) { expect(await TokenSelectionPage.emptyStateMessage.getText()).to.equal( - await t('package.core.assetSelectorOverlay.usedAllAssets') + await t('core.assetSelectorOverlay.usedAllAssets') ); } } @@ -55,7 +55,7 @@ class TransactionAssetSelectionAssert { await TokenSelectionPage.sadFaceIcon.waitForDisplayed({ reverse: !shouldSee }); if (shouldSee) { expect(await TokenSelectionPage.emptyStateMessage.getText()).to.equal( - await t('package.core.assetSelectorOverlay.noMatchingResult') + await t('core.assetSelectorOverlay.noMatchingResult') ); } } @@ -64,11 +64,11 @@ class TransactionAssetSelectionAssert { await TokenSelectionPage.emptyStateMessage.waitForDisplayed({ reverse: !shouldSee }); await TokenSelectionPage.sadFaceIcon.waitForDisplayed({ reverse: !shouldSee }); if (shouldSee) { - const messageForTokens = `${await t('package.core.assetSelectorOverlay.youDonthaveAnyTokens')}\n${await t( - 'package.core.assetSelectorOverlay.justAddSomeDigitalAssetsToGetStarted' + const messageForTokens = `${await t('core.assetSelectorOverlay.youDonthaveAnyTokens')}\n${await t( + 'core.assetSelectorOverlay.justAddSomeDigitalAssetsToGetStarted' )}`; - const messageForNFTs = `${await t('package.core.assetSelectorOverlay.noNFTs')}\n${await t( - 'package.core.assetSelectorOverlay.addFundsToStartYourWeb3Journey.' + const messageForNFTs = `${await t('core.assetSelectorOverlay.noNFTs')}\n${await t( + 'core.assetSelectorOverlay.addFundsToStartYourWeb3Journey.' )}`; const expectedMessage = assetType === 'tokens' ? messageForTokens : messageForNFTs; expect(await TokenSelectionPage.emptyStateMessage.getText()).to.equal(expectedMessage); diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index 64d8c4f84..26fada99a 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -39,9 +39,7 @@ class TransactionsDetailsAssert { await TransactionDetailsPage.transactionDetails.waitForDisplayed({ reverse: !shouldBeDisplayed }); await TransactionDetailsPage.transactionHeader.waitForDisplayed({ reverse: !shouldBeDisplayed }); if (shouldBeDisplayed) { - expect(await TransactionDetailsPage.transactionHeader.getText()).to.equal( - await t('package.core.activityDetails.header') - ); + expect(await TransactionDetailsPage.transactionHeader.getText()).to.equal(await t('core.activityDetails.header')); } } diff --git a/packages/e2e-tests/src/features/ForgotPassword.feature b/packages/e2e-tests/src/features/ForgotPassword.feature index 970deaaa3..820b296c6 100644 --- a/packages/e2e-tests/src/features/ForgotPassword.feature +++ b/packages/e2e-tests/src/features/ForgotPassword.feature @@ -48,9 +48,9 @@ Feature: Forgot password Then Password recommendation: "<passw_err>", complexity bar level: "<complex_bar_lvl>" and password confirmation error: "<passw_conf_err>" are displayed Examples: | password | password_conf | passw_err | complex_bar_lvl | passw_conf_err | - | a | | package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | - | P@ss | | package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | - | N_8J@bne | | package.core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | + | a | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | + | P@ss | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | + | N_8J@bne | | core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | | N_8J@bne87 | | empty | 3 | empty | | N_8J@bne87A | N_8J@bne87 | empty | 4 | core.walletSetupRegisterStep.noMatchPassword | diff --git a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature index c8011b42b..137fac069 100755 --- a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature @@ -38,9 +38,9 @@ Feature: Onboarding - Create wallet Then Password recommendation: "<passw_err>", complexity bar level: "<complex_bar_lvl>" and password confirmation error: "<passw_conf_err>" are displayed Examples: | password | password_conf | passw_err | complex_bar_lvl | passw_conf_err | - | a | | package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | - | P@ss | | package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | - | N_8J@bne | | package.core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | + | a | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | + | P@ss | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | + | N_8J@bne | | core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | | N_8J@bne87 | | empty | 3 | empty | | N_8J@bne87A | N_8J@bne87 | empty | 4 | core.walletSetupRegisterStep.noMatchPassword | diff --git a/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature b/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature index eb53d86e7..70e4c7085 100755 --- a/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingRestoreWallet.feature @@ -44,9 +44,9 @@ Feature: Onboarding - Restore wallet Then Password recommendation: "<passw_err>", complexity bar level: "<complex_bar_lvl>" and password confirmation error: "<passw_conf_err>" are displayed Examples: | password | password_conf | passw_err | complex_bar_lvl | passw_conf_err | - | a | | package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | - | P@ss | | package.core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | - | N_8J@bne | | package.core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | + | a | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | + | P@ss | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | + | N_8J@bne | | core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | | N_8J@bne87 | | empty | 3 | empty | | N_8J@bne87A | N_8J@bne87 | empty | 4 | core.walletSetupRegisterStep.noMatchPassword | diff --git a/packages/e2e-tests/src/features/TransactionsExtended.feature b/packages/e2e-tests/src/features/TransactionsExtended.feature index bf145c62f..925c1a711 100644 --- a/packages/e2e-tests/src/features/TransactionsExtended.feature +++ b/packages/e2e-tests/src/features/TransactionsExtended.feature @@ -123,4 +123,4 @@ Feature: Transactions - Extended view And I wait for the transaction history to be loaded and all transactions to be confirmed When the Sent transaction is displayed with value: "24.79 tADA" and tokens count 1 When I click on a transaction: 1 - Then The Tx details are displayed as "package.core.activityDetails.sent" for ADA with value: 24.79 and wallet: "WalletReceiveSimpleTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.sent" for ADA with value: 24.79 and wallet: "WalletReceiveSimpleTransactionE2E" address diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionBundlesE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionBundlesE2E.feature index 938c1fd21..a6d2b7dd7 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionBundlesE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionBundlesE2E.feature @@ -35,7 +35,7 @@ Feature: Send Transaction bundles - E2E When I navigate to Transactions extended page Then the Sent transaction is displayed with value: "4.50 tADA, 0.2333 LaceCoin3, 3 LaceCoin , +1" and tokens count 4 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.sent" for 4 tokens with following details: + Then The Tx details are displayed as "core.activityDetails.sent" for 4 tokens with following details: | address | ada | assets | | WalletFirstReceiveBundlesTransactionE2E | 1.34 tADA | 0.2333 LaceCoin3,1 LaceCoin | | WalletSecondReceiveBundlesTransactionE2E | 2.00 tADA | 2 LaceCoin | @@ -46,7 +46,7 @@ Feature: Send Transaction bundles - E2E And I navigate to Transactions extended page Then the Received transaction is displayed with value: "1.34 tADA, 0.2333 LaceCoin3, 1 LaceCoin" and tokens count 3 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.received" for 3 tokens with following details: + Then The Tx details are displayed as "core.activityDetails.received" for 3 tokens with following details: | address | ada | assets | | WalletSendBundlesTransactionE2E | 1.34 tADA | 0.2333 LaceCoin3,1 LaceCoin | When I open wallet: "WalletSecondReceiveBundlesTransactionE2E" in: extended mode @@ -54,7 +54,7 @@ Feature: Send Transaction bundles - E2E And I navigate to Transactions extended page Then the Received transaction is displayed with value: "3.16 tADA, 2 LaceCoin, 1 LaceCoin2" and tokens count 3 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.received" for 3 tokens with following details: + Then The Tx details are displayed as "core.activityDetails.received" for 3 tokens with following details: | address | ada | assets | | WalletSendBundlesTransactionE2E | 2.00 tADA | 2 LaceCoin | | WalletSendBundlesTransactionE2E | 1.16 tADA | 1 LaceCoin2 | diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature index 00d416513..45eaeff3c 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature @@ -25,13 +25,13 @@ Feature: Send Transactions from Dapp - E2E When I navigate to Transactions extended page Then the Sent transaction is displayed with value: "3.00 tADA" and tokens count 1 And I click on a transaction: 1 - Then The Tx details are displayed as "package.core.activityDetails.sent" for ADA with value: 3.00 and wallet: "WalletReceiveDappTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.sent" for ADA with value: 3.00 and wallet: "WalletReceiveDappTransactionE2E" address When I open wallet: "WalletReceiveDappTransactionE2E" in: extended mode And Wallet is synced And I navigate to Transactions extended page Then the Received transaction is displayed with value: "3.00 tADA" and tokens count 1 And I click on a transaction: 1 - Then The Tx details are displayed as "package.core.activityDetails.received" for ADA with value: 3.00 and wallet: "WalletSendDappTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.received" for ADA with value: 3.00 and wallet: "WalletSendDappTransactionE2E" address @LW-6797 @Testnet Scenario: Send Token from DApp E2E @@ -56,13 +56,13 @@ Feature: Send Transactions from Dapp - E2E When I navigate to Transactions extended page Then the Sent transaction is displayed with value: "1.38 tADA, 2 LaceCoin2" and tokens count 2 And I click on a transaction: 1 - Then The Tx details are displayed as "package.core.activityDetails.sent" for ADA with value: "1.38" and LaceCoin2 with value: "2" and wallet: "WalletReceiveDappTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.sent" for ADA with value: "1.38" and LaceCoin2 with value: "2" and wallet: "WalletReceiveDappTransactionE2E" address When I open wallet: "WalletReceiveDappTransactionE2E" in: extended mode And Wallet is synced And I navigate to Transactions extended page Then the Received transaction is displayed with value: "1.38 tADA" and tokens count 2 And I click on a transaction: 1 - Then The Tx details are displayed as "package.core.activityDetails.received" for ADA with value: "1.38" and LaceCoin2 with value: "2" and wallet: "WalletSendDappTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.received" for ADA with value: "1.38" and LaceCoin2 with value: "2" and wallet: "WalletSendDappTransactionE2E" address @LW-9279 @Testnet Scenario: Defect LW-9273 - User is not able to accept second dapp transaction until close first dapp window diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature index 3872ea473..3fdc19bfe 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature @@ -22,13 +22,13 @@ Feature: Send Simple Transactions - Extended view - E2E When I navigate to Transactions extended page Then the Sent transaction is displayed with value: "1.12 tADA" and tokens count 1 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.sent" for ADA with value: 1.12 and wallet: "WalletReceiveSimpleTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.sent" for ADA with value: 1.12 and wallet: "WalletReceiveSimpleTransactionE2E" address When I open wallet: "WalletReceiveSimpleTransactionE2E" in: extended mode And Wallet is synced And I navigate to Transactions extended page Then the Received transaction is displayed with value: "1.12 tADA" and tokens count 1 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.received" for ADA with value: 1.12 and wallet: "WalletSendSimpleTransactionE2E" address + Then The Tx details are displayed as "core.activityDetails.received" for ADA with value: 1.12 and wallet: "WalletSendSimpleTransactionE2E" address @LW-4677 Scenario: Extended-view - Self Transaction E2E diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature index d1b4431cc..1b600cbe5 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature @@ -22,13 +22,13 @@ Feature: Send Simple Transactions - Popup view - E2E When I navigate to Transactions popup page Then the Sent transaction is displayed with value: "1.12 tADA" and tokens count 1 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.sent" for ADA with value: 1.12 and wallet: "WalletReceiveSimpleTransaction2E2E" address + Then The Tx details are displayed as "core.activityDetails.sent" for ADA with value: 1.12 and wallet: "WalletReceiveSimpleTransaction2E2E" address When I open wallet: "WalletReceiveSimpleTransaction2E2E" in: popup mode And Wallet is synced And I navigate to Transactions popup page Then the Received transaction is displayed with value: "1.12 tADA" and tokens count 1 And I click and open recent transactions details until find transaction with correct hash - Then The Tx details are displayed as "package.core.activityDetails.received" for ADA with value: 1.12 and wallet: "WalletSendSimpleTransaction2E2E" address + Then The Tx details are displayed as "core.activityDetails.received" for ADA with value: 1.12 and wallet: "WalletSendSimpleTransaction2E2E" address @LW-4678 Scenario: Popup-view - Self Transaction E2E diff --git a/packages/e2e-tests/src/steps/nftsCommonSteps.ts b/packages/e2e-tests/src/steps/nftsCommonSteps.ts index 78839438d..c42f1f31b 100644 --- a/packages/e2e-tests/src/steps/nftsCommonSteps.ts +++ b/packages/e2e-tests/src/steps/nftsCommonSteps.ts @@ -65,8 +65,7 @@ When( Then( /^The Tx details are displayed as (sent|received) for NFT with name: "([^"]*)" and wallet: "([^"]*)" address$/, async (type: string, nftName: string, walletName: string) => { - const typeTranslationKey = - type === 'sent' ? 'package.core.activityDetails.sent' : 'package.core.activityDetails.received'; + const typeTranslationKey = type === 'sent' ? 'core.activityDetails.sent' : 'core.activityDetails.received'; const expectedActivityDetails = { transactionDescription: `${await t(typeTranslationKey)}\n(2)`, diff --git a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCardContainer.tsx b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCardContainer.tsx index 9f9cb7603..109a93a32 100644 --- a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCardContainer.tsx +++ b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCardContainer.tsx @@ -1,11 +1,14 @@ +import { initI18n } from 'features/i18n'; import { StakingPage } from 'features/staking'; +import { SetupBase } from 'features/staking/Setup/SetupBase'; import { PoolsFilter, QueryStakePoolsFilters, activePageSelector, useDelegationPortfolioStore } from 'features/store'; import { useState } from 'react'; import { useQueryStakePools } from '../hooks'; import { BrowsePoolsPreferencesCard } from './BrowsePoolsPreferencesCard'; import { SortAndFilterTab } from './types'; +initI18n(); -export const BrowsePoolsPreferencesCardContainer = () => { +export const BrowsePoolsPreferencesCardContainer = ({ theme }: { theme: 'light' | 'dark' }) => { const activePage = useDelegationPortfolioStore(activePageSelector); const { sort, setSort } = useQueryStakePools(); @@ -21,13 +24,15 @@ export const BrowsePoolsPreferencesCardContainer = () => { if (activePage !== StakingPage.browsePools) return null; return ( - <BrowsePoolsPreferencesCard - activeTab={activeTab} - sort={sort} - filter={filter} - onSortChange={setSort} - onFilterChange={setFilter} - onTabChange={setActiveTab} - /> + <SetupBase theme={theme}> + <BrowsePoolsPreferencesCard + activeTab={activeTab} + sort={sort} + filter={filter} + onSortChange={setSort} + onFilterChange={setFilter} + onTabChange={setActiveTab} + /> + </SetupBase> ); }; diff --git a/yarn.lock b/yarn.lock index fabcfe5b5..870c07ed8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7115,7 +7115,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:7.15.3, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.12.0, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.5, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:7.15.3, @babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2": version: 7.15.3 resolution: "@babel/runtime@npm:7.15.3" dependencies: @@ -10619,7 +10619,7 @@ __metadata: fake-indexeddb: 3.1.3 fork-ts-checker-webpack-plugin: ^7.2.1 graphql-tag: 2.12.5 - i18next: 20.4.0 + i18next: ^22.5.1 intersection-observer-polyfill: 0.1.0 jest-webextension-mock: ^3.7.19 lodash: 4.17.21 @@ -10630,7 +10630,7 @@ __metadata: process: ^0.11.10 react: 17.0.2 react-dom: 17.0.2 - react-i18next: 11.11.4 + react-i18next: ^12.3.1 react-lottie: ^1.2.3 react-router: 5.2.0 react-router-dom: 5.2.0 @@ -10675,7 +10675,7 @@ __metadata: react-dom: 17.0.2 rollup-plugin-polyfill-node: ^0.8.0 rxjs: 7.4.0 - typescript: ^4.3.5 + typescript: ^4.9.5 webextension-polyfill: 0.8.0 peerDependencies: react: 17.0.2 @@ -10697,7 +10697,7 @@ __metadata: react-copy-to-clipboard: 5.0.4 react-dom: 17.0.2 react-tooltip: 4.2.7 - typescript: ^4.3.5 + typescript: ^4.9.5 peerDependencies: react: 17.0.2 react-dom: 17.0.2 @@ -10738,13 +10738,13 @@ __metadata: axios-cache-adapter: 2.7.3 classnames: ^2.3.1 debounce-promise: ^3.1.2 - i18next: 20.4.0 + i18next: ^22.5.1 jest-mock-extended: 3.0.4 lodash: 4.17.21 react: 17.0.2 react-copy-to-clipboard: 5.0.4 react-dom: 17.0.2 - react-i18next: 11.11.4 + react-i18next: ^12.3.1 react-infinite-scroll-component: ^6.1.0 sass: ^1.68.0 storybook: ^7.4.3 @@ -33940,15 +33940,6 @@ __metadata: languageName: node linkType: hard -"i18next@npm:20.4.0": - version: 20.4.0 - resolution: "i18next@npm:20.4.0" - dependencies: - "@babel/runtime": ^7.12.0 - checksum: e2e3697132317f241b6290d4d0b549b0e445a9b400666505f549d62d38cd2f8347fb511e79c6f88f59021f0c9976c6f18cfe345fa3f223c110a3b0072a256f66 - languageName: node - linkType: hard - "i18next@npm:^22.5.1": version: 22.5.1 resolution: "i18next@npm:22.5.1" @@ -37633,7 +37624,7 @@ __metadata: ts-jest: 29.1.2 ts-patch: ^2.0.1 typedoc: ^0.21.5 - typescript: ^4.3.5 + typescript: ^4.9.5 typescript-transform-paths: 3.3.1 webpack: 5.76.0 webpack-cli: 4.7.2 @@ -44828,19 +44819,6 @@ __metadata: languageName: node linkType: hard -"react-i18next@npm:11.11.4": - version: 11.11.4 - resolution: "react-i18next@npm:11.11.4" - dependencies: - "@babel/runtime": ^7.14.5 - html-parse-stringify: ^3.0.1 - peerDependencies: - i18next: ">= 19.0.0" - react: ">= 16.8.0" - checksum: 5630bfe1b39e6ce893fcccfba62cd42db92d130c0d00f181ec3bc68884ee0db3c698d0b6320d05dc783e5e32c14f7e547bb8aa034788a5f317563ad32f29edf9 - languageName: node - linkType: hard - "react-i18next@npm:^12.3.1": version: 12.3.1 resolution: "react-i18next@npm:12.3.1" From 21e9f79a5218fda652ff51eabc4bf6234162411c Mon Sep 17 00:00:00 2001 From: bslabiak <112852128+bslabiak@users.noreply.github.com> Date: Tue, 9 Apr 2024 13:45:10 +0200 Subject: [PATCH 38/74] =?UTF-8?q?test(extension):=20LW-10167=20add=20from/?= =?UTF-8?q?to=20section=20dapp=20tx=20and=20new=20ui=20elem=E2=80=A6=20(#1?= =?UTF-8?q?011)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * test(extension): lw-10167 add from/to section dapp tx and new ui elements for test automation * test(extension): remove additional div * test(extension): remove additional div from second expander * test(extension): fix after comments * test(extension): extract methods to utils file * test(extension): fix parsing method * test(extension): fix test after translation changes --- .../components/ActivityDetail/Collateral.tsx | 3 + .../DappAddressSections.tsx | 66 +++++---- .../DappTransaction/DappTransaction.tsx | 4 +- .../DappTransactionHeader.tsx | 2 +- .../src/assert/dAppConnectorAssert.ts | 63 +++++++-- .../dappConnector/confirmTransactionPage.ts | 131 ++++++++++-------- .../e2e/SendTransactionDappE2E.feature | 22 ++- .../e2e-tests/src/steps/dAppConnectorSteps.ts | 32 +++-- .../e2e-tests/src/utils/dappConnectorUtils.ts | 27 ++++ .../e2e-tests/src/utils/getTextFromArray.ts | 3 + .../dapp-transaction-assets.component.tsx | 4 +- .../dapp-transaction-summary.component.tsx | 5 +- .../summary-expander.component.tsx | 11 +- 13 files changed, 260 insertions(+), 113 deletions(-) create mode 100644 packages/e2e-tests/src/utils/dappConnectorUtils.ts create mode 100644 packages/e2e-tests/src/utils/getTextFromArray.ts diff --git a/packages/core/src/ui/components/ActivityDetail/Collateral.tsx b/packages/core/src/ui/components/ActivityDetail/Collateral.tsx index affab932e..ba6ee5439 100644 --- a/packages/core/src/ui/components/ActivityDetail/Collateral.tsx +++ b/packages/core/src/ui/components/ActivityDetail/Collateral.tsx @@ -17,6 +17,7 @@ export interface Props { displayFiat?: boolean; className?: string; status?: CollateralStatus; + testId?: string; } export const Collateral = ({ @@ -25,6 +26,7 @@ export const Collateral = ({ coinSymbol, displayFiat, className, + testId, status = CollateralStatus.REVIEW }: Props): React.ReactElement => { const { t } = useTranslate(); @@ -50,6 +52,7 @@ export const Collateral = ({ tooltip={getTooltipText()} displayFiat={displayFiat} className={className} + data-testid={testId ?? 'collateral'} /> {status === CollateralStatus.ERROR && ( <Box mt="$32"> diff --git a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx index 93c0a18a2..34abb6ebd 100644 --- a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx +++ b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx @@ -49,12 +49,12 @@ const getAssetTokenName = (assetWithAmount: AssetInfoWithAmount) => { const charBeforeEllName = 9; const charAfterEllName = 0; -const displayGroupedNFTs = (nfts: AssetInfoWithAmount[]) => +const displayGroupedNFTs = (nfts: AssetInfoWithAmount[], testId?: string) => nfts.map((nft: AssetInfoWithAmount) => { const imageSrc = nft.assetInfo.tokenMetadata?.icon ?? nft.assetInfo.nftMetadata?.image ?? undefined; return ( <TransactionAssets - testId="dapp-transaction-nfts-container" + testId={testId} key={nft.assetInfo.fingerprint} imageSrc={imageSrc} balance={Wallet.util.calculateAssetBalance(nft.amount, nft.assetInfo)} @@ -64,13 +64,13 @@ const displayGroupedNFTs = (nfts: AssetInfoWithAmount[]) => ); }); -const displayGroupedTokens = (tokens: AssetInfoWithAmount[]) => +const displayGroupedTokens = (tokens: AssetInfoWithAmount[], testId?: string) => tokens.map((token: AssetInfoWithAmount) => { const imageSrc = token.assetInfo.tokenMetadata?.icon ?? token.assetInfo.nftMetadata?.image ?? undefined; return ( <TransactionAssets - testId="dapp-transaction-token-container" + testId={testId} key={token.assetInfo.fingerprint} imageSrc={token.assetInfo.tokenMetadata?.icon ?? token.assetInfo.nftMetadata?.image ?? undefined} balance={Wallet.util.calculateAssetBalance(token.amount, token.assetInfo)} @@ -109,15 +109,19 @@ export const DappAddressSections = ({ return ( <> - <SummaryExpander title={t('core.dappTransaction.fromAddress')} disabled={!isFromAddressesEnabled}> + <SummaryExpander + title={t('core.dappTransaction.fromAddress')} + disabled={!isFromAddressesEnabled} + testId="dapp-transaction-from-section-expander" + > <div className={styles.summaryContent}> {[...groupedFromAddresses.entries()].map(([address, addressData]) => ( <> - <div key={address} className={styles.address}> - <Text className={styles.label} data-testid="dapp-transaction-from-address-title"> + <div key={address} className={styles.address} data-testid="dapp-transaction-from-row"> + <Text className={styles.label} data-testid="dapp-transaction-address-title"> {t('core.dappTransaction.address')} </Text> - <Text className={styles.value} data-testid="dapp-transaction-from-address-address"> + <Text className={styles.value} data-testid="dapp-transaction-address"> <Tooltip label={address}> <span>{addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)}</span> </Tooltip> @@ -125,36 +129,37 @@ export const DappAddressSections = ({ </div> {(addressData.tokens.length > 0 || addressData.coins.length > 0) && ( <> - <div className={styles.tokenCount}> + <div className={styles.tokenCount} data-testid="dapp-transaction-from-row"> <Title level={5} className={styles.label} data-testid="dapp-transaction-tokens-title"> {t('core.dappTransaction.tokens')} - + <Title level={5} className={styles.value} data-testid="dapp-transaction-tokens-value"> -{getTokenQuantity(addressData.tokens, addressData.coins)} {itemsCountCopy}
{addressData.coins.map((coin) => ( ))} - {displayGroupedTokens(addressData.tokens)} + {displayGroupedTokens(addressData.tokens, 'dapp-transaction-from-row')} )} {addressData.nfts.length > 0 && ( <> -
+
{t('core.dappTransaction.nfts')} - + <Title level={5} data-testid="dapp-transaction-nfts-amount-value"> -{addressData.nfts.length} {itemsCountCopy}
- {displayGroupedNFTs(addressData.nfts)} + {displayGroupedNFTs(addressData.nfts, 'dapp-transaction-from-row')} )} @@ -162,15 +167,19 @@ export const DappAddressSections = ({
- +
{[...groupedToAddresses.entries()].map(([address, addressData]) => ( <> -
- +
+ {t('core.dappTransaction.address')} - + {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} @@ -178,11 +187,15 @@ export const DappAddressSections = ({
{(addressData.tokens.length > 0 || addressData.coins.length > 0) && ( <> -
+
{t('core.dappTransaction.tokens')} - + <Title + level={5} + className={classNames(styles.value, styles.positiveAmount)} + data-testid="dapp-transaction-tokens-value" + > {getTokenQuantity(addressData.tokens, addressData.coins)} {itemsCountCopy}
@@ -191,23 +204,28 @@ export const DappAddressSections = ({ key={`${address}${coin}`} cardanoSymbol={coinSymbol} transactionAmount={getStringFromLovelace(coin)} + testId="dapp-transaction-to-row" /> ))} - {displayGroupedTokens(addressData.tokens)} + {displayGroupedTokens(addressData.tokens, 'dapp-transaction-to-row')} )} {addressData.nfts.length > 0 && ( <> -
+
{t('core.dappTransaction.nfts')} - + <Title + level={5} + className={classNames(styles.value, styles.positiveAmount)} + data-testid="dapp-transaction-nfts-amount-value" + > {addressData.nfts.length} {itemsCountCopy}
- {displayGroupedNFTs(addressData.nfts)} + {displayGroupedNFTs(addressData.nfts, 'dapp-transaction-to-row')} )} diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx index b4621b7b8..aed6f5cfd 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx @@ -126,6 +126,7 @@ export const DappTransaction = ({
- + {name} diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index 2eff34bdf..1fab9e989 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -19,6 +19,9 @@ import { getTestWallet, TestWalletName } from '../support/walletConfiguration'; import { browser } from '@wdio/globals'; import InsufficientFundsDAppPage from '../elements/dappConnector/insufficientFundsDAppPage'; import ErrorDAppModal from '../elements/dappConnector/errorDAppModal'; +import { getTextFromElementArray } from '../utils/getTextFromArray'; +import DAppConnectorPageObject from '../pageobject/dAppConnectorPageObject'; +import { parseDappCucumberAssetList } from '../utils/dappConnectorUtils'; export type ExpectedDAppDetails = { hasLogo: boolean; @@ -28,9 +31,7 @@ export type ExpectedDAppDetails = { export type ExpectedTransactionData = { typeOfTransaction: string; - amountADA: number; - amountAsset?: string; - recipientAddress: string; + assetsDetails: string[]; }; class DAppConnectorAssert { @@ -41,14 +42,27 @@ class DAppConnectorAssert { expect(await commonDappPageElements.betaPill.getText()).to.equal(await t('core.dapp.beta')); } - async assertSeeAuthorizeDAppPage() { + async assertSeeTitleAndDappDetails(expectedTitleKey: string, expectedDappDetails: ExpectedDAppDetails) { + const currentDAppUrl = new URL(expectedDappDetails.url); + const commonDappPageElements = new CommonDappPageElements(); + await commonDappPageElements.pageTitle.waitForDisplayed(); + expect(await commonDappPageElements.pageTitle.getText()).to.equal(await t(expectedTitleKey)); + await commonDappPageElements.dAppLogo.waitForDisplayed({ reverse: !expectedDappDetails.hasLogo }); + await commonDappPageElements.dAppName.waitForDisplayed(); + expect(await commonDappPageElements.dAppName.getText()).to.equal(expectedDappDetails.name); + await commonDappPageElements.dAppUrl.waitForDisplayed(); + const expectedUrl = `${currentDAppUrl.protocol}//${currentDAppUrl.host}`; + expect(await commonDappPageElements.dAppUrl.getText()).to.equal(expectedUrl); + } + + async assertSeeAuthorizeDAppPage(expectedDappDetails: ExpectedDAppDetails) { await this.assertSeeHeader(); + await this.assertSeeTitleAndDappDetails('dapp.connect.header', expectedDappDetails); await AuthorizeDAppPage.banner.container.waitForDisplayed(); await AuthorizeDAppPage.banner.icon.waitForDisplayed(); await AuthorizeDAppPage.banner.description.waitForDisplayed(); expect(await AuthorizeDAppPage.banner.description.getText()).to.equal(await t('core.authorizeDapp.warning')); await this.assertSeeAuthorizePagePermissions(); - await AuthorizeDAppPage.authorizeButton.waitForDisplayed(); expect(await AuthorizeDAppPage.authorizeButton.getText()).to.equal(await t('dapp.connect.btn.accept')); await AuthorizeDAppPage.cancelButton.waitForDisplayed(); @@ -259,25 +273,41 @@ class DAppConnectorAssert { } } - async assertSeeConfirmTransactionPage(expectedTransactionData: ExpectedTransactionData) { + async assertSeeConfirmTransactionPage({ assetsDetails, typeOfTransaction }: ExpectedTransactionData) { await this.assertSeeHeader(); await ConfirmTransactionPage.transactionTypeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionTypeTitle.getText()).to.equal( - await t('core.dappTransaction.transaction') + await t('core.dappTransaction.transaction', 'core') ); await ConfirmTransactionPage.transactionType.waitForDisplayed(); - expect(await ConfirmTransactionPage.transactionType.getText()).to.equal(expectedTransactionData.typeOfTransaction); + expect(await ConfirmTransactionPage.transactionType.getText()).to.equal(typeOfTransaction); + await ConfirmTransactionPage.transactionOriginSectionExpanderButton.waitForDisplayed(); + await ConfirmTransactionPage.transactionOriginLabel.waitForDisplayed(); + expect(await ConfirmTransactionPage.transactionOriginLabel.getText()).to.equal( + await t('core.dappTransaction.origin', 'core') + ); + await ConfirmTransactionPage.expandSectionInDappTransactionWindow('Origin'); + expect(await ConfirmTransactionPage.transactionOrigin.getText()).to.equal(DAppConnectorPageObject.TEST_DAPP_NAME); await ConfirmTransactionPage.transactionFeeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionFeeTitle.getText()).to.equal( await t('core.activityDetails.transactionFee', 'core') ); await ConfirmTransactionPage.transactionFeeValueAda.waitForDisplayed(); - const fee = Number((await ConfirmTransactionPage.transactionFeeValueAda.getText()).split(' ')[0]); - await ConfirmTransactionPage.transactionAmountValue.waitForDisplayed(); - const totalAdaAmount = (expectedTransactionData.amountADA - fee).toFixed(2); - expect(await ConfirmTransactionPage.transactionAmountValue.getText()).to.equal(`${totalAdaAmount} tADA`); + const parsedAssetsList = await parseDappCucumberAssetList(assetsDetails); + expect(await getTextFromElementArray(await ConfirmTransactionPage.transactionSummaryAssetsRows)).to.deep.equal( + parsedAssetsList + ); + + await ConfirmTransactionPage.transactionFromSectionExpanderButton.waitForDisplayed(); + expect(await ConfirmTransactionPage.transactionFromSectionExpanderLabel.getText()).to.equal( + await t('core.dappTransaction.fromAddress', 'core') + ); + await ConfirmTransactionPage.transactionToSectionExpanderButton.waitForDisplayed(); + expect(await ConfirmTransactionPage.transactionToSectionExpanderLabel.getText()).to.equal( + await t('core.dappTransaction.toAddress', 'core') + ); await ConfirmTransactionPage.confirmButton.waitForDisplayed(); expect(await ConfirmTransactionPage.confirmButton.getText()).to.equal(await t('dapp.confirm.btn.confirm')); @@ -286,6 +316,15 @@ class DAppConnectorAssert { expect(await ConfirmTransactionPage.cancelButton.getText()).to.equal(await t('dapp.confirm.btn.cancel')); } + async assertSeeConfirmFromAddressTransactionPage(section: 'To address' | 'From address', assets: string[]) { + const adjustedAssetsList = await parseDappCucumberAssetList(assets); + const expectedAssets = + section === 'To address' + ? await ConfirmTransactionPage.getAssetsToAddressSection() + : await ConfirmTransactionPage.getAssetsFromAddressSection(); + expect(expectedAssets).to.deep.equal(adjustedAssetsList); + } + async assertSeeSignTransactionPage() { await this.assertSeeHeader(); await SignTransactionPage.passwordInput.container.waitForDisplayed(); diff --git a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts index e5ddbe951..46ef3468d 100644 --- a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts +++ b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts @@ -1,120 +1,141 @@ /* eslint-disable no-undef */ -import { ChainablePromiseElement } from 'webdriverio'; import CommonDappPageElements from './commonDappPageElements'; +import { ChainablePromiseArray } from 'webdriverio/build/types'; +import { getTextFromElementArray } from '../../utils/getTextFromArray'; class ConfirmTransactionPage extends CommonDappPageElements { private TRANSACTION_TYPE_TITLE = '[data-testid="dapp-transaction-title"]'; private TRANSACTION_TYPE = '[data-testid="dapp-transaction-type"]'; - private TRANSACTION_AMOUNT_TITLE = '[data-testid="dapp-transaction-amount-title"]'; - private TRANSACTION_AMOUNT_VALUE = '[data-testid="dapp-transaction-amount-value"]'; - + private TRANSACTION_SUMMARY_ROW = '[data-testid="dapp-transaction-summary-row"]'; + private TRANSACTION_FROM_ROW = '[data-testid="dapp-transaction-from-row"]'; + private TRANSACTION_TO_ROW = '[data-testid="dapp-transaction-to-row"]'; private TRANSACTION_ORIGIN = '[data-testid="dapp-transaction-origin"]'; - + private TRANSACTION_ORIGIN_LABEL = '[data-testid="dapp-transaction-origin-expander"] [data-testid="expander-title"]'; + private TRANSACTION_ORIGIN_EXPANDER_BUTTON = + '[data-testid="dapp-transaction-origin-expander"] [data-testid="expander-button"]'; private TRANSACTION_RETURNED_DEPOSIT_TITLE = '[data-testid="tx-amount-returned-deposit-label"]'; private TRANSACTION_RETURNED_DEPOSIT_ADA = '[data-testid="tx-amount-returned-deposit-amount"]'; - private TRANSACTION_DEPOSIT_TITLE = '[data-testid="tx-amount-deposit-label"]'; private TRANSACTION_DEPOSIT_ADA = '[data-testid="tx-amount-deposit-amount"]'; - private TRANSACTION_FEE_TITLE = '[data-testid="tx-amount-fee-label"]'; private TRANSACTION_FEE_ADA = '[data-testid="tx-amount-fee-amount"]'; - - private TRANSACTION_TO_ADDRESS_TITLE = '[data-testid="dapp-transaction-to-address-title"]'; - private TRANSACTION_TO_ADDRESS = '[data-testid="dapp-transaction-to-address-address"]'; - - private TRANSACTION_FROM_ADDRESS_TITLE = '[data-testid="dapp-transaction-from-address-title"]'; - private TRANSACTION_FROM_ADDRESS_ADDRESS = '[data-testid="dapp-transaction-from-address-address"]'; - - private TRANSACTION_AMOUNT_NFTS_TITLE = '[data-testid="dapp-transaction-nfts-title"]'; - private TRANSACTION_AMOUNT_NFTS_CONTAINER = '[data-testid="dapp-transaction-nfts-container"]'; - - private TRANSACTION_AMOUNT_TOKENS_TITLE = '[data-testid="dapp-transaction-tokens-title"]'; - private TRANSACTION_AMOUNT_TOKEN_CONTAINER = '[data-testid="dapp-transaction-token-container"]'; - + private TRANSACTION_TO_SECTION_EXPANDER_BUTTON = + '[data-testid="dapp-transaction-to-section-expander"] [data-testid="expander-button"]'; + private TRANSACTION_TO_SECTION_EXPANDER_LABEL = + '[data-testid="dapp-transaction-to-section-expander"] [data-testid="expander-title"]'; + private TRANSACTION_FROM_SECTION_EXPANDER_BUTTON = + '[data-testid="dapp-transaction-from-section-expander"] [data-testid="expander-button"]'; + private TRANSACTION_FROM_SECTION_EXPANDER_LABEL = + '[data-testid="dapp-transaction-from-section-expander"] [data-testid="expander-title"]'; private CONFIRM_BUTTON = '[data-testid="dapp-transaction-confirm"]'; private CANCEL_BUTTON = '[data-testid="dapp-transaction-cancel"]'; - get transactionOrigin(): ChainablePromiseElement { + get transactionOrigin() { return $(this.TRANSACTION_ORIGIN); } - get transactionFeeTitle(): ChainablePromiseElement { + get transactionOriginLabel() { + return $(this.TRANSACTION_ORIGIN_LABEL); + } + + get transactionFeeTitle() { return $(this.TRANSACTION_FEE_TITLE); } - get transactionFeeValueAda(): ChainablePromiseElement { + get transactionFeeValueAda() { return $(this.TRANSACTION_FEE_ADA); } - get transactionDepositTitle(): ChainablePromiseElement { + get transactionDepositTitle() { return $(this.TRANSACTION_DEPOSIT_TITLE); } - get transactionDepositValueAda(): ChainablePromiseElement { + get transactionDepositValueAda() { return $(this.TRANSACTION_DEPOSIT_ADA); } - get transactionReturnedDepositValueAda(): ChainablePromiseElement { + get transactionReturnedDepositValueAda() { return $(this.TRANSACTION_RETURNED_DEPOSIT_ADA); } - get transactionReturnedDepositTitle(): ChainablePromiseElement { + get transactionReturnedDepositTitle() { return $(this.TRANSACTION_RETURNED_DEPOSIT_TITLE); } - get transactionAmountNftsTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_NFTS_TITLE); + get transactionTypeTitle() { + return $(this.TRANSACTION_TYPE_TITLE); } - get transactionAmountNftsContainer(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_NFTS_CONTAINER); + get transactionType() { + return $(this.TRANSACTION_TYPE); } - get transactionAmountTokensTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_TOKENS_TITLE); + get confirmButton() { + return $(this.CONFIRM_BUTTON); } - get transactionAmountTokensContainer(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_TOKEN_CONTAINER); + get cancelButton() { + return $(this.CANCEL_BUTTON); } - get transactionToAddressTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_TO_ADDRESS_TITLE); + get transactionToSectionExpanderButton() { + return $(this.TRANSACTION_TO_SECTION_EXPANDER_BUTTON); } - get transactionToAddress(): ChainablePromiseElement { - return $(this.TRANSACTION_TO_ADDRESS); + get transactionToSectionExpanderLabel() { + return $(this.TRANSACTION_TO_SECTION_EXPANDER_LABEL); } - get transactionFromAddressTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_FROM_ADDRESS_TITLE); + get transactionFromSectionExpanderButton() { + return $(this.TRANSACTION_FROM_SECTION_EXPANDER_BUTTON); } - get transactionFromAddress(): ChainablePromiseElement { - return $(this.TRANSACTION_FROM_ADDRESS_ADDRESS); + get transactionFromSectionExpanderLabel() { + return $(this.TRANSACTION_FROM_SECTION_EXPANDER_LABEL); } - get transactionTypeTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_TYPE_TITLE); + get transactionOriginSectionExpanderButton() { + return $(this.TRANSACTION_ORIGIN_EXPANDER_BUTTON); } - get transactionType(): ChainablePromiseElement { - return $(this.TRANSACTION_TYPE); + get transactionSummaryAssetsRows(): ChainablePromiseArray { + return $$(this.TRANSACTION_SUMMARY_ROW); } - get transactionAmountTitle(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_TITLE); + get transactionFromAssetsRows(): ChainablePromiseArray { + return $$(this.TRANSACTION_FROM_ROW); } - get transactionAmountValue(): ChainablePromiseElement { - return $(this.TRANSACTION_AMOUNT_VALUE); + get transactionToAssetsRows(): ChainablePromiseArray { + return $$(this.TRANSACTION_TO_ROW); } - get confirmButton(): ChainablePromiseElement { - return $(this.CONFIRM_BUTTON); + async getAssetsFromAddressSection() { + const textArray = await getTextFromElementArray(await this.transactionToAssetsRows); + return textArray.map((str) => str.replace(/\n/g, ' ')); } - get cancelButton(): ChainablePromiseElement { - return $(this.CANCEL_BUTTON); + + async getAssetsToAddressSection() { + const textArray = await getTextFromElementArray(await this.transactionFromAssetsRows); + return textArray.map((str) => str.replace(/\n/g, ' ')); + } + + async expandSectionInDappTransactionWindow(section: 'Origin' | 'From address' | 'To address') { + await this.transactionOriginSectionExpanderButton.waitForDisplayed(); + switch (section) { + case 'Origin': + await this.transactionOriginSectionExpanderButton.click(); + break; + case 'From address': + await this.transactionFromSectionExpanderButton.click(); + break; + case 'To address': + await this.transactionToSectionExpanderButton.scrollIntoView(); + await this.transactionToSectionExpanderButton.click(); + break; + default: + throw new Error(`Unsupported section name: ${section}`); + } } } diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature index 45eaeff3c..d54705a36 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionDappE2E.feature @@ -10,7 +10,8 @@ Feature: Send Transactions from Dapp - E2E And I open and authorize test DApp with "Only once" setting And I set send to wallet address to: "WalletReceiveDappTransactionE2E" in test DApp When I click "Send ADA" "Run" button in test DApp - Then I see DApp connector "Confirm transaction" page with: "-3.00" tADA - fee, "0" assets and receiving wallet "WalletReceiveDappTransactionE2E" + Then I see DApp connector "Confirm transaction" page with all UI elements and with following data in "Transaction Summary" section: + | -3.00 tADA - FEE | And I save fee value on DApp "Confirm transaction" page And I click "Confirm" button on "Confirm transaction" page And I see DApp connector "Sign transaction" page @@ -41,7 +42,21 @@ Feature: Send Transactions from Dapp - E2E And I set send to wallet address to: "WalletReceiveDappTransactionE2E" in test DApp And I click "Send Token" button in test DApp When I click "Send Token" "Run" button in test DApp - Then I see DApp connector "Confirm transaction" page with: "-1.38" tADA - fee, "-2 LaceCoin2" assets and receiving wallet "WalletReceiveDappTransactionE2E" + Then I see DApp connector "Confirm transaction" page with all UI elements and with following data in "Transaction Summary" section: + | -1.38 tADA - FEE | + | -2 LaceCoin2 | + And I expand "From address" section in DApp transaction window + And I see DApp connector "Confirm transaction" page "From address" section with following data: + | Address WalletSendDappTransactionE2E | + | Tokens -2 item(s) | + | -1.38 tADA - FEE | + | -2 LaceCoin2 | + And I expand "To address" section in DApp transaction window + And I see DApp connector "Confirm transaction" page "To address" section with following data: + | Address WalletReceiveDappTransactionE2E | + | Tokens 2 item(s) | + | 1.38 tADA | + | 2 LaceCoin2 | And I save fee value on DApp "Confirm transaction" page And I click "Confirm" button on "Confirm transaction" page And I see DApp connector "Sign transaction" page @@ -70,7 +85,8 @@ Feature: Send Transactions from Dapp - E2E And I open and authorize test DApp with "Only once" setting And I set send to wallet address to: "WalletReceiveDappTransactionE2E" in test DApp When I click "Send ADA" "Run" button in test DApp - Then I see DApp connector "Confirm transaction" page with: "-3.00" tADA - fee, "0" assets and receiving wallet "WalletReceiveDappTransactionE2E" + Then I see DApp connector "Confirm transaction" page with all UI elements and with following data in "Transaction Summary" section: + | -3.00 tADA - FEE | And I click "Confirm" button on "Confirm transaction" page And I see DApp connector "Sign transaction" page And I fill correct password diff --git a/packages/e2e-tests/src/steps/dAppConnectorSteps.ts b/packages/e2e-tests/src/steps/dAppConnectorSteps.ts index 894774b0b..b92b465e9 100755 --- a/packages/e2e-tests/src/steps/dAppConnectorSteps.ts +++ b/packages/e2e-tests/src/steps/dAppConnectorSteps.ts @@ -15,6 +15,7 @@ import popupView from '../page/popupView'; import { Logger } from '../support/logger'; import collateralDAppPage from '../elements/dappConnector/collateralDAppPage'; import InsufficientFundsDAppPage from '../elements/dappConnector/insufficientFundsDAppPage'; +import { dataTableAsStringArray } from '../utils/cucumberDataHelper'; const testDAppDetails: ExpectedDAppDetails = { hasLogo: true, @@ -28,7 +29,7 @@ When(/^I open test DApp$/, async () => { Then(/^I see DApp authorization window$/, async () => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); - await DAppConnectorAssert.assertSeeAuthorizeDAppPage(); + await DAppConnectorAssert.assertSeeAuthorizeDAppPage(testDAppDetails); }); Then(/^I see DApp collateral window$/, async () => { @@ -43,7 +44,7 @@ Then(/^I see DApp insufficient funds window$/, async () => { Then(/^I see DApp authorization window in (dark|light) mode$/, async (mode: 'dark' | 'light') => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); - await DAppConnectorAssert.assertSeeAuthorizeDAppPage(); + await DAppConnectorAssert.assertSeeAuthorizeDAppPage(testDAppDetails); await CommonAssert.assertSeeThemeMode(mode); }); @@ -69,20 +70,24 @@ Then(/^I see DApp connector Sign data "Confirm transaction" page$/, async () => }); Then( - /^I see DApp connector "Confirm transaction" page with: "([^"]*)" tADA - fee, "([^"]*)" assets and receiving wallet "([^"]*)"$/, - async (adaValue: string, assetValue: string, walletName: string) => { + /^I see DApp connector "Confirm transaction" page with all UI elements and with following data in "Transaction Summary" section:$/, + async (dataTable) => { await DAppConnectorPageObject.waitAndSwitchToDAppConnectorWindow(3); - const expectedTransactionData: ExpectedTransactionData = { typeOfTransaction: 'Send', - amountADA: Number(adaValue), - amountAsset: assetValue, - recipientAddress: String(getTestWallet(walletName).address) + assetsDetails: dataTableAsStringArray(dataTable) }; await DAppConnectorAssert.assertSeeConfirmTransactionPage(expectedTransactionData); } ); +Then( + /^I see DApp connector "Confirm transaction" page "(From address|To address)" section with following data:$/, + async (section: 'From address' | 'To address', entries) => { + await DAppConnectorAssert.assertSeeConfirmFromAddressTransactionPage(section, dataTableAsStringArray(entries)); + } +); + Then( /^I see DApp connector "(Confirm transaction|Something went wrong|All done)" page on (\d) window handle$/, async (expectedPage: 'Confirm transaction' | 'Something went wrong' | 'All done', handleNumber: number) => { @@ -90,9 +95,7 @@ Then( const defaultDAppTransactionData: ExpectedTransactionData = { typeOfTransaction: 'Send', - amountADA: -3, - amountAsset: '0', - recipientAddress: String(getTestWallet('WalletReceiveDappTransactionE2E').address) + assetsDetails: ['-3 tADA'] }; switch (expectedPage) { @@ -148,6 +151,13 @@ Then(/^I click "(Authorize|Cancel)" button in DApp authorization window$/, async await DAppConnectorPageObject.clickButtonInDAppAuthorizationWindow(button); }); +Then( + /^I expand "(Origin|From address|To address)" section in DApp transaction window$/, + async (section: 'Origin' | 'From address' | 'To address') => { + await ConfirmTransactionPage.expandSectionInDappTransactionWindow(section); + } +); + Then(/^I click "(Always|Only once)" button in DApp authorization window$/, async (button: 'Always' | 'Only once') => { await DAppConnectorPageObject.clickButtonInDAppAuthorizationModal(button); }); diff --git a/packages/e2e-tests/src/utils/dappConnectorUtils.ts b/packages/e2e-tests/src/utils/dappConnectorUtils.ts new file mode 100644 index 000000000..0da82b0a1 --- /dev/null +++ b/packages/e2e-tests/src/utils/dappConnectorUtils.ts @@ -0,0 +1,27 @@ +import ConfirmTransactionPage from '../elements/dappConnector/confirmTransactionPage'; +import { getTestWallet } from '../support/walletConfiguration'; + +const subtractFeeFromCucumberListElement = async (entry: string) => { + const fee = Number((await ConfirmTransactionPage.transactionFeeValueAda.getText()).split(' ')[0]); + const [amount, currency] = entry.split(' '); + const amountIncludingFee = (Number(amount) - fee).toFixed(2); + return `${amountIncludingFee} ${currency}`; +}; + +const shortenAddressFromCucumberListElement = (wallet: string) => { + const [addressLabel, walletValue] = wallet.split(' '); + const fullAddress = String(getTestWallet(walletValue).address); + return `${addressLabel} ${fullAddress.slice(0, 8)}...${fullAddress.slice(-8)}`; +}; + +export const parseDappCucumberAssetList = async (assetsList: string[]): Promise => + await Promise.all( + assetsList.map(async (asset) => { + if (asset.includes('- FEE')) { + return await subtractFeeFromCucumberListElement(asset); + } else if (asset.includes('Address')) { + return shortenAddressFromCucumberListElement(asset); + } + return asset; + }) + ); diff --git a/packages/e2e-tests/src/utils/getTextFromArray.ts b/packages/e2e-tests/src/utils/getTextFromArray.ts new file mode 100644 index 000000000..af652b27a --- /dev/null +++ b/packages/e2e-tests/src/utils/getTextFromArray.ts @@ -0,0 +1,3 @@ +/* eslint-disable no-undef */ +export const getTextFromElementArray = async (array: WebdriverIO.ElementArray): Promise => + await array.map(async (element) => await element.getText()); diff --git a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-assets.component.tsx b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-assets.component.tsx index 4c6ca2394..86b292e64 100644 --- a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-assets.component.tsx +++ b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-assets.component.tsx @@ -61,7 +61,7 @@ export const TransactionAssets = ({ }; return ( -
+
- + {balance} {tokenName} diff --git a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx index 40429f2b9..ad400931e 100644 --- a/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx +++ b/packages/ui/src/design-system/dapp-transaction-summary/dapp-transaction-summary.component.tsx @@ -13,12 +13,14 @@ import * as styles from './dapp-transaction-summary.css'; import type { OmitClassName } from '../../types'; type Props = OmitClassName<'div'> & { + testId?: string; transactionAmount: string; title?: string; cardanoSymbol?: string; }; export const TransactionSummary = ({ + testId, transactionAmount, title, cardanoSymbol, @@ -32,7 +34,7 @@ export const TransactionSummary = ({ )} -
+
@@ -44,7 +46,6 @@ export const TransactionSummary = ({ [styles.positiveBalance]: !transactionAmount.includes('-'), [styles.negativeBalance]: transactionAmount.includes('-'), })} - data-testId="dapp-transaction-amount-value" > {transactionAmount} {cardanoSymbol} diff --git a/packages/ui/src/design-system/summary-expander/summary-expander.component.tsx b/packages/ui/src/design-system/summary-expander/summary-expander.component.tsx index 0857be2eb..f51a341a6 100644 --- a/packages/ui/src/design-system/summary-expander/summary-expander.component.tsx +++ b/packages/ui/src/design-system/summary-expander/summary-expander.component.tsx @@ -18,6 +18,7 @@ export type Props = OmitClassName<'button'> & onOpenChange?: (open: boolean) => void; title: string; disabled?: boolean; + testId?: string; }>; export const SummaryExpander = ({ @@ -25,6 +26,7 @@ export const SummaryExpander = ({ onOpenChange, title, disabled, + testId, children, ...props }: Readonly): JSX.Element => { @@ -34,6 +36,7 @@ export const SummaryExpander = ({ open={open} onOpenChange={onOpenChange} disabled={disabled} + data-testid={testId ?? 'expander'} > - + {title} - + From 0bd48b59aac369f5ce9d5b1d63efd6b306e950cb Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Tue, 9 Apr 2024 14:18:53 +0200 Subject: [PATCH 39/74] chore: update sonarcloud config (#1042) --- .github/workflows/sonar-cloud.yml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/sonar-cloud.yml b/.github/workflows/sonar-cloud.yml index 8523a93a8..919fe493b 100644 --- a/.github/workflows/sonar-cloud.yml +++ b/.github/workflows/sonar-cloud.yml @@ -3,18 +3,13 @@ on: push: branches: - main - - release/**/* - - feat/* - - feat-* - - fix/* - - fix-* - - lw-* + - release/** pull_request: types: [opened, synchronize, reopened] jobs: sonarcloud: name: SonarCloud Code Analysis - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: From 9227b4c3114c7e47cc1ee30e0ac7222f282a96a8 Mon Sep 17 00:00:00 2001 From: John Oshalusi Date: Tue, 9 Apr 2024 14:43:41 +0100 Subject: [PATCH 40/74] feat: add search text box to tokens page (#986) * feat: add search text box to tokens page * feat: add empty search component * refactor: simplify asset portfolio component * test: fix asset portfolio test * feat: add search by ticker --- .../src/lib/translations/en.json | 2 + .../EmptySearch/EmptySearch.module.scss | 15 +++ .../components/EmptySearch/EmptySearch.tsx | 11 ++ .../components/EmptySearch/index.ts | 1 + .../views/browser-view/components/index.ts | 1 + .../features/assets/components/Assets.tsx | 1 + .../AssetsPortfolio/AssetPortfolioContent.tsx | 106 ++++++++++++++++++ .../AssetsPortfolio.module.scss | 6 + .../AssetsPortfolio/AssetsPortfolio.tsx | 38 +++---- .../__tests__/AssetsPortfolio.test.tsx | 1 - 10 files changed, 156 insertions(+), 26 deletions(-) create mode 100644 apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.module.scss create mode 100644 apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.tsx create mode 100644 apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/index.ts create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetPortfolioContent.tsx diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index a43e14b72..97a820c28 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -449,6 +449,8 @@ "browserView.assets.startYourWeb3Journey": "Start your Web3 journey. Just add some digital assets to get started.", "browserView.assets.totalWalletBalance": "Total wallet balance", "browserView.assets.portfolioBalanceToolTip": "Approximate value of all tokens with available fiat pair", + "browserView.assets.emptySearch": "No results matching your search", + "browserView.assets.searchPlaceholder": "Search by name, policy ID or fingerprint", "browserView.assetDetails.title": "Token detail", "browserView.assetDetails.price": "Price", "browserView.assetDetails.assetPrice": "Token price", diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.module.scss b/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.module.scss new file mode 100644 index 000000000..93f101c34 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.module.scss @@ -0,0 +1,15 @@ +@import '../../../../../../../packages/common/src/ui/styles/theme.scss'; + +.container { + align-items: center; + display: flex; + flex-direction: column; + justify-content: center; + margin: size_unit(5) 0; + width: 100%; + position: relative; +} + +.text { + color: var(--text-color-secondary); +} diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.tsx b/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.tsx new file mode 100644 index 000000000..9c4617072 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/EmptySearch.tsx @@ -0,0 +1,11 @@ +import React, { ReactElement } from 'react'; +import Empty from '@assets/icons/empty.svg'; +import { Image } from 'antd'; +import styles from './EmptySearch.module.scss'; + +export const EmptySearch = ({ text }: { text: string }): ReactElement => ( +
+ +
{text}
+
+); diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/index.ts b/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/index.ts new file mode 100644 index 000000000..97fb268c3 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/components/EmptySearch/index.ts @@ -0,0 +1 @@ +export { EmptySearch } from './EmptySearch'; diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/index.ts b/apps/browser-extension-wallet/src/views/browser-view/components/index.ts index 72eef31c3..c6d53f8f2 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/components/index.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/components/index.ts @@ -8,3 +8,4 @@ export * from './SideMenu'; export * from './WarningModal'; export * from './SocialNetworks'; export * from './WalletUsedAddressesDrawer'; +export * from './EmptySearch'; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/Assets.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/Assets.tsx index b8b0aa842..c3c0098a4 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/Assets.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/Assets.tsx @@ -281,6 +281,7 @@ export const Assets = ({ topSection }: AssetsProps): React.ReactElement => { totalAssets={fullAssetList?.length ?? 0} /> ); + const drawers = ( <> { + const fields = ['name', 'policyId', 'fingerprint', 'ticker'] as const; + const lowerSearchValue = searchValue.toLowerCase(); + + return data.filter((item) => + fields.some((field) => field in item && item[field] && item[field].toLowerCase().includes(lowerSearchValue)) + ); +}; + +interface AssetPortfolioContentProps { + assetList: IRow[]; + totalAssets: number; + isPortfolioBalanceLoading: boolean; + onRowClick: (id: string) => void; + onTableScroll: () => void; + isPopupView: boolean; +} + +export const AssetPortfolioContent = ({ + assetList, + totalAssets, + isPortfolioBalanceLoading, + onRowClick, + onTableScroll, + isPopupView +}: AssetPortfolioContentProps): ReactElement => { + const { t } = useTranslation(); + const [searchValue, setSearchValue] = useState(''); + const [currentAssets, setCurrentAssets] = useState<{ + data: IAssetDetails[]; + total: number; + }>({ + data: assetList, + total: totalAssets + }); + const { walletInfo } = useWalletStore(); + + useEffect(() => { + setCurrentAssets({ + data: searchValue ? currentAssets.data : assetList, + total: searchValue ? currentAssets.total : totalAssets + }); + }, [assetList, currentAssets.data, currentAssets.total, searchValue, totalAssets]); + + const handleSearch = useCallback( + (value: string) => { + const filteredAssets = searchTokens(assetList, value); + setSearchValue(value); + setCurrentAssets({ data: filteredAssets, total: filteredAssets.length }); + }, + [assetList] + ); + + return ( + <> + {assetList?.length > MIN_ASSETS_COUNT_FOR_SEARCH && ( +
+ setSearchValue('')} + /> +
+ )} + {searchValue && currentAssets.total === 0 && } + {!searchValue && currentAssets.total === 0 && ( + + )} + { + + {currentAssets.total > 0 && ( + + )} + + } + + ); +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.module.scss b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.module.scss index d3f02b8ea..a7a085239 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.module.scss +++ b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.module.scss @@ -13,3 +13,9 @@ width: 1000px; } } + +.searchBoxContainer { + @media (max-width: $breakpoint-popup) { + margin-top: size_unit(3); + } +} diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.tsx index f5e3bfcce..52747e13d 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/AssetsPortfolio.tsx @@ -2,12 +2,11 @@ import { Skeleton } from 'antd'; import dayjs from 'dayjs'; import React, { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { AssetTable, IRow, SendReceive } from '@lace/core'; -import { CONTENT_LAYOUT_ID } from '@components/Layout/ContentLayout'; +import { IRow, SendReceive } from '@lace/core'; import { SectionTitle } from '@components/Layout/SectionTitle'; -import { APP_MODE_POPUP, AppMode, LACE_APP_ID } from '@src/utils/constants'; +import { APP_MODE_POPUP, AppMode } from '@src/utils/constants'; import { compactNumberWithUnit } from '@src/utils/format-number'; -import { FundWalletBanner, PortfolioBalance } from '@src/views/browser-view/components'; +import { PortfolioBalance } from '@src/views/browser-view/components'; import { useCurrencyStore } from '@providers/currency'; import { useWalletStore } from '@src/stores'; import { useFetchCoinPrice } from '@hooks/useFetchCoinPrice'; @@ -18,6 +17,7 @@ import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; import styles from './AssetsPortfolio.module.scss'; import BigNumber from 'bignumber.js'; import { SendFlowTriggerPoints } from '../../../send-transaction'; +import { AssetPortfolioContent } from './AssetPortfolioContent'; const MINUTES_UNTIL_WARNING_BANNER = 3; @@ -49,7 +49,6 @@ export const AssetsPortfolio = ({ const analytics = useAnalyticsContext(); const { t } = useTranslation(); const { - walletInfo, walletUI: { canManageBalancesVisibility, areBalancesVisible } } = useWalletStore(); const { fiatCurrency } = useCurrencyStore(); @@ -105,7 +104,7 @@ export const AssetsPortfolio = ({ isBalanceVisible={areBalancesVisible || portfolioBalanceAsBigNumber.eq(0)} />
- {isPopupView && totalAssets > 0 && ( + {isPopupView && portfolioBalanceAsBigNumber.gt(0) && ( )} - - {portfolioBalanceAsBigNumber.gt(0) ? ( - - ) : ( - - )} - + ); }; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/__tests__/AssetsPortfolio.test.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/__tests__/AssetsPortfolio.test.tsx index b23966b12..909645f37 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/__tests__/AssetsPortfolio.test.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/assets/components/AssetsPortfolio/__tests__/AssetsPortfolio.test.tsx @@ -148,7 +148,6 @@ describe('AssetsPortfolio', () => { /> ); expect(queryByTestId('asset-table')).not.toBeInTheDocument(); - expect(queryByTestId('fund-wallet-banner-mock')).not.toBeInTheDocument(); expect(queryByTestId('section-title-counter')).toHaveTextContent('(0)'); }); From 0960c95b339791f2f2cd8a86e6779612412c5e60 Mon Sep 17 00:00:00 2001 From: Lukasz Jagiela <12641433+ljagiela@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:24:00 +0200 Subject: [PATCH 41/74] test(extension): test maintenance 8 April (#1047) --- packages/e2e-tests/src/assert/dAppConnectorAssert.ts | 3 +++ .../src/assert/multidelegation/StakePoolDetailsAssert.ts | 3 +++ packages/e2e-tests/src/assert/transactionDetailsAssert.ts | 1 + .../src/features/SendTransactionBundlesExtended.feature | 5 +++-- .../src/features/SendTransactionSimplePopup.part2.feature | 5 +++-- .../analytics/AnalyticsEventProperitesExtended.feature | 3 ++- packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts | 3 +++ 7 files changed, 18 insertions(+), 5 deletions(-) diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index 1fab9e989..44114e5ab 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -274,6 +274,9 @@ class DAppConnectorAssert { } async assertSeeConfirmTransactionPage({ assetsDetails, typeOfTransaction }: ExpectedTransactionData) { + // TODO + // remove when https://input-output.atlassian.net/browse/LW-9917 is fixed + await browser.pause(3000); await this.assertSeeHeader(); await ConfirmTransactionPage.transactionTypeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionTypeTitle.getText()).to.equal( diff --git a/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts b/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts index 500da2947..8614c9a69 100644 --- a/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts @@ -18,9 +18,12 @@ class StakePoolDetailsAssert { expect(await StakePoolDetails.informationDescription.getText()).to.equal(expectedStakedPool.information); if (noMetaDataPool) { + const shortPoolId = `${expectedStakedPool.poolId.slice(0, 6)}...${expectedStakedPool.poolId.slice(-8)}`; + expect(await StakePoolDetails.poolTicker.getText()).to.equal(shortPoolId); await StakePoolDetails.socialLinksTitle.waitForDisplayed({ reverse: true }); await StakePoolDetails.socialWebsiteIcon.waitForDisplayed({ reverse: true }); } else { + expect(await StakePoolDetails.poolTicker.getText()).to.equal(expectedStakedPool.ticker); expect(await StakePoolDetails.socialLinksTitle.getText()).to.equal(await t('drawer.details.social', 'staking')); await StakePoolDetails.socialWebsiteIcon.waitForDisplayed(); } diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index 26fada99a..a97a63f46 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -92,6 +92,7 @@ class TransactionsDetailsAssert { expectedTickers.push(pool.poolTicker); } + await TransactionDetailsPage.transactionDetails.waitForStable(); const actualIds: string[] = await TransactionDetailsPage.getTransactionDetailsStakepoolIds(); const actualNames: string[] = await TransactionDetailsPage.getTransactionDetailsStakepoolNames(); const actualTickers: string[] = await TransactionDetailsPage.getTransactionDetailsStakepoolTickers(); diff --git a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature index 6023d7fa2..105fff29e 100644 --- a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature +++ b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature @@ -214,8 +214,9 @@ Feature: Send - Extended Browser View (Advanced Tx) Scenario: Extended-view - send maximum amount of multiple assets by clicking MAX button When I click "Send" button on page header And I enter a valid "shelley" address in the bundle 1 recipient's address - And I click MAX button in bundle 1 for "tADA" asset - Then the maximum available amount is displayed in bundle: 1 for "tADA" asset +# disabled until "utxo fully depleted" error is fixed for MAX tADA +# And I click MAX button in bundle 1 for "tADA" asset +# Then the maximum available amount is displayed in bundle: 1 for "tADA" asset When I click "Add token or NFT" button for bundle 1 And click on an token with name: "LaceCoin" And I click MAX button in bundle 1 for "LaceCoin1" asset diff --git a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature index 48d3f4d98..a5a6cf33f 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature @@ -196,8 +196,9 @@ Feature: LW-484: Send & Receive - Popup View (Simple Tx) Scenario: Popup-view - send maximum amount of multiple assets by clicking MAX button When I click "Send" button on Tokens page in popup mode And I enter a valid "shelley" address in the bundle 1 recipient's address - And I click MAX button in bundle 1 for "tADA" asset - Then the maximum available amount is displayed in bundle: 1 for "tADA" asset +# disabled until "utxo fully depleted" error is fixed for MAX tADA +# And I click MAX button in bundle 1 for "tADA" asset +# Then the maximum available amount is displayed in bundle: 1 for "tADA" asset When I click "Add token or NFT" button for bundle 1 And click on an token with name: "LaceCoin" And I click MAX button in bundle 1 for "LaceCoin1" asset diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsEventProperitesExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsEventProperitesExtended.feature index 7cb049ece..b633ef4cd 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsEventProperitesExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsEventProperitesExtended.feature @@ -26,7 +26,8 @@ Feature: Analytics - Posthog - Event properties Then I validate that the event includes "trigger_point" property And I validate that the "send | send | click" event includes property "trigger_point" with value "nfts page" in posthog - @LW-8351 + @LW-8351 @Pending + @issue=LW-10242 Scenario: Analytics -Extended View - Verify event properties - Send - Send token Given I set up request interception for posthog analytics request(s) And I click token with name: "Cardano" diff --git a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts index 3cfd82989..15a03e6da 100644 --- a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts +++ b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts @@ -269,6 +269,9 @@ Then( async (valueToEnter: string, assetName: string, bundleIndex: number) => { assetName = assetName === 'tADA' && extensionUtils.isMainnet() ? 'ADA' : assetName; await TransactionNewPage.coinConfigure(bundleIndex, assetName).fillTokenValue(Number.parseFloat(valueToEnter)); + // workaround for test automation only to fire all events after finished typing + await TransactionNewPage.clickDrawerBackground(); + await TransactionNewPage.coinConfigure(bundleIndex, assetName).balanceFiatValueElement.click(); } ); From 481c9d89b8a89d28a387d7a7c69f815a9e1f768c Mon Sep 17 00:00:00 2001 From: Tom Mayel <143514860+tommayeliog@users.noreply.github.com> Date: Wed, 10 Apr 2024 09:24:19 +0100 Subject: [PATCH 42/74] fix(extension): settings-about swap Medium with Discord icons [LW-10180] (#1027) * fix(extension): settings-about swap Medium with Discort icons * test(extension): update LW-2387 --------- Co-authored-by: wklos-iohk --- apps/browser-extension-wallet/.env.defaults | 1 - apps/browser-extension-wallet/.env.developerpreview | 1 - apps/browser-extension-wallet/.env.example | 1 - .../src/assets/icons/discord-logo.component.svg | 3 +++ .../components/SocialNetworks/SocialNetworkIcon.tsx | 6 +++--- .../features/settings/components/SettingsAbout.tsx | 2 +- .../e2e-tests/src/assert/settings/SettingsPageAssert.ts | 4 ++-- .../src/elements/settings/extendedView/AboutLaceWidget.ts | 4 ++-- .../settings/extendedView/SocialComponentElement.ts | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) create mode 100644 apps/browser-extension-wallet/src/assets/icons/discord-logo.component.svg diff --git a/apps/browser-extension-wallet/.env.defaults b/apps/browser-extension-wallet/.env.defaults index 687e06644..ebb1fa3d7 100644 --- a/apps/browser-extension-wallet/.env.defaults +++ b/apps/browser-extension-wallet/.env.defaults @@ -38,7 +38,6 @@ CATALYST_GOOGLE_PLAY_URL=https://play.google.com/store/apps/details?id=io.iohk.v CATALYST_APP_STORE_URL=https://apps.apple.com/fr/app/catalyst-voting/id1517473397?l=en TWITTER_URL=https://twitter.com/lace_io YOUTUBE_URL=https://www.youtube.com/c/Laceio -MEDIUM_URL=https://medium.com/@lace_wallet GITHUB_URL=https://github.com/input-output-hk DISCORD_URL=https://discord.gg/lacewallet WEBSITE_URL=https://www.lace.io diff --git a/apps/browser-extension-wallet/.env.developerpreview b/apps/browser-extension-wallet/.env.developerpreview index 72d9c2e35..59bf74f4b 100644 --- a/apps/browser-extension-wallet/.env.developerpreview +++ b/apps/browser-extension-wallet/.env.developerpreview @@ -38,7 +38,6 @@ CATALYST_GOOGLE_PLAY_URL=https://play.google.com/store/apps/details?id=io.iohk.v CATALYST_APP_STORE_URL=https://apps.apple.com/fr/app/catalyst-voting/id1517473397?l=en TWITTER_URL=https://twitter.com/lace_io YOUTUBE_URL=https://www.youtube.com/c/Laceio -MEDIUM_URL=https://medium.com/@lace_wallet GITHUB_URL=https://github.com/input-output-hk DISCORD_URL=https://discord.gg/lacewallet WEBSITE_URL=https://www.lace.io diff --git a/apps/browser-extension-wallet/.env.example b/apps/browser-extension-wallet/.env.example index 38bf0387a..ec2b95fa7 100644 --- a/apps/browser-extension-wallet/.env.example +++ b/apps/browser-extension-wallet/.env.example @@ -34,7 +34,6 @@ CATALYST_GOOGLE_PLAY_URL=https://play.google.com/store/apps/details?id=io.iohk.v CATALYST_APP_STORE_URL=https://apps.apple.com/fr/app/catalyst-voting/id1517473397?l=en TWITTER_URL=https://twitter.com/lace_io YOUTUBE_URL=https://www.youtube.com/c/Laceio -MEDIUM_URL=https://medium.com/@lace_wallet GITHUB_URL=https://github.com/input-output-hk DISCORD_URL=https://discord.gg/inputoutput WEBSITE_URL=https://www.lace.io diff --git a/apps/browser-extension-wallet/src/assets/icons/discord-logo.component.svg b/apps/browser-extension-wallet/src/assets/icons/discord-logo.component.svg new file mode 100644 index 000000000..5eda88758 --- /dev/null +++ b/apps/browser-extension-wallet/src/assets/icons/discord-logo.component.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/browser-extension-wallet/src/views/browser-view/components/SocialNetworks/SocialNetworkIcon.tsx b/apps/browser-extension-wallet/src/views/browser-view/components/SocialNetworks/SocialNetworkIcon.tsx index ec1a277f1..07c4aebd4 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/components/SocialNetworks/SocialNetworkIcon.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/components/SocialNetworks/SocialNetworkIcon.tsx @@ -9,7 +9,7 @@ import Telegram from '../../../../assets/icons/telegram-icon.component.svg'; import GitHub from '../../../../assets/icons/github-icon.component.svg'; import Twitter from '../../../../assets/icons/twitter-icon.component.svg'; import Youtube from '../../../../assets/icons/youtube-icon.component.svg'; -import Medium from '../../../../assets/icons/medium-icon.component.svg'; +import Discord from '../../../../assets/icons/discord-logo.component.svg'; const normalizeHref = (href: string) => href && !href.startsWith('https://') ? `https://${href.replace(/^\/+/, '')}` : href; @@ -22,7 +22,7 @@ export enum SocialNetwork { GITHUB, TWITTER, YOUTUBE, - MEDIUM + DISCORD } export const iconsMap: Record>> = { @@ -33,7 +33,7 @@ export const iconsMap: Record Date: Wed, 10 Apr 2024 11:26:37 +0200 Subject: [PATCH 43/74] test(extension): fix from to section selectors and invalid step (#1049) * test(extension): fix from to section selectors and invalid step * test(extension): remove unnecessary check Signed-off-by: Lukasz Jagiela --------- Signed-off-by: Lukasz Jagiela Co-authored-by: Lukasz Jagiela --- .../src/assert/multidelegation/StakePoolDetailsAssert.ts | 1 - .../src/elements/dappConnector/confirmTransactionPage.ts | 4 ++-- .../src/features/analytics/AnalyticsSendExtended.feature | 3 ++- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts b/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts index 8614c9a69..12e38a252 100644 --- a/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/multidelegation/StakePoolDetailsAssert.ts @@ -14,7 +14,6 @@ class StakePoolDetailsAssert { await this.assertSeeStakePoolDetailsCommonElements(); expect(await StakePoolDetails.poolName.getText()).to.equal(expectedStakedPool.name); - expect(await StakePoolDetails.poolTicker.getText()).to.equal(expectedStakedPool.ticker); expect(await StakePoolDetails.informationDescription.getText()).to.equal(expectedStakedPool.information); if (noMetaDataPool) { diff --git a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts index 46ef3468d..771953832 100644 --- a/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts +++ b/packages/e2e-tests/src/elements/dappConnector/confirmTransactionPage.ts @@ -111,12 +111,12 @@ class ConfirmTransactionPage extends CommonDappPageElements { } async getAssetsFromAddressSection() { - const textArray = await getTextFromElementArray(await this.transactionToAssetsRows); + const textArray = await getTextFromElementArray(await this.transactionFromAssetsRows); return textArray.map((str) => str.replace(/\n/g, ' ')); } async getAssetsToAddressSection() { - const textArray = await getTextFromElementArray(await this.transactionFromAssetsRows); + const textArray = await getTextFromElementArray(await this.transactionToAssetsRows); return textArray.map((str) => str.replace(/\n/g, ' ')); } diff --git a/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature b/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature index 5c8a99065..98e4f7048 100644 --- a/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature +++ b/packages/e2e-tests/src/features/analytics/AnalyticsSendExtended.feature @@ -42,7 +42,8 @@ Feature: Analytics - Posthog - Sending - Extended View And I open and authorize test DApp with "Only once" setting And I set send to wallet address to: "WalletAnalyticsReceiveSimpleTransactionE2E" in test DApp And I click "Send ADA" "Run" button in test DApp - And I see DApp connector "Confirm transaction" page with: "-3.00" tADA - fee, "0" assets and receiving wallet "WalletAnalyticsReceiveSimpleTransactionE2E" + Then I see DApp connector "Confirm transaction" page with all UI elements and with following data in "Transaction Summary" section: + | -3.00 tADA - FEE | And I set up request interception for posthog analytics request(s) When I click "Confirm" button on "Confirm transaction" page Then I validate latest analytics single event "send | transaction summary | confirm | click" From cf8cf369d3ca24494a3b7abc3611c3626b561ac8 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Wed, 10 Apr 2024 13:26:22 +0300 Subject: [PATCH 44/74] fix(extension): [LW-9917] fix dapp connector translations (#1046) --- .../src/lib/translations/en.json | 10 ++++++++++ packages/e2e-tests/src/assert/dAppConnectorAssert.ts | 3 --- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 97a820c28..06bcf0320 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -180,6 +180,16 @@ "core.dappTransaction.quantity": "Quantity", "core.dappTransaction.send": "Send", "core.dappTransaction.sending": "Sending", + "core.dappTransaction.transactionSummary": "Transaction Summary", + "core.dappTransaction.origin": "Origin", + "core.dappTransaction.fromAddress": "From address", + "core.dappTransaction.toAddress": "To address", + "core.dappTransaction.tokens": "Tokens", + "core.dappTransaction.nfts": "NFTs", + "core.dappTransaction.address": "Address", + "core.dappTransaction.deposit": "Deposit", + "core.dappTransaction.returnedDeposit": "Returned deposit", + "core.dappTransaction.items": "item(s)", "tab.main.title": "Tab extension", "general.buttons.back": "Back", "general.button.cancel": "Cancel", diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index 44114e5ab..1fab9e989 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -274,9 +274,6 @@ class DAppConnectorAssert { } async assertSeeConfirmTransactionPage({ assetsDetails, typeOfTransaction }: ExpectedTransactionData) { - // TODO - // remove when https://input-output.atlassian.net/browse/LW-9917 is fixed - await browser.pause(3000); await this.assertSeeHeader(); await ConfirmTransactionPage.transactionTypeTitle.waitForDisplayed(); expect(await ConfirmTransactionPage.transactionTypeTitle.getText()).to.equal( From d444b4e7968184c623b48dfff923eda19a1f6d5f Mon Sep 17 00:00:00 2001 From: Daniel Main Date: Wed, 10 Apr 2024 12:29:57 +0200 Subject: [PATCH 45/74] LW-10255 - update text for term and privacy policy (#1050) * chore: updated text for term and privacy policy in the first screen of the onboarding * fix(extension): changed horizontal margin to text fits in one line --- .../WalletSetupRevamp/WalletSetupOptionsStepRevamp.module.scss | 2 ++ .../WalletSetupRevamp/WalletSetupOptionsStepRevamp.tsx | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.module.scss b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.module.scss index e20a97550..7b1039eba 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.module.scss +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.module.scss @@ -53,4 +53,6 @@ .legal { margin-top: size_unit(2); text-align: center; + margin-left: -5px; + margin-right: -5px; } diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.tsx index 111215a76..101a04647 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupOptionsStepRevamp.tsx @@ -66,7 +66,7 @@ export const WalletSetupOptionsStepRevamp = ({
- By proceeding you agree to Lace’s{' '} + By clicking the Create, Connect or Restore button above, you agree with Lace’s{' '} Terms of Service {' '} From 50a6aa0a65c7bc78b9b08a2ec17e5bf38954475a Mon Sep 17 00:00:00 2001 From: Tom Mayel <143514860+tommayeliog@users.noreply.github.com> Date: Wed, 10 Apr 2024 13:15:17 +0100 Subject: [PATCH 46/74] feat(staking): add default sorting for browse pools [LW-10104] (#985) * feat(staking): add default sorting for browse pools * feat(staking): browse pools - add default sorting for hardware wallet legacy --- .../StakePoolsTable/StakePoolsTable.tsx | 12 ++-- packages/staking/package.json | 1 + .../BrowsePoolsPreferencesCard.stories.tsx | 72 ++++++++++++++++--- .../BrowsePoolsPreferencesCard.tsx | 17 ++++- .../StakePoolsList/StakePoolsListHeader.tsx | 4 +- .../__tests__/defaultSortOrderByField.test.ts | 19 +++++ .../src/features/BrowsePools/constants.ts | 3 +- .../staking/src/features/BrowsePools/index.ts | 2 +- .../staking/src/features/BrowsePools/utils.ts | 5 +- packages/staking/src/index.ts | 2 + .../radio-button/radio-button.component.tsx | 9 +-- yarn.lock | 65 +++++++++++++++++ 12 files changed, 184 insertions(+), 27 deletions(-) create mode 100644 packages/staking/src/features/BrowsePools/__tests__/defaultSortOrderByField.test.ts diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx index 626fa89dd..b3992ae2f 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakePoolsTable/StakePoolsTable.tsx @@ -9,7 +9,9 @@ import { StakePoolsListRowSkeleton, StakePoolSortOptions, stakePoolTableConfig, - TranslationsFor + TranslationsFor, + getDefaultSortOrderByField, + DEFAULT_SORT_OPTIONS } from '@lace/staking'; import { Typography } from 'antd'; import { Search } from '@lace/common'; @@ -31,11 +33,6 @@ type stakePoolsTableProps = { type LoadMoreDataParam = Parameters[0]['loadMoreData']; -const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { - field: 'ticker', - order: 'desc' -}; - const searchDebounce = 300; export const StakePoolsTable = ({ scrollableTargetId }: stakePoolsTableProps): React.ReactElement => { @@ -109,7 +106,8 @@ export const StakePoolsTable = ({ scrollableTargetId }: stakePoolsTableProps): R }; const onSortChange = (sortField: SortField) => { - const order = sortField === sort?.field && sort?.order === 'asc' ? 'desc' : 'asc'; + const inverseOrder = sort?.order === 'asc' ? 'desc' : 'asc'; + const order = sortField !== sort?.field ? getDefaultSortOrderByField(sortField) : inverseOrder; setSort({ field: sortField, order }); }; diff --git a/packages/staking/package.json b/packages/staking/package.json index d1147173e..334a12ce9 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -81,6 +81,7 @@ "@storybook/addon-links": "^7.6.7", "@storybook/blocks": "^7.6.7", "@storybook/jest": "^0.2.3", + "@storybook/preview-api": "^8.0.4", "@storybook/react": "^7.6.7", "@storybook/react-vite": "^7.6.7", "@storybook/test": "^7.6.7", diff --git a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx index 05bdfe479..18576f082 100644 --- a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx +++ b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.stories.tsx @@ -1,23 +1,23 @@ import { Box, Cell, Flex, Grid, LocalThemeProvider, Section, ThemeColorScheme, Variants } from '@lace/ui'; import { action } from '@storybook/addon-actions'; -import { StakePoolSortOptions } from 'features/BrowsePools'; +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 { Meta } from '@storybook/react'; +import type { Meta, StoryObj } from '@storybook/react'; import { PoolsFilter, QueryStakePoolsFilters } from '../../store'; import { BrowsePoolsPreferencesCard } from './BrowsePoolsPreferencesCard'; import { SortAndFilterTab } from './types'; -export default { +const meta: Meta = { + component: BrowsePoolsPreferencesCard, title: 'Cards/Stake Pool Sorting & Filter', -} as Meta; - +}; +export default meta; const Wrapper = ({ defaultTab }: { defaultTab: SortAndFilterTab }) => { const [activeTab, setActiveTab] = useState(defaultTab); - const [sort, setSort] = useState({ - field: 'saturation', - order: 'asc', - }); + const [sort, setSort] = useState(DEFAULT_SORT_OPTIONS); const [filter, setFilter] = useState({ [PoolsFilter.Saturation]: ['', ''], [PoolsFilter.ProfitMargin]: ['', ''], @@ -109,3 +109,57 @@ export const Overview = (): JSX.Element => ( ); + +export const Interactions: StoryObj = { + args: { + activeTab: SortAndFilterTab.sort, + filter: { + [PoolsFilter.Saturation]: ['', ''], + [PoolsFilter.ProfitMargin]: ['', ''], + [PoolsFilter.Performance]: ['', ''], + [PoolsFilter.Ros]: ['lastepoch'], + }, + sort: { + field: 'pledge', + order: 'desc', + }, + }, + play: async ({ canvasElement, step }) => { + const canvas = within(canvasElement); + await waitFor(() => expect(canvas.getByTestId('radio-btn-test-id-ticker'))); + const tickerBtn = canvas.getByTestId('radio-btn-test-id-ticker'); + + await userEvent.click(tickerBtn); + const saturationBtn = canvas.getByTestId('radio-btn-test-id-saturation'); + await step('Change sort field', async () => { + await userEvent.click(saturationBtn); + }); + + const iconDesc = canvas.getByTestId('sort-desc'); + await waitFor(() => expect(saturationBtn).toBeChecked()); + await waitFor(() => expect(iconDesc).toBeInTheDocument()); + await step('Click on iconDesc', async () => { + await userEvent.click(iconDesc); + }); + + await waitFor(() => expect(iconDesc).not.toBeInTheDocument()); + const iconAsc = canvas.getByTestId('sort-asc'); + await waitFor(() => expect(iconAsc).toBeInTheDocument()); + }, + render: function Render(args) { + const [{ sort }, setArgs] = useArgs(); + + return ( + { + setArgs({ + ...args, + sort: newSort, + }); + }} + /> + ); + }, +}; diff --git a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx index 80a459a83..db62eca50 100644 --- a/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx +++ b/packages/staking/src/features/BrowsePools/BrowsePoolsPreferencesCard/BrowsePoolsPreferencesCard.tsx @@ -21,6 +21,7 @@ import { useCallback, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { USE_MULTI_DELEGATION_STAKING_FILTERS, USE_ROS_STAKING_COLUMN } from '../../../featureFlags'; import { PoolsFilter, QueryStakePoolsFilters } from '../../store'; +import { getDefaultSortOrderByField } from '../utils'; import * as styles from './BrowsePoolsPreferencesCard.css'; import { BrowsePoolsPreferencesCardLabel } from './BrowsePoolsPreferencesCardLabel'; import { FilterOption, SelectOption, SortAndFilterTab } from './types'; @@ -89,7 +90,7 @@ export const BrowsePoolsPreferencesCard = ({ onSortChange({ field: sortField, - order: 'asc', + order: getDefaultSortOrderByField(sortField), }); }, [analytics, onSortChange] @@ -135,8 +136,18 @@ export const BrowsePoolsPreferencesCard = ({ }; const sortingOptions: RadioButtonGroupOption[] = useMemo(() => { - const iconAlphabetical = direction === 'asc' ? SortAlphabeticalAscIcon : SortAlphabeticalDescIcon; - const iconNumerical = direction === 'asc' ? SortNumericalAscIcon : SortNumericalDescIcon; + const iconAlphabetical = + direction === 'asc' ? ( + + ) : ( + + ); + const iconNumerical = + direction === 'asc' ? ( + + ) : ( + + ); return [ { icon: iconAlphabetical, diff --git a/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolsListHeader.tsx b/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolsListHeader.tsx index 60e9d79df..a16120b8c 100644 --- a/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolsListHeader.tsx +++ b/packages/staking/src/features/BrowsePools/StakePoolsList/StakePoolsListHeader.tsx @@ -4,6 +4,7 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { useOutsideHandles } from '../../outside-handles-provider'; import { analyticsActionsMap } from '../analytics'; +import { getDefaultSortOrderByField } from '../utils'; import { config } from './config'; export interface TableHeaders { @@ -66,7 +67,8 @@ export const StakePoolsListHeader = ({ setActiveSort, activeSort }: StakePoolsLi })); const onSortChange = (field: SortField) => { - const order = field === activeSort?.field && activeSort?.order === 'asc' ? 'desc' : 'asc'; + const inverseOrder = activeSort?.order === 'asc' ? 'desc' : 'asc'; + const order = field !== activeSort?.field ? getDefaultSortOrderByField(field) : inverseOrder; analytics.sendEventToPostHog(analyticsActionsMap[field]); setActiveSort({ field, order }); diff --git a/packages/staking/src/features/BrowsePools/__tests__/defaultSortOrderByField.test.ts b/packages/staking/src/features/BrowsePools/__tests__/defaultSortOrderByField.test.ts new file mode 100644 index 000000000..7541f64d3 --- /dev/null +++ b/packages/staking/src/features/BrowsePools/__tests__/defaultSortOrderByField.test.ts @@ -0,0 +1,19 @@ +/* eslint-disable unicorn/no-useless-undefined */ +import { getDefaultSortOrderByField } from '../utils'; + +describe('getDefaultSortOrderByField', () => { + it('returns asc', () => { + const tickerOrder = getDefaultSortOrderByField('ticker'); + const costOrder = getDefaultSortOrderByField('cost'); + const marginOrder = getDefaultSortOrderByField('margin'); + + expect(tickerOrder).toEqual('asc'); + expect(costOrder).toEqual('asc'); + expect(marginOrder).toEqual('asc'); + }); + + it('returns desc', () => { + const pledgeOrder = getDefaultSortOrderByField('pledge'); + expect(pledgeOrder).toEqual('desc'); + }); +}); diff --git a/packages/staking/src/features/BrowsePools/constants.ts b/packages/staking/src/features/BrowsePools/constants.ts index be5b99c4b..fee9816f3 100644 --- a/packages/staking/src/features/BrowsePools/constants.ts +++ b/packages/staking/src/features/BrowsePools/constants.ts @@ -1,10 +1,11 @@ import { BrowsePoolsView, StakePoolSortOptions } from './types'; +import { getDefaultSortOrderByField } from './utils'; export const SEARCH_DEBOUNCE_IN_MS = 300; export const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { field: 'ticker', - order: 'asc', + order: getDefaultSortOrderByField('ticker'), }; export const DEFAULT_BROWSE_POOLS_VIEW: BrowsePoolsView = BrowsePoolsView.grid; diff --git a/packages/staking/src/features/BrowsePools/index.ts b/packages/staking/src/features/BrowsePools/index.ts index d32c0715b..7c91737bb 100644 --- a/packages/staking/src/features/BrowsePools/index.ts +++ b/packages/staking/src/features/BrowsePools/index.ts @@ -5,7 +5,7 @@ export { useBrowsePoolsPersistence } from './hooks'; export { DEFAULT_SORT_OPTIONS } from './constants'; // TODO: remove once multi delegation feature is GA'd -export { getSaturationLevel, isOversaturated } from './utils'; +export { getSaturationLevel, isOversaturated, getDefaultSortOrderByField } from './utils'; export type { StakePoolSortOptions, TranslationsFor, SortField, SortOrder } from './types'; export { BrowsePoolsView } from './types'; export { StakePoolCardProgressBar } from './StakePoolCard'; diff --git a/packages/staking/src/features/BrowsePools/utils.ts b/packages/staking/src/features/BrowsePools/utils.ts index 790885c9d..1ed498b65 100644 --- a/packages/staking/src/features/BrowsePools/utils.ts +++ b/packages/staking/src/features/BrowsePools/utils.ts @@ -1,5 +1,5 @@ import inRange from 'lodash/inRange'; -import { SaturationLevels } from './types'; +import { SaturationLevels, SortField, SortOrder } from './types'; const mediumUpperBound = 90; const highUpperBound = 95; @@ -28,3 +28,6 @@ export const getSaturationLevel = (saturation: number): SaturationLevels => { } return SaturationLevels.Medium; }; + +export const getDefaultSortOrderByField = (field: SortField): SortOrder => + ['ticker', 'cost', 'margin'].includes(field) ? 'asc' : 'desc'; diff --git a/packages/staking/src/index.ts b/packages/staking/src/index.ts index 1c3003125..1bd7a9566 100644 --- a/packages/staking/src/index.ts +++ b/packages/staking/src/index.ts @@ -16,6 +16,8 @@ export { StakePoolsListRowSkeleton, getSaturationLevel, isOversaturated, + getDefaultSortOrderByField, + DEFAULT_SORT_OPTIONS, } from './features/BrowsePools'; export { mapStakePoolToDisplayData } from './features/store'; /* eslint-enable import/export */ diff --git a/packages/ui/src/design-system/radio-button/radio-button.component.tsx b/packages/ui/src/design-system/radio-button/radio-button.component.tsx index b500ffd1e..41fe988bc 100644 --- a/packages/ui/src/design-system/radio-button/radio-button.component.tsx +++ b/packages/ui/src/design-system/radio-button/radio-button.component.tsx @@ -11,7 +11,7 @@ import * as cx from './radio-button.css'; export interface RadioButtonGroupOption { value: string; label: React.ReactNode; - icon?: React.ComponentType>; + icon?: JSX.Element; onIconClick?: () => void; tooltipText?: string; } @@ -41,7 +41,7 @@ export const RadioButtonGroup = ({ onValueChange={onValueChange} className={cx.radioGroupRoot} > - {options.map(({ value, label, icon: Icon, onIconClick }) => { + {options.map(({ value, label, icon, onIconClick }) => { const hasLabel = Boolean(label); return ( @@ -60,6 +60,7 @@ export const RadioButtonGroup = ({ id={`radio-btn-control-id-${value}`} value={value} className={cx.radioGroupIndicatorWrapper} + data-testid={`radio-btn-test-id-${value}`} > )} - {Icon !== undefined && value === selectedValue && ( + {icon !== undefined && value === selectedValue && ( )} diff --git a/yarn.lock b/yarn.lock index 870c07ed8..b3a953335 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10833,6 +10833,7 @@ __metadata: "@storybook/addon-links": ^7.6.7 "@storybook/blocks": ^7.6.7 "@storybook/jest": ^0.2.3 + "@storybook/preview-api": ^8.0.4 "@storybook/react": ^7.6.7 "@storybook/react-vite": ^7.6.7 "@storybook/test": ^7.6.7 @@ -14792,6 +14793,19 @@ __metadata: languageName: node linkType: hard +"@storybook/channels@npm:8.0.5": + version: 8.0.5 + resolution: "@storybook/channels@npm:8.0.5" + dependencies: + "@storybook/client-logger": 8.0.5 + "@storybook/core-events": 8.0.5 + "@storybook/global": ^5.0.0 + telejson: ^7.2.0 + tiny-invariant: ^1.3.1 + checksum: d17bee03ee26c6fe635da70b13d631f344803deebd238639508fd0399fcb1f650c4aa7a16ac2e57916b492ad632413e68e4e0745cd4d49708845a0604231251e + languageName: node + linkType: hard + "@storybook/cli@npm:7.4.3": version: 7.4.3 resolution: "@storybook/cli@npm:7.4.3" @@ -15044,6 +15058,15 @@ __metadata: languageName: node linkType: hard +"@storybook/client-logger@npm:8.0.5": + version: 8.0.5 + resolution: "@storybook/client-logger@npm:8.0.5" + dependencies: + "@storybook/global": ^5.0.0 + checksum: 1deae606ed148268806db2c1bdea7e3514480b5e3be2b2e99d5f7b09c2ae2a0c6f759540e708aea1a1a6084204a4bace3996fe4f8ba4e6de330a62ddf735cd96 + languageName: node + linkType: hard + "@storybook/codemod@npm:7.4.3": version: 7.4.3 resolution: "@storybook/codemod@npm:7.4.3" @@ -15610,6 +15633,15 @@ __metadata: languageName: node linkType: hard +"@storybook/core-events@npm:8.0.5": + version: 8.0.5 + resolution: "@storybook/core-events@npm:8.0.5" + dependencies: + ts-dedent: ^2.0.0 + checksum: d21293b760f16ce63ccbf6829213de5b7cdd54cb495a23a0311ae2edd388336e0fc6752f92f6c79115435a156258516d0ff5cd658189ca18a2b2cdd3382684c1 + languageName: node + linkType: hard + "@storybook/core-server@npm:6.5.10": version: 6.5.10 resolution: "@storybook/core-server@npm:6.5.10" @@ -16557,6 +16589,28 @@ __metadata: languageName: node linkType: hard +"@storybook/preview-api@npm:^8.0.4": + version: 8.0.5 + resolution: "@storybook/preview-api@npm:8.0.5" + dependencies: + "@storybook/channels": 8.0.5 + "@storybook/client-logger": 8.0.5 + "@storybook/core-events": 8.0.5 + "@storybook/csf": ^0.1.2 + "@storybook/global": ^5.0.0 + "@storybook/types": 8.0.5 + "@types/qs": ^6.9.5 + dequal: ^2.0.2 + lodash: ^4.17.21 + memoizerific: ^1.11.3 + qs: ^6.10.0 + tiny-invariant: ^1.3.1 + ts-dedent: ^2.0.0 + util-deprecate: ^1.0.2 + checksum: f765258498cca244f058338ea3cf5eaf180d5769c83437a6e3a43aab1743dcb1c990a128a96d62332e264b286a1be24054c1a9ab6962fd2ba2824374707ef1cf + languageName: node + linkType: hard + "@storybook/preview-web@npm:6.5.10": version: 6.5.10 resolution: "@storybook/preview-web@npm:6.5.10" @@ -17390,6 +17444,17 @@ __metadata: languageName: node linkType: hard +"@storybook/types@npm:8.0.5": + version: 8.0.5 + resolution: "@storybook/types@npm:8.0.5" + dependencies: + "@storybook/channels": 8.0.5 + "@types/express": ^4.7.0 + file-system-cache: 2.3.0 + checksum: 6df50d1b324b8da2698eb822a7b0997fde2b267b3c9318939f22d6685bd7ee8b2d765434682651d81561ae11f75650a57082d0572d616836470c6e790823023d + languageName: node + linkType: hard + "@storybook/ui@npm:6.5.10": version: 6.5.10 resolution: "@storybook/ui@npm:6.5.10" From 838bd7708265a6eebf1a2be2148319f272f5b490 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Wed, 10 Apr 2024 15:25:25 +0200 Subject: [PATCH 47/74] fix(extension): incorrect action button width in dapp connector LW-10232 (#1052) --- .../confirm-transaction/ConfirmTransaction.module.scss | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss index a884c336c..9e5da7e97 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/ConfirmTransaction.module.scss @@ -6,7 +6,7 @@ padding-top: size_unit(0); } -.transactionContainer { +.transactionContainer { display: flex; flex-direction: column; flex: 1; @@ -15,13 +15,13 @@ .actions { @extend %flex-column; - height: size_unit(17.12); justify-content: space-between; - padding: size_unit(2) size_unit(3) size_unit(2) size_unit(3); + padding: size_unit(3) 0; border-top: 1px solid var(--light-mode-light-grey-plus, var(--dark-mode-mid-grey)); position: sticky; bottom: 0; z-index: 10; + gap: size_unit(1); background-color: var(--light-mode-body, var(--dark-mode-bg-black)); .actionBtn { width: 100%; From fd66963615f92b78f194b7f044008375a455964b Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Wed, 10 Apr 2024 19:53:59 +0200 Subject: [PATCH 48/74] chore(extension): update lace version v1.10.1 (#1056) --- apps/browser-extension-wallet/manifest.json | 2 +- apps/browser-extension-wallet/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser-extension-wallet/manifest.json b/apps/browser-extension-wallet/manifest.json index 2b671e9de..e85486290 100644 --- a/apps/browser-extension-wallet/manifest.json +++ b/apps/browser-extension-wallet/manifest.json @@ -1,7 +1,7 @@ { "name": "$WALLET_MANIFEST_NAME", "description": "One fast, accessible, and secure platform for digital assets, DApps, NFTs, and DeFi.", - "version": "1.10.0", + "version": "1.10.1", "manifest_version": 3, "key": "$LACE_EXTENSION_KEY", "icons": { diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 5da2607af..432ea0dab 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@lace/browser-extension-wallet", - "version": "1.10.0", + "version": "1.10.1", "description": "A fully capable wallet packaged as browser extensions for Chrome, Firefox, and Edge", "homepage": "https://github.com/input-output-hk/lace/blob/master/apps/browser-extension-wallet/README.md", "bugs": { From bb582f40a71168f779f54261b6184bb722dbfc38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Thu, 11 Apr 2024 12:56:15 +0200 Subject: [PATCH 49/74] test(extension): update tests for LW-10255 (#1060) --- .../src/assert/onboarding/onboardingMainPageAssert.ts | 2 +- .../e2e-tests/src/features/OnboardingCreateWallet.feature | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/e2e-tests/src/assert/onboarding/onboardingMainPageAssert.ts b/packages/e2e-tests/src/assert/onboarding/onboardingMainPageAssert.ts index 5c070fd6e..77eaccb6f 100644 --- a/packages/e2e-tests/src/assert/onboarding/onboardingMainPageAssert.ts +++ b/packages/e2e-tests/src/assert/onboarding/onboardingMainPageAssert.ts @@ -13,7 +13,7 @@ class OnboardingMainPageAssert extends OnboardingCommonAssert { async assertSeeAgreementText() { await OnboardingMainPage.agreementText.waitForDisplayed(); expect(await OnboardingMainPage.agreementText.getText()).to.equal( - 'By proceeding you agree to Lace’s Terms of Service and Privacy Policy' + 'By clicking the Create, Connect or Restore button above, you agree with Lace’s Terms of Service and Privacy Policy' ); } diff --git a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature index 137fac069..6327978ad 100755 --- a/packages/e2e-tests/src/features/OnboardingCreateWallet.feature +++ b/packages/e2e-tests/src/features/OnboardingCreateWallet.feature @@ -37,12 +37,12 @@ Feature: Onboarding - Create wallet When I enter wallet name: "wallet", password: "" and password confirmation: "" Then Password recommendation: "", complexity bar level: "" and password confirmation error: "" are displayed Examples: - | password | password_conf | passw_err | complex_bar_lvl | passw_conf_err | + | password | password_conf | passw_err | complex_bar_lvl | passw_conf_err | | a | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | | P@ss | | core.walletNameAndPasswordSetupStep.firstLevelPasswordStrengthFeedback | 1 | empty | | N_8J@bne | | core.walletNameAndPasswordSetupStep.secondLevelPasswordStrengthFeedback | 2 | empty | - | N_8J@bne87 | | empty | 3 | empty | - | N_8J@bne87A | N_8J@bne87 | empty | 4 | core.walletSetupRegisterStep.noMatchPassword | + | N_8J@bne87 | | empty | 3 | empty | + | N_8J@bne87A | N_8J@bne87 | empty | 4 | core.walletSetupRegisterStep.noMatchPassword | @LW-3013 Scenario: Create Wallet - Mnemonic writedown page - appears correctly after wallet setup page From f37d85e5234007a6fa6dd807048a77e6dc2dc14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Mas=C5=82owski?= Date: Thu, 11 Apr 2024 16:14:26 +0200 Subject: [PATCH 50/74] chore(all): disable promise/avoid-new eslint rule (#1062) --- .eslintrc.js | 5 ++--- .../src/features/dapp/components/Connect.tsx | 1 - .../hooks/__tests__/useBuildDelegation.test.ts | 1 - .../src/lib/scripts/background/util.ts | 1 - .../src/utils/__tests__/createQueue.test.ts | 1 - .../src/utils/mocks/fake-api-request.ts | 1 - .../src/utils/taskQueue.ts | 1 - .../__tests__/get-block-info-by-hash.test.ts | 1 - .../lib/__tests__/get-inputs-value.test.ts | 1 - .../wallet/test/mocks/AssetsProviderStub.ts | 18 ++++++++---------- .../wallet/test/mocks/TxSubmitProviderFake.ts | 1 - packages/common/src/ui/hooks/useFetchImage.ts | 1 - .../AddCoSigners/AddCoSigners.stories.tsx | 11 ++++++----- 13 files changed, 16 insertions(+), 28 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b0f687c23..4efee2f60 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -30,11 +30,10 @@ module.exports = { 'no-invalid-this': 0, 'react/prop-types': 'off', 'max-len': 'off', // prettier is already handling this automatically, - // note: prod webpack config strips console logs anyway, nevertheless - // we don't want the dev build to be spammed by needless logging '@typescript-eslint/no-explicit-any': ['error'], 'no-console': ['error', { allow: ['warn', 'error', 'info', 'debug'] }], - 'lodash/import-scope': ['error', 'method'] + 'lodash/import-scope': ['error', 'method'], + 'promise/avoid-new': 'off', }, overrides: [ { diff --git a/apps/browser-extension-wallet/src/features/dapp/components/Connect.tsx b/apps/browser-extension-wallet/src/features/dapp/components/Connect.tsx index fd2e18014..bbc8150db 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/Connect.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/Connect.tsx @@ -67,7 +67,6 @@ const NonSSLBanner = () => { const authorize = (authorization: 'deny' | 'just-once' | 'allow', url: string) => { const api$ = of({ allowOrigin(origin: cip30.Origin): Promise<'deny' | 'just-once' | 'allow'> { - /* eslint-disable-next-line promise/avoid-new */ if (!url.startsWith(origin)) { return Promise.reject(); } diff --git a/apps/browser-extension-wallet/src/hooks/__tests__/useBuildDelegation.test.ts b/apps/browser-extension-wallet/src/hooks/__tests__/useBuildDelegation.test.ts index 8e1dfc48a..e6d8a4540 100644 --- a/apps/browser-extension-wallet/src/hooks/__tests__/useBuildDelegation.test.ts +++ b/apps/browser-extension-wallet/src/hooks/__tests__/useBuildDelegation.test.ts @@ -49,7 +49,6 @@ jest.mock('../../stores', () => ({ }) })); -// eslint-disable-next-line promise/avoid-new const flushPromises = () => new Promise(setImmediate); describe('Testing useBuildDelegation hook', () => { diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/util.ts b/apps/browser-extension-wallet/src/lib/scripts/background/util.ts index a317f75f5..1579e512a 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/util.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/util.ts @@ -86,7 +86,6 @@ export const launchCip30Popup = async (url: string, windowType: Windows.CreateTy }; const waitForTabLoad = (tab: Tabs.Tab) => - // eslint-disable-next-line promise/avoid-new new Promise((resolve) => { const listener = (tabId: number, changeInfo: Tabs.OnUpdatedChangeInfoType) => { // make sure the status is 'complete' and it's the right tab diff --git a/apps/browser-extension-wallet/src/utils/__tests__/createQueue.test.ts b/apps/browser-extension-wallet/src/utils/__tests__/createQueue.test.ts index 758afeaf2..e6a4942fb 100644 --- a/apps/browser-extension-wallet/src/utils/__tests__/createQueue.test.ts +++ b/apps/browser-extension-wallet/src/utils/__tests__/createQueue.test.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable promise/avoid-new */ import { createQueue, TaskQueue } from '../taskQueue'; describe('createQueue', () => { diff --git a/apps/browser-extension-wallet/src/utils/mocks/fake-api-request.ts b/apps/browser-extension-wallet/src/utils/mocks/fake-api-request.ts index bf078cafb..e7902a039 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/fake-api-request.ts +++ b/apps/browser-extension-wallet/src/utils/mocks/fake-api-request.ts @@ -1,7 +1,6 @@ const DEFAULT_TIMEOUT = 1000; export const fakeApiRequest = (response: TResponse, timeout = DEFAULT_TIMEOUT): Promise => - // eslint-disable-next-line promise/avoid-new new Promise((resolve) => { setTimeout(() => resolve(response), timeout); }); diff --git a/apps/browser-extension-wallet/src/utils/taskQueue.ts b/apps/browser-extension-wallet/src/utils/taskQueue.ts index 67bdfacf9..0337630a5 100644 --- a/apps/browser-extension-wallet/src/utils/taskQueue.ts +++ b/apps/browser-extension-wallet/src/utils/taskQueue.ts @@ -37,7 +37,6 @@ export const createQueue = (batchTasks: number, intervalBetweenBatch: number): T if (sent >= batchTasks) { // Reset the sent count sent = 0; - // eslint-disable-next-line promise/avoid-new await new Promise((resolve) => setTimeout(resolve, intervalBetweenBatch)); } execute(); diff --git a/packages/cardano/src/wallet/lib/__tests__/get-block-info-by-hash.test.ts b/packages/cardano/src/wallet/lib/__tests__/get-block-info-by-hash.test.ts index 7f75cf83a..e10093999 100644 --- a/packages/cardano/src/wallet/lib/__tests__/get-block-info-by-hash.test.ts +++ b/packages/cardano/src/wallet/lib/__tests__/get-block-info-by-hash.test.ts @@ -1,7 +1,6 @@ /* eslint-disable no-magic-numbers */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable promise/avoid-new */ import '@testing-library/jest-dom'; import { getBlockInfoByHash } from '../get-block-info-by-hash'; import { stakepoolSearchProviderStub, mockedStakePools } from '../../test/mocks/StakepoolSearchProviderStub'; diff --git a/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts b/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts index 37a0952b1..96533029e 100644 --- a/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts +++ b/packages/cardano/src/wallet/lib/__tests__/get-inputs-value.test.ts @@ -1,7 +1,6 @@ /* eslint-disable no-magic-numbers */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unused-vars */ -/* eslint-disable promise/avoid-new */ /* eslint-disable max-len */ /* eslint-disable sonarjs/no-identical-functions */ import '@testing-library/jest-dom'; diff --git a/packages/cardano/src/wallet/test/mocks/AssetsProviderStub.ts b/packages/cardano/src/wallet/test/mocks/AssetsProviderStub.ts index e3c3f44bb..49cdb3b9f 100644 --- a/packages/cardano/src/wallet/test/mocks/AssetsProviderStub.ts +++ b/packages/cardano/src/wallet/test/mocks/AssetsProviderStub.ts @@ -22,15 +22,13 @@ export const mockedAssets: Asset.AssetInfo[] = [ ]; export const assetsProviderStub = (assets: Asset.AssetInfo[] = mockedAssets): AssetProvider => ({ - getAsset: jest.fn().mockImplementation( - ({ assetId }) => - // eslint-disable-next-line promise/avoid-new - new Promise((resolve) => resolve(assets.find((asset) => asset.assetId === assetId) || assets[0])) - ), - getAssets: jest.fn().mockImplementation( - ({ assetIds }) => - // eslint-disable-next-line promise/avoid-new - new Promise((resolve) => resolve(assets.filter((asset) => assetIds.includes(asset.assetId)) || assets[0])) - ), + getAsset: jest + .fn() + .mockImplementation(async ({ assetId }) => assets.find((asset) => asset.assetId === assetId) || assets[0]), + getAssets: jest + .fn() + .mockImplementation( + async ({ assetIds }) => assets.filter((asset) => assetIds.includes(asset.assetId)) || assets[0] + ), healthCheck: jest.fn().mockResolvedValue({ ok: true }) }); diff --git a/packages/cardano/src/wallet/test/mocks/TxSubmitProviderFake.ts b/packages/cardano/src/wallet/test/mocks/TxSubmitProviderFake.ts index 0428b1fd7..23c95362e 100644 --- a/packages/cardano/src/wallet/test/mocks/TxSubmitProviderFake.ts +++ b/packages/cardano/src/wallet/test/mocks/TxSubmitProviderFake.ts @@ -8,7 +8,6 @@ export const TxSubmitProviderFake = { return { submittedTxs$: submittedTxs.asObservable(), submitTx: ({ signedTransaction }): Promise => - // eslint-disable-next-line promise/avoid-new new Promise((resolve) => { submittedTxs.next(signedTransaction); resolve(); diff --git a/packages/common/src/ui/hooks/useFetchImage.ts b/packages/common/src/ui/hooks/useFetchImage.ts index 562fd8068..06c05ed87 100644 --- a/packages/common/src/ui/hooks/useFetchImage.ts +++ b/packages/common/src/ui/hooks/useFetchImage.ts @@ -31,7 +31,6 @@ type FetchAction = { const handleImageFetch = (image: string) => { const downloadingImage = new Image(); - // eslint-disable-next-line promise/avoid-new const imageResponse: Promise = new Promise((resolve) => { const onLoadEvent = (event: any) => { resolve({ diff --git a/packages/core/src/ui/components/SharedWallet/AddCoSigners/AddCoSigners.stories.tsx b/packages/core/src/ui/components/SharedWallet/AddCoSigners/AddCoSigners.stories.tsx index 243ab5b30..34b6481b8 100644 --- a/packages/core/src/ui/components/SharedWallet/AddCoSigners/AddCoSigners.stories.tsx +++ b/packages/core/src/ui/components/SharedWallet/AddCoSigners/AddCoSigners.stories.tsx @@ -1,4 +1,3 @@ -/* eslint-disable no-magic-numbers, promise/avoid-new */ import React from 'react'; import type { Meta } from '@storybook/react'; @@ -40,7 +39,7 @@ const addressBook = [ const handleResolution = 'addr_test1qzrljm7nskakjydxlr450ktsj08zuw6aktvgfkmmyw9semrkrezryq3ydtmkg0e7e2jvzg443h0ffzfwd09wpcxy2fuql9tk0g' as Wallet.Cardano.PaymentAddress; -let timeout: NodeJS.Timeout; +let timeout: number; const validateAddress: ValidateAddress = async (address) => { if (!address) { @@ -51,10 +50,12 @@ const validateAddress: ValidateAddress = async (address) => { if (timeout) { clearTimeout(timeout); } - timeout = setTimeout(() => { + const twoSeconds = 2000; + timeout = window.setTimeout(() => { clearTimeout(timeout); - resolve(Math.random() < 0.5 ? { isValid: true, handleResolution } : { isValid: false }); - }, 2000); + const factor = 0.5; + resolve(Math.random() < factor ? { isValid: true, handleResolution } : { isValid: false }); + }, twoSeconds); }); } return { From e273aa9e07dfa1911845ba5889cd91bef572b1d3 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Thu, 11 Apr 2024 15:52:32 +0100 Subject: [PATCH 51/74] fix: [LW-10267] staking persistence local storage (#1064) * fix(extension): add a fallback for stakingBrowserPreferencesPersistence localstorage object * fix: lw-10267 add migration to fix broken staking center * refactor(staking,extension): expose DEFAULT_STAKING_BROWSER_PREFERENCES and apply changes to popup --------- Co-authored-by: przemyslaw.wlodek --- .../MultiDelegationStakingPopup.tsx | 4 +-- .../src/lib/scripts/migrations/migrations.ts | 3 +- .../lib/scripts/migrations/versions/index.ts | 1 + .../scripts/migrations/versions/v1_10_2.ts | 31 +++++++++++++++++++ .../staking/components/StakingContainer.tsx | 4 +-- .../src/features/BrowsePools/constants.ts | 7 ++++- .../staking/src/features/BrowsePools/index.ts | 4 +-- .../staking/src/features/BrowsePools/types.ts | 5 +++ .../outside-handles-provider/types.ts | 7 +---- packages/staking/src/index.ts | 4 +-- 10 files changed, 53 insertions(+), 17 deletions(-) create mode 100644 apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v1_10_2.ts diff --git a/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx b/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx index fdf8e7a83..3f05828cb 100644 --- a/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx +++ b/apps/browser-extension-wallet/src/features/delegation/components/MultiDelegationStakingPopup.tsx @@ -1,4 +1,4 @@ -import { OutsideHandlesProvider, StakingPopup } from '@lace/staking'; +import { DEFAULT_STAKING_BROWSER_PREFERENCES, OutsideHandlesProvider, StakingPopup } from '@lace/staking'; import React, { useCallback, useEffect } from 'react'; import { useAnalyticsContext, @@ -89,7 +89,7 @@ export const MultiDelegationStakingPopup = (): JSX.Element => { ] = useLocalStorage(MULTIDELEGATION_FIRST_VISIT_SINCE_PORTFOLIO_PERSISTENCE_LS_KEY, true); const [stakingBrowserPreferencesPersistence, { updateLocalStorage: setStakingBrowserPreferencesPersistence }] = - useLocalStorage(STAKING_BROWSER_PREFERENCES_LS_KEY); + useLocalStorage(STAKING_BROWSER_PREFERENCES_LS_KEY, DEFAULT_STAKING_BROWSER_PREFERENCES); const walletAddress = walletInfo.addresses?.[0].address?.toString(); const analytics = useAnalyticsContext(); diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts index 46d5496b8..6e87218ae 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/migrations/migrations.ts @@ -43,7 +43,7 @@ export type Migration = { downgrade?: (password?: string) => MigrationPersistance | Promise; }; -const migrations: Migration[] = [versions.v_1_0_0]; +const migrations: Migration[] = [versions.v_1_10_2]; /** * Applies all migrations in order between the two version provided @@ -137,7 +137,6 @@ export const migrationsRequirePassword = async ( */ export const checkMigrations = async (previousVersion: string, migrationsArray = migrations): Promise => { const currentVersion = runtime.getManifest().version; - // Return if a downgrade is occurring if (isVersionOlderThanOrEqual(currentVersion, previousVersion)) { // TODO: allow migrations if downgrading versions too [LW-5595] diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts index c0d8c76da..0dcc43d33 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/index.ts @@ -1 +1,2 @@ export * from './v1_0_0'; +export * from './v1_10_2'; diff --git a/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v1_10_2.ts b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v1_10_2.ts new file mode 100644 index 000000000..8983bfe51 --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/migrations/versions/v1_10_2.ts @@ -0,0 +1,31 @@ +/* eslint-disable camelcase, @typescript-eslint/no-empty-function, no-console */ +import { getItemFromLocalStorage, removeItemFromLocalStorage } from '../util'; +import { Migration } from '../migrations'; + +const MIGRATION_VERSION = '1.10.2'; + +export const v_1_10_2: Migration = { + version: MIGRATION_VERSION, + upgrade: async () => ({ + prepare: () => { + try { + /** + * Between the v1.9.0 and v1.10.0 releases, the 'stakingBrowserPreferences' + * localStorage was updated, and contained a change in spelling, removing this + * object allows it to be saved again and any issues related to spelling + * are mitigated + * */ + removeItemFromLocalStorage('stakingBrowserPreferences'); + } catch (error) { + console.log(`error executing migration ${MIGRATION_VERSION}: ${error}`); + throw error; + } + }, + assert: () => { + const stakingBrowserPreferences = getItemFromLocalStorage('stakingBrowserPreferences'); + return !!stakingBrowserPreferences; + }, + persist: () => {}, + rollback: () => {} + }) +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx index c9d8d1bd7..3df3675da 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/components/StakingContainer.tsx @@ -10,7 +10,7 @@ import { compactNumberWithUnit } from '@utils/format-number'; import { isMultidelegationSupportedByDevice } from '@views/browser/features/staking'; import { useWalletStore } from '@stores'; import { useAnalyticsContext, useCurrencyStore, useExternalLinkOpener } from '@providers'; -import { OutsideHandlesProvider } from '@lace/staking'; +import { DEFAULT_STAKING_BROWSER_PREFERENCES, OutsideHandlesProvider } from '@lace/staking'; import { useBalances, useFetchCoinPrice, useLocalStorage } from '@hooks'; import { MULTIDELEGATION_FIRST_VISIT_LS_KEY, @@ -27,7 +27,7 @@ export const StakingContainer = (): React.ReactElement => { const analytics = useAnalyticsContext(); const [stakingBrowserPreferencesPersistence, { updateLocalStorage: setStakingBrowserPreferencesPersistence }] = - useLocalStorage(STAKING_BROWSER_PREFERENCES_LS_KEY); + useLocalStorage(STAKING_BROWSER_PREFERENCES_LS_KEY, DEFAULT_STAKING_BROWSER_PREFERENCES); const [multidelegationFirstVisit, { updateLocalStorage: setMultidelegationFirstVisit }] = useLocalStorage( MULTIDELEGATION_FIRST_VISIT_LS_KEY, true diff --git a/packages/staking/src/features/BrowsePools/constants.ts b/packages/staking/src/features/BrowsePools/constants.ts index fee9816f3..fca7546d3 100644 --- a/packages/staking/src/features/BrowsePools/constants.ts +++ b/packages/staking/src/features/BrowsePools/constants.ts @@ -1,4 +1,4 @@ -import { BrowsePoolsView, StakePoolSortOptions } from './types'; +import { BrowsePoolsView, StakePoolSortOptions, StakingBrowserPreferences } from './types'; import { getDefaultSortOrderByField } from './utils'; export const SEARCH_DEBOUNCE_IN_MS = 300; @@ -9,3 +9,8 @@ export const DEFAULT_SORT_OPTIONS: StakePoolSortOptions = { }; export const DEFAULT_BROWSE_POOLS_VIEW: BrowsePoolsView = BrowsePoolsView.grid; + +export const DEFAULT_STAKING_BROWSER_PREFERENCES: StakingBrowserPreferences = { + poolsView: DEFAULT_BROWSE_POOLS_VIEW, + selectedPoolIds: [], +}; diff --git a/packages/staking/src/features/BrowsePools/index.ts b/packages/staking/src/features/BrowsePools/index.ts index 7c91737bb..7681853e0 100644 --- a/packages/staking/src/features/BrowsePools/index.ts +++ b/packages/staking/src/features/BrowsePools/index.ts @@ -2,11 +2,11 @@ export { BrowsePools } from './BrowsePools'; export { BrowsePoolsPreferencesCard } from './BrowsePoolsPreferencesCard'; export { getPoolInfos } from './queries'; export { useBrowsePoolsPersistence } from './hooks'; -export { DEFAULT_SORT_OPTIONS } from './constants'; +export { DEFAULT_SORT_OPTIONS, DEFAULT_STAKING_BROWSER_PREFERENCES } from './constants'; // TODO: remove once multi delegation feature is GA'd export { getSaturationLevel, isOversaturated, getDefaultSortOrderByField } from './utils'; -export type { StakePoolSortOptions, TranslationsFor, SortField, SortOrder } from './types'; +export type { StakingBrowserPreferences, StakePoolSortOptions, TranslationsFor, SortField, SortOrder } from './types'; export { BrowsePoolsView } from './types'; export { StakePoolCardProgressBar } from './StakePoolCard'; export { StakePoolsListRowSkeleton, config as stakePoolTableConfig } from './StakePoolsList'; diff --git a/packages/staking/src/features/BrowsePools/types.ts b/packages/staking/src/features/BrowsePools/types.ts index 9c561e754..6e76f49b7 100644 --- a/packages/staking/src/features/BrowsePools/types.ts +++ b/packages/staking/src/features/BrowsePools/types.ts @@ -22,3 +22,8 @@ export type StakePoolSortOptions = { }; export type TranslationsFor = Record; + +export type StakingBrowserPreferences = { + poolsView: BrowsePoolsView; + selectedPoolIds: string[]; +}; diff --git a/packages/staking/src/features/outside-handles-provider/types.ts b/packages/staking/src/features/outside-handles-provider/types.ts index 2282239c1..66d1c28ab 100644 --- a/packages/staking/src/features/outside-handles-provider/types.ts +++ b/packages/staking/src/features/outside-handles-provider/types.ts @@ -1,7 +1,7 @@ import { TxBuilder } from '@cardano-sdk/tx-construction'; import { Wallet } from '@lace/cardano'; import { AssetActivityListProps } from '@lace/core'; -import { BrowsePoolsView, StakePoolSortOptions } from 'features/BrowsePools/types'; +import { StakePoolSortOptions, StakingBrowserPreferences } from 'features/BrowsePools'; import type { IAnalyticsTracker } from '@lace/common'; type WalletBalance = { @@ -40,11 +40,6 @@ export enum StateStatus { ERROR = 'error', } -export interface StakingBrowserPreferences { - poolsView: BrowsePoolsView; - selectedPoolIds: string[]; -} - export interface IBlockchainProvider { stakePoolProvider: Wallet.StakePoolProvider; assetProvider: Wallet.AssetProvider; diff --git a/packages/staking/src/index.ts b/packages/staking/src/index.ts index 1bd7a9566..0759ea148 100644 --- a/packages/staking/src/index.ts +++ b/packages/staking/src/index.ts @@ -1,7 +1,7 @@ export { Staking, StakingPopup } from './features/staking'; -export { BrowsePoolsPreferencesCard } from './features/BrowsePools'; +export type { StakingBrowserPreferences } from 'features/BrowsePools'; +export { BrowsePoolsPreferencesCard, DEFAULT_STAKING_BROWSER_PREFERENCES } from './features/BrowsePools'; export { OutsideHandlesProvider } from './features/outside-handles-provider'; -export type { StakingBrowserPreferences } from './features/outside-handles-provider'; export { MAX_POOLS_COUNT } from './features/store'; // TODO: remove once multi delegaion feature is GA'd From 97256cb982262be93fc65b8ff6ced9b3e89cda11 Mon Sep 17 00:00:00 2001 From: Michael Chappell <7581002+mchappell@users.noreply.github.com> Date: Thu, 11 Apr 2024 16:29:35 +0100 Subject: [PATCH 52/74] chore(extension): update lace version v1.10.2 (#1066) --- apps/browser-extension-wallet/manifest.json | 2 +- apps/browser-extension-wallet/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser-extension-wallet/manifest.json b/apps/browser-extension-wallet/manifest.json index e85486290..18c96b95b 100644 --- a/apps/browser-extension-wallet/manifest.json +++ b/apps/browser-extension-wallet/manifest.json @@ -1,7 +1,7 @@ { "name": "$WALLET_MANIFEST_NAME", "description": "One fast, accessible, and secure platform for digital assets, DApps, NFTs, and DeFi.", - "version": "1.10.1", + "version": "1.10.2", "manifest_version": 3, "key": "$LACE_EXTENSION_KEY", "icons": { diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index 432ea0dab..cdc8667ac 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -1,6 +1,6 @@ { "name": "@lace/browser-extension-wallet", - "version": "1.10.1", + "version": "1.10.2", "description": "A fully capable wallet packaged as browser extensions for Chrome, Firefox, and Edge", "homepage": "https://github.com/input-output-hk/lace/blob/master/apps/browser-extension-wallet/README.md", "bugs": { From ce6a4141d945971c4182dfee0e26578cbbfa5b0e Mon Sep 17 00:00:00 2001 From: Tom Mayel <143514860+tommayeliog@users.noreply.github.com> Date: Thu, 11 Apr 2024 18:44:29 +0100 Subject: [PATCH 53/74] chore: [LW-10275] fix tooling to build developer preview app (#1065) --- apps/browser-extension-wallet/.env.developerpreview | 1 + apps/browser-extension-wallet/webpack.common.dev.js | 2 +- apps/browser-extension-wallet/webpack.common.js | 2 +- apps/browser-extension-wallet/webpack.common.prod.js | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/browser-extension-wallet/.env.developerpreview b/apps/browser-extension-wallet/.env.developerpreview index 59bf74f4b..b92bf9963 100644 --- a/apps/browser-extension-wallet/.env.developerpreview +++ b/apps/browser-extension-wallet/.env.developerpreview @@ -59,6 +59,7 @@ PRODUCTION_MODE_TRACKING=false POSTHOG_DEV_TOKEN_MAINNET=phc_gH96Lx5lEVXTTWEyytSdTFPDk3Xsxwi4BqG88mKObd1 POSTHOG_DEV_TOKEN_PREPROD=phc_Xlmldm6EYSfQVgB9Uxm3b2xC1noDlgFFXpF9AJ6SMfJ POSTHOG_DEV_TOKEN_PREVIEW=phc_e8SaOOWpXpNE59TnpLumeUjWm4iv024AWjhQqU406jr +POSTHOG_DEV_TOKEN_SANCHONET=phc_OUu6sPucDu5S6skRmYbWN5Jn8TpggWTQu1Y1ETkm3xt # Cardano Services CARDANO_SERVICES_URL_MAINNET=https://dev-mainnet.lw.iog.io diff --git a/apps/browser-extension-wallet/webpack.common.dev.js b/apps/browser-extension-wallet/webpack.common.dev.js index ef6db8300..35825150a 100644 --- a/apps/browser-extension-wallet/webpack.common.dev.js +++ b/apps/browser-extension-wallet/webpack.common.dev.js @@ -48,7 +48,7 @@ module.exports = path: '.env', safe: false, silent: false, - defaults: process.env.BUILD_DEV_PREVIEW === 'true' ? '.env.devpreview' : true, + defaults: process.env.BUILD_DEV_PREVIEW === 'true' ? '.env.developerpreview' : true, systemvars: true, allowEmptyValues: true }), diff --git a/apps/browser-extension-wallet/webpack.common.js b/apps/browser-extension-wallet/webpack.common.js index fdd930229..8c4d06e13 100644 --- a/apps/browser-extension-wallet/webpack.common.js +++ b/apps/browser-extension-wallet/webpack.common.js @@ -86,7 +86,7 @@ module.exports = () => { path: '.env', safe: false, silent: false, - defaults: process.env.BUILD_DEV_PREVIEW === 'true' ? '.env.devpreview' : true, + defaults: process.env.BUILD_DEV_PREVIEW === 'true' ? '.env.developerpreview' : true, systemvars: true, allowEmptyValues: true }), diff --git a/apps/browser-extension-wallet/webpack.common.prod.js b/apps/browser-extension-wallet/webpack.common.prod.js index 98a4e4e58..73cff79a0 100644 --- a/apps/browser-extension-wallet/webpack.common.prod.js +++ b/apps/browser-extension-wallet/webpack.common.prod.js @@ -16,7 +16,7 @@ module.exports = () => ({ path: '.env', safe: false, silent: false, - defaults: process.env.BUILD_DEV_PREVIEW === 'true' ? '.env.devpreview' : true, + defaults: process.env.BUILD_DEV_PREVIEW === 'true' ? '.env.developerpreview' : true, systemvars: true, allowEmptyValues: true }), From 8df74e9ad9a3e574c620530105e8e2df3f37cd32 Mon Sep 17 00:00:00 2001 From: vetalcore Date: Fri, 12 Apr 2024 08:26:23 +0300 Subject: [PATCH 54/74] chore(extension): fix storybook for core package (#1061) --- yarn.lock | 134 ++---------------------------------------------------- 1 file changed, 3 insertions(+), 131 deletions(-) diff --git a/yarn.lock b/yarn.lock index b3a953335..6dedb7380 100644 --- a/yarn.lock +++ b/yarn.lock @@ -19270,16 +19270,6 @@ __metadata: languageName: node linkType: hard -"@types/eslint-scope@npm:^3.7.0": - version: 3.7.1 - resolution: "@types/eslint-scope@npm:3.7.1" - dependencies: - "@types/eslint": "*" - "@types/estree": "*" - checksum: 4271c9adad19ad8a1d23062d9020468a51c7f81594b12b8e68f7d460c09e14d57cae3e82b077c402766369c0c17e2de72da72c405fa465d18a46c0b14ce92530 - languageName: node - linkType: hard - "@types/eslint-scope@npm:^3.7.3": version: 3.7.4 resolution: "@types/eslint-scope@npm:3.7.4" @@ -19300,7 +19290,7 @@ __metadata: languageName: node linkType: hard -"@types/estree@npm:*, @types/estree@npm:^0.0.50": +"@types/estree@npm:*": version: 0.0.50 resolution: "@types/estree@npm:0.0.50" checksum: 9a2b6a4a8c117f34d08fbda5e8f69b1dfb109f7d149b60b00fd7a9fb6ac545c078bc590aa4ec2f0a256d680cf72c88b3b28b60c326ee38a7bc8ee1ee95624922 @@ -29031,7 +29021,7 @@ __metadata: languageName: node linkType: hard -"enhanced-resolve@npm:^5.7.0, enhanced-resolve@npm:^5.8.3": +"enhanced-resolve@npm:^5.7.0": version: 5.8.3 resolution: "enhanced-resolve@npm:5.8.3" dependencies: @@ -52577,13 +52567,6 @@ __metadata: languageName: node linkType: hard -"webpack-sources@npm:^3.2.0": - version: 3.2.0 - resolution: "webpack-sources@npm:3.2.0" - checksum: 8f1d686bd6aab2eda330579a07e14803cb2e01415f5a603697402aea3c36e98c1d2731167c3e97e50170cf1b0214cf8ef945fc639b100d1e3b67c023feb35716 - languageName: node - linkType: hard - "webpack-sources@npm:^3.2.3": version: 3.2.3 resolution: "webpack-sources@npm:3.2.3" @@ -52704,118 +52687,7 @@ __metadata: languageName: node linkType: hard -"webpack@npm:>=4.43.0 <6.0.0": - version: 5.74.0 - resolution: "webpack@npm:5.74.0" - dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^0.0.51 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.7.1 - acorn-import-assertions: ^1.7.6 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.10.0 - es-module-lexer: ^0.9.0 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.9 - json-parse-even-better-errors: ^2.3.1 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.1.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.1.3 - watchpack: ^2.4.0 - webpack-sources: ^3.2.3 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: 320c41369a75051b19e18c63f408b3dcc481852e992f83d311771c5ec0f05f2946385e8ebef62030cf3587f0a3d2f12779ffdb191569a966847289ba7313f946 - languageName: node - linkType: hard - -"webpack@npm:^5": - version: 5.58.2 - resolution: "webpack@npm:5.58.2" - dependencies: - "@types/eslint-scope": ^3.7.0 - "@types/estree": ^0.0.50 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.4.1 - acorn-import-assertions: ^1.7.6 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.8.3 - es-module-lexer: ^0.9.0 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.4 - json-parse-better-errors: ^1.0.2 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.1.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.1.3 - watchpack: ^2.2.0 - webpack-sources: ^3.2.0 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: 775da3b72c181204ca3f5200c144dde874fec8e059ba6c08dc9596e33e8408fd55c9c21ab5996124fe4b65963a54817bd4dfbb79fb89f307c9bf6054d69eaef5 - languageName: node - linkType: hard - -"webpack@npm:^5.76.1": - version: 5.76.1 - resolution: "webpack@npm:5.76.1" - dependencies: - "@types/eslint-scope": ^3.7.3 - "@types/estree": ^0.0.51 - "@webassemblyjs/ast": 1.11.1 - "@webassemblyjs/wasm-edit": 1.11.1 - "@webassemblyjs/wasm-parser": 1.11.1 - acorn: ^8.7.1 - acorn-import-assertions: ^1.7.6 - browserslist: ^4.14.5 - chrome-trace-event: ^1.0.2 - enhanced-resolve: ^5.10.0 - es-module-lexer: ^0.9.0 - eslint-scope: 5.1.1 - events: ^3.2.0 - glob-to-regexp: ^0.4.1 - graceful-fs: ^4.2.9 - json-parse-even-better-errors: ^2.3.1 - loader-runner: ^4.2.0 - mime-types: ^2.1.27 - neo-async: ^2.6.2 - schema-utils: ^3.1.0 - tapable: ^2.1.1 - terser-webpack-plugin: ^5.1.3 - watchpack: ^2.4.0 - webpack-sources: ^3.2.3 - peerDependenciesMeta: - webpack-cli: - optional: true - bin: - webpack: bin/webpack.js - checksum: b01fe0bc2dbca0e10d290ddb0bf81e807a031de48028176e2b21afd696b4d3f25ab9accdad888ef4a1f7c7f4d41f13d5bf2395b7653fdf3e5e3dafa54e56dab2 - languageName: node - linkType: hard - -"webpack@npm:^5.9.0": +"webpack@npm:>=4.43.0 <6.0.0, webpack@npm:^5, webpack@npm:^5.76.1, webpack@npm:^5.9.0": version: 5.84.1 resolution: "webpack@npm:5.84.1" dependencies: From 19f1506062b2831a60d1f75611111b8f9545c4aa Mon Sep 17 00:00:00 2001 From: Emir Hodzic Date: Fri, 12 Apr 2024 15:31:23 +0200 Subject: [PATCH 55/74] test(extension): unify toast asertion (#1043) * test(extension): unify toast asertion * test(extension): fix code formatting --------- Co-authored-by: wklos-iohk --- .../src/features/AdaHandleExtended.feature | 12 +++-- .../src/features/AdaHandlePopup.feature | 9 +++- .../src/features/AddressBookExtended.feature | 46 +++++++++++-------- .../src/features/AddressBookPopup.feature | 44 ++++++++++-------- .../src/features/EmptyStatesExtended.feature | 8 ++-- .../src/features/EmptyStatesPopup.feature | 8 ++-- .../features/NavigationTopExtended.feature | 6 +-- .../src/features/NavigationTopPopup.feature | 6 +-- .../src/features/SettingsPageExtended.feature | 8 ++-- .../src/features/SettingsPagePopup.feature | 8 ++-- .../SendTransactionSimpleExtendedE2E.feature | 4 +- .../e2e/SendTransactionSimplePopupE2E.feature | 4 +- .../e2e-tests/src/steps/AddressBookSteps.ts | 19 -------- packages/e2e-tests/src/steps/commonSteps.ts | 37 +++++++++++++-- .../e2e-tests/src/steps/nftFoldersSteps.ts | 32 ------------- .../src/steps/waletAddressPageSteps.ts | 21 --------- 16 files changed, 125 insertions(+), 147 deletions(-) diff --git a/packages/e2e-tests/src/features/AdaHandleExtended.feature b/packages/e2e-tests/src/features/AdaHandleExtended.feature index a18c4d15b..f92dede50 100644 --- a/packages/e2e-tests/src/features/AdaHandleExtended.feature +++ b/packages/e2e-tests/src/features/AdaHandleExtended.feature @@ -37,7 +37,7 @@ Feature: ADA handle - extended view When I fill address form with "AH 1 edited" name and "$test_handle_3" address Then Green tick icon is displayed next to ADA handle And I click "Done" button on "Edit address" drawer - And I see a toast with message: "browserView.addressBook.toast.editAddress" + And I see a toast with text: "Edited successfully" And I see address row with name "AH 1 edited" and address "$test_handle_3" on the list in extended mode @LW-7337 @@ -58,7 +58,7 @@ Feature: ADA handle - extended view When I fill address form with "AH 1 edited" name and "$test_handle_2" address Then Green tick icon is displayed next to ADA handle And I click "Done" button on "Edit address" drawer - And I see a toast with message: "addressBook.errors.givenAddressAlreadyExist" + And I see a toast with text: "Given address already exists" @LW-7140 @LW-7136 Scenario: Extended View - Ada handles displayed and sorted by handle length @@ -79,18 +79,23 @@ Feature: ADA handle - extended view And I see "Wallet Address" page in extended mode for wallet "WalletAdaHandle" When I click "Copy" button on "Receive" page for default wallet address Then I see a toast with text: "Address copied" + And I close a toast message And Clipboard contains address of wallet: "WalletAdaHandle" When I click "Copy" button on "Receive" page for handle: "$cde" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$cde" When I click "Copy" button on "Receive" page for handle: "$t_h_1" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$t_h_1" When I click "Copy" button on "Receive" page for handle: "$test_handle_1" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$test_handle_1" When I click "Copy" button on "Receive" page for handle: "$test_handle_3" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$test_handle_3" @LW-7427 @LW-7426 @@ -144,8 +149,7 @@ Feature: ADA handle - extended view And I see ADA handle NFT with custom image on the Select NFT page And the corresponding custom images are displayed - @LW-5025 @LW-5028 @LW-5030 @Pending - @issue=LW-9885 + @LW-5025 @LW-5028 @LW-5030 @Pending @issue=LW-9885 Scenario: Extended View - Send flow - Enter ADA handle and confirm validated When I click "Send" button on page header And I enter "$test_handle_3" in the bundle 1 recipient's address diff --git a/packages/e2e-tests/src/features/AdaHandlePopup.feature b/packages/e2e-tests/src/features/AdaHandlePopup.feature index 76f1c8316..21807257e 100644 --- a/packages/e2e-tests/src/features/AdaHandlePopup.feature +++ b/packages/e2e-tests/src/features/AdaHandlePopup.feature @@ -37,7 +37,7 @@ Feature: ADA handle - popup view When I fill address form with "AH 1 edited" name and "$test_handle_3" address Then Green tick icon is displayed next to ADA handle And I click "Done" button on "Edit address" drawer - And I see a toast with message: "browserView.addressBook.toast.editAddress" + And I see a toast with text: "Edited successfully" And I see address row with name "AH 1 edited" and address "$test_handle_3" on the list in popup mode @LW-7338 @@ -58,7 +58,7 @@ Feature: ADA handle - popup view When I fill address form with "AH 1 edited" name and "$test_handle_2" address Then Green tick icon is displayed next to ADA handle And I click "Done" button on "Edit address" drawer - And I see a toast with message: "addressBook.errors.givenAddressAlreadyExist" + And I see a toast with text: "Given address already exists" @LW-7135 @LW-7139 Scenario: Popup View - Ada handles displayed and sorted by handle length @@ -78,18 +78,23 @@ Feature: ADA handle - popup view And I see "Wallet Address" page in popup mode for wallet "WalletAdaHandle" When I click "Copy" button on "Receive" page for default wallet address Then I see a toast with text: "Address copied" + And I close a toast message And Clipboard contains address of wallet: "WalletAdaHandle" When I click "Copy" button on "Receive" page for handle: "$cde" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$cde" When I click "Copy" button on "Receive" page for handle: "$t_h_1" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$t_h_1" When I click "Copy" button on "Receive" page for handle: "$test_handle_1" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$test_handle_1" When I click "Copy" button on "Receive" page for handle: "$test_handle_3" Then I see a toast with text: "Handle copied" + And I close a toast message And Clipboard contains text: "$test_handle_3" @LW-7435 @LW-7436 diff --git a/packages/e2e-tests/src/features/AddressBookExtended.feature b/packages/e2e-tests/src/features/AddressBookExtended.feature index 90f0131ed..48501e142 100644 --- a/packages/e2e-tests/src/features/AddressBookExtended.feature +++ b/packages/e2e-tests/src/features/AddressBookExtended.feature @@ -36,6 +36,7 @@ Feature: Address book - extended view And I click "Save address" button on "Add new address" drawer Then I see a toast with text: "Address added" And I see address row with name "" and address "
" on the list in extended mode + Examples: | wallet_name | address | | Byron_manual | 37btjrVyb4KC6N6XtRHwEuLPQW2aa9JA89gbnm67PArSi8E7vGeqgA6W1pFBphc1hhrk1WKGPZpUbnvYRimVLRVnUH6M6d3dsVdxYoAC4m7oNj7Dzp | @@ -52,6 +53,7 @@ Feature: Address book - extended view When I fill address form with "" name and "
" address Then Contact "" name error and "" address error are displayed And "Save address" button is disabled on "Add new address" drawer + Examples: | wallet_name | address | name_error | address_error | | too_long_name_123456789 | addr_invalid | Max 20 Characters | Invalid Cardano address | @@ -69,6 +71,7 @@ Feature: Address book - extended view And I fill address form with "" name and "" address Then Contact "" name error and "" address error are displayed And "Save address" button is disabled on "Add new address" drawer + Examples: | wallet_name | wallet_name2 | address | address2 | name_error | address_error | | name_ok | empty | 2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf | 2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf | Name field is required | empty | @@ -100,11 +103,12 @@ Feature: Address book - extended view And I click "Add address" button on address book page When I fill address form with "" name and address from "" address And I click "Save address" button on "Add new address" drawer - Then I see a toast with message: "" + Then I see a toast with text: "" + Examples: - | wallet_name | wallet_address | toast_message | - | Byron | Byron | addressBook.errors.givenNameAlreadyExist | - | SomeWallet | Byron | addressBook.errors.givenAddressAlreadyExist | + | wallet_name | wallet_address | toast_message | + | Byron | Byron | Given name already exists | + | SomeWallet | Byron | Given address already exists | @LW-4469 Scenario: Extended-view - Address Book - Copy address button @@ -112,7 +116,7 @@ Feature: Address book - extended view And I have 3 addresses in my address book in extended mode When I click address on the list with name "Byron" And I click "Copy" button on address details page - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" And address is saved to clipboard @LW-4470 @@ -124,6 +128,7 @@ Feature: Address book - extended view And I click "Done" button on "Edit address" drawer Then I see a toast with text: "Edited successfully" And I see address row with name "" and address "
" on the list in extended mode + Examples: | edited_address | wallet_name | address | | Shelley | Shelley_edited | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | @@ -145,17 +150,18 @@ Feature: Address book - extended view And I fill address form with "" name and "
" address Then Contact "" name error and "" address error are displayed And "Done" button is disabled on "Edit address" drawer + Examples: | wallet_name | address | name_error | address_error | | empty | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | Name field is required | empty | | too_long_name_123456789 | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | Max 20 Characters | empty | | " name preceded by space" | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | Name has unnecessary white space | empty | -# | valid wallet name | empty | empty | Address field is required | # TODO: Uncomment when LW-7419 is fixed | valid wallet name | invalid_address | empty | Invalid Cardano address | | valid wallet name | " addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja" | empty | Address has unnecessary white space | | valid wallet name | "addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja " | empty | Address has unnecessary white space | -# | empty | empty | Name field is required | Address field is required | # TODO: Uncomment when LW-7419 is fixed | "name followed by space " | invalid_address | Name has unnecessary white space | Invalid Cardano address | +# | valid wallet name | empty | empty | Address field is required | # TODO: Uncomment when LW-7419 is fixed +# | empty | empty | Name field is required | Address field is required | # TODO: Uncomment when LW-7419 is fixed @LW-4567 Scenario Outline: Extended-view - Address Book - Edit address book entry - Uniqueness validation and toast display with text @@ -164,11 +170,12 @@ Feature: Address book - extended view And I click "Edit" button on address details page And I fill address form with "" name and address from "" address And I click "Done" button on "Edit address" drawer - Then I see a toast with message: "" + Then I see a toast with text: "" + Examples: - | wallet_name | wallet_address | toast_message | - | Byron | Byron | addressBook.errors.givenNameAlreadyExist | - | SomeWallet | Byron | addressBook.errors.givenAddressAlreadyExist | + | wallet_name | wallet_address | toast_message | + | Byron | Byron | Given name already exists | + | SomeWallet | Byron | Given address already exists | @LW-4535 Scenario: Extended-view - Address Book - Edit address and click exit button @@ -196,13 +203,13 @@ Feature: Address book - extended view Given I don't have any addresses added to my address book in extended mode When I click on a widget item with subtitle: "" Then I see a "" article with title "" + Examples: | type | subtitle | | Glossary | What is the Lace address book? | | Glossary | What is a saved address? | - @LW-4744 @Pending - @issue=LW-7697 + @LW-4744 @Pending @issue=LW-7697 Scenario: Extended-view - Address Book - Enter and Escape buttons support when editing address Given I have 3 addresses in my address book in extended mode When I click address on the list with name "Byron" @@ -214,7 +221,7 @@ Feature: Address book - extended view When I press keyboard Enter button And I fill address form with "Byron_edited" name and "37btjrVyb4KC6N6XtRHwEuLPQW2aa9JA89gbnm67PArSi8E7vGeqgA6W1pFBphc1hhrk1WKGPZpUbnvYRimVLRVnUH6M6d3dsVdxYoAC4m7oNj7Dzp" address When I press keyboard Enter button - Then I see a toast with message: "browserView.addressBook.toast.editAddress" + Then I see a toast with text: "Edited successfully" @LW-4745 Scenario: Extended-view - Address Book - Escape button support when closing drawer @@ -224,8 +231,7 @@ Feature: Address book - extended view When I press keyboard Escape button Then I do not see address detail page in extended mode with details of "Byron" address - @LW-4779 @Pending - @issue=LW-7419 + @LW-4779 @Pending @issue=LW-7419 Scenario: Extended-view - Address Book - Display error message after filling name and clicking outside with empty address Given I don't have any addresses added to my address book in extended mode And I click "Add address" button on address book page @@ -235,8 +241,7 @@ Feature: Address book - extended view And I click outside address form to lose focus Then Contact "empty" name error and "Address field is required" address error are displayed - @LW-4780 @Pending - @issue=LW-7419 + @LW-4780 @Pending @issue=LW-7419 Scenario: Extended-view - Address Book - Display error message when adding valid address and clicking outside with empty name field Given I don't have any addresses added to my address book in extended mode And I click "Add address" button on address book page @@ -246,8 +251,7 @@ Feature: Address book - extended view And I click outside address form to lose focus Then Contact "Name field is required" name error and "empty" address error are displayed - @LW-4781 @Pending - @issue=LW-7419 + @LW-4781 @Pending @issue=LW-7419 Scenario: Extended-view - Address Book - No error is displayed when leaving both fields empty Given I don't have any addresses added to my address book in extended mode And I click "Add address" button on address book page @@ -272,6 +276,7 @@ Feature: Address book - extended view And I close wallet synced toast When I add new address: "
" with name: "" in extended mode Then I verify that address: "
" with name: "" has been added in extended mode + Examples: | wallet_name | address | | example_name1 | addr_test1qzngq82mhkzqttqvdk8yl4twk4ea70ja2e7j92x9vqwatds4dm4z5j48w9mjpag2htut4g6pzfxm7x958m3wxjwc8t6q8k6txr | @@ -295,6 +300,7 @@ Feature: Address book - extended view And I switch network to: "Mainnet" in extended mode And I open address book in extended mode Then I see address row with name "" and address "
" on the list in extended mode + Examples: | wallet_name | address | | example_name2 | addr_test1qzcx0kfmglh9hg5wa7kxzt3c3e8psnm0pus38qth0wgmmljcexj60ge60d8h7nyz9ez0mzgxznr5kr6rfsemdqp74p0q9rw57j | diff --git a/packages/e2e-tests/src/features/AddressBookPopup.feature b/packages/e2e-tests/src/features/AddressBookPopup.feature index 594c8b0fd..ccb53a548 100644 --- a/packages/e2e-tests/src/features/AddressBookPopup.feature +++ b/packages/e2e-tests/src/features/AddressBookPopup.feature @@ -15,7 +15,7 @@ Feature: Address book - popup view And I have 3 addresses in my address book in popup mode When I click address on the list with name "Byron" And I click "Copy" button on address details page - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" And address is saved to clipboard @LW-4475 @@ -27,6 +27,7 @@ Feature: Address book - popup view And I click "Done" button on "Edit address" drawer Then I see a toast with text: "Edited successfully" And I see address row with name "" and address "" on the list in popup mode + Examples: | edited_address | wallet_name | address | address_label | | Shelley | Shelley_edit | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | addr_tes...6ja | @@ -48,17 +49,18 @@ Feature: Address book - popup view And I fill address form with "" name and "
" address Then Contact "" name error and "" address error are displayed And "Done" button is disabled on "Edit address" drawer + Examples: | wallet_name | address | name_error | address_error | | empty | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | Name field is required | empty | | too_long_name_123456789 | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | Max 20 Characters | empty | | " name preceded by space" | addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja | Name has unnecessary white space | empty | -# | valid wallet name | empty | empty | Address field is required | # TODO: Uncomment when LW-7419 is fixed - | valid wallet name | invalid_address | empty | Invalid Cardano address | + | valid wallet name | invalid_address | empty | Invalid Cardano address | | valid wallet name | " addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja" | empty | Address has unnecessary white space | | valid wallet name | "addr_test1qq959a7g4spmkg4gz2yw02622c739p8crt6tzh04qzag992wcj4m99m95nmkgxhk8j0upqp2jzaxxdsj3jf9v4yhv3uqfwr6ja " | empty | Address has unnecessary white space | + | "name followed by space " | invalid_address | Name has unnecessary white space | Invalid Cardano address | +# | valid wallet name | empty | empty | Address field is required | # TODO: Uncomment when LW-7419 is fixed # | empty | empty | Name field is required | Address field is required | # TODO: Uncomment when LW-7419 is fixed - | "name followed by space " | invalid_address | Name has unnecessary white space | Invalid Cardano address | @LW-4568 Scenario Outline: Popup-view - Address Book - Edit address book entry - Uniqueness validation and toast display with text @@ -68,11 +70,12 @@ Feature: Address book - popup view And I fill address form with "" name and address from "" address And "Done" button is enabled on "Edit address" drawer Then I click "Done" button on "Edit address" drawer - And I see a toast with message: "" + And I see a toast with text: "" + Examples: - | wallet_name | wallet_address | toast_message | - | Byron | Byron | addressBook.errors.givenNameAlreadyExist | - | SomeWallet | Byron | addressBook.errors.givenAddressAlreadyExist | + | wallet_name | wallet_address | toast_message | + | Byron | Byron | Given name already exists | + | SomeWallet | Byron | Given address already exists | @LW-4534 Scenario: Popup-view - Address Book - Edit address and click back button @@ -111,6 +114,7 @@ Feature: Address book - popup view And I click "Save address" button on "Add new address" drawer Then I see a toast with text: "Address added" And I see address row with name "" and address "" on the list in popup mode + Examples: | wallet_name | wallet_address | | Byron_man | Byron | @@ -127,10 +131,11 @@ Feature: Address book - popup view And I fill address form with "" name and "
" address Then Contact "" name error and "" address error are displayed And "Save address" button is disabled on "Add new address" drawer + Examples: | wallet_name | address | name_error | address_error | - | too_long_name_123456789 | addr_invalid | Max 20 Characters | Invalid Cardano address | - | name_ok | addr_invalid | empty | Invalid Cardano address | + | too_long_name_123456789 | addr_invalid | Max 20 Characters | Invalid Cardano address | + | name_ok | addr_invalid | empty | Invalid Cardano address | | too_long_name_123456789 | 2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf | Max 20 Characters | empty | | "name followed by space " | "2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf " | Name has unnecessary white space | Address has unnecessary white space | | " name preceded by space" | " 2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf" | Name has unnecessary white space | Address has unnecessary white space | @@ -143,6 +148,7 @@ Feature: Address book - popup view And I fill address form with "" name and "" address Then Contact "" name error and "" address error are displayed And "Save address" button is disabled on "Add new address" drawer + Examples: | wallet_name | wallet_name2 | address | address2 | name_error | address_error | | name_ok | empty | 2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf | 2cWKMJemoBainaQxNUjUnKDr6mGgSERDRrvKAJzWejubdymYZv1uKedpSYkkehHnSwMCf | Name field is required | empty | @@ -155,14 +161,14 @@ Feature: Address book - popup view When I click "Add address" button on address book page And I fill address form with "" name and address from "" address And I click "Save address" button on "Add new address" drawer - Then I see a toast with message: "" + Then I see a toast with text: "" + Examples: - | wallet_name | wallet_address | toast_message | - | Byron | Byron | addressBook.errors.givenNameAlreadyExist | - | SomeWallet | Byron | addressBook.errors.givenAddressAlreadyExist | + | wallet_name | wallet_address | toast_message | + | Byron | Byron | Given name already exists | + | SomeWallet | Byron | Given address already exists | - @LW-4784 @Pending - @issue=LW-7419 + @LW-4784 @Pending @issue=LW-7419 Scenario: Popup-view - Address Book - Display error message after filling name and clicking outside with empty address Given I don't have any addresses added to my address book in popup mode When I click "Add address" button on address book page @@ -172,8 +178,7 @@ Feature: Address book - popup view And I click outside address form to lose focus Then Contact "Name field is required" name error and "empty" address error are displayed - @LW-4783 @Pending - @issue=LW-7419 + @LW-4783 @Pending @issue=LW-7419 Scenario: Popup-view - Address Book - No error is displayed when leaving both fields empty Given I don't have any addresses added to my address book in popup mode When I click "Add address" button on address book page @@ -185,8 +190,7 @@ Feature: Address book - popup view Then Contact "empty" name error and "empty" address error are displayed And "Save address" button is disabled on "Add new address" drawer - @LW-4785 @Pending - @issue=LW-7419 + @LW-4785 @Pending @issue=LW-7419 Scenario: Popup-view - Address Book - Display error message after filling name and clicking outside with empty address Given I don't have any addresses added to my address book in popup mode When I click "Add address" button on address book page diff --git a/packages/e2e-tests/src/features/EmptyStatesExtended.feature b/packages/e2e-tests/src/features/EmptyStatesExtended.feature index 273e140f5..1697237c3 100644 --- a/packages/e2e-tests/src/features/EmptyStatesExtended.feature +++ b/packages/e2e-tests/src/features/EmptyStatesExtended.feature @@ -10,7 +10,7 @@ Feature: Empty states Then I see empty state banner for Tokens page in extended mode And I do not see CoinGecko credits When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-2516 @LW-7236 Scenario: Extended View - NFTs empty state @@ -18,21 +18,21 @@ Feature: Empty states Then I see empty state banner for NFTs page in extended mode And I do not see "Create folder" button on NFTs page in extended mode When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-4445 @Smoke Scenario: Extended View - Transactions empty state When I navigate to Transactions extended page Then I see empty state banner for Transactions page in extended mode When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-8447 Scenario: Extended View - Staking empty state And I navigate to Staking extended page Then I see empty state banner for Staking page in extended mode When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-3746 Scenario: Extended-view - verify that MAX button is hidden when user has no tokens available in the wallet diff --git a/packages/e2e-tests/src/features/EmptyStatesPopup.feature b/packages/e2e-tests/src/features/EmptyStatesPopup.feature index daa1864c5..b345407fd 100644 --- a/packages/e2e-tests/src/features/EmptyStatesPopup.feature +++ b/packages/e2e-tests/src/features/EmptyStatesPopup.feature @@ -9,7 +9,7 @@ Feature: Empty states Then I see empty state banner for Tokens page in popup mode And I do not see CoinGecko credits When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-2517 @LW-7239 Scenario: Popup View - NFTs empty state @@ -17,21 +17,21 @@ Feature: Empty states Then I see empty state banner for NFTs page in popup mode And I do not see "Create folder" button on NFTs page in popup mode When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-4448 Scenario: Popup View - Transactions empty state When I navigate to Transactions popup page Then I see empty state banner for Transactions page in popup mode When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-8470 Scenario: Popup View - Staking empty state And I navigate to Staking popup page Then I see empty state banner for Staking page in popup mode When I click "Copy" button on empty state banner - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-5522 Scenario: Popup View - Settings - Not enough Ada for Collateral diff --git a/packages/e2e-tests/src/features/NavigationTopExtended.feature b/packages/e2e-tests/src/features/NavigationTopExtended.feature index 64480fa30..49a104135 100644 --- a/packages/e2e-tests/src/features/NavigationTopExtended.feature +++ b/packages/e2e-tests/src/features/NavigationTopExtended.feature @@ -38,7 +38,7 @@ Feature: Top Navigation - Extended view When I click the menu button Then the dropdown menu is visible When I click on the user details button - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-4598 @Testnet Scenario: Extended View - network id is visible for Testnet @@ -98,7 +98,7 @@ Feature: Top Navigation - Extended view When I click the menu button And I click on the network option When I click on "Preview" radio button - Then I see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I see a toast with text: "Switched network" Then Lace is loaded properly @LW-6074 @Testnet @Mainnet @@ -120,7 +120,7 @@ Feature: Top Navigation - Extended view Given I close wallet synced toast When I am in the offline network mode: true Then I see network id with status: offline - And I see a toast with message: "general.errors.networkError" + And I see a toast with text: "Network Error" When I click the menu button Then wallet sync status component is visible And sync status displays "Not synced to the blockchain" state diff --git a/packages/e2e-tests/src/features/NavigationTopPopup.feature b/packages/e2e-tests/src/features/NavigationTopPopup.feature index ec3240794..331761712 100644 --- a/packages/e2e-tests/src/features/NavigationTopPopup.feature +++ b/packages/e2e-tests/src/features/NavigationTopPopup.feature @@ -23,7 +23,7 @@ Feature: Top Navigation - Popup view When I click the menu button Then the dropdown menu is visible When I click on the user details button - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" @LW-4599 @Testnet Scenario: Popup View - network id is visible for Testnet @@ -73,7 +73,7 @@ Feature: Top Navigation - Popup view When I click the menu button And I click on the network option When I click on "Preview" radio button - Then I see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I see a toast with text: "Switched network" And Lace is loaded properly @LW-6079 @Testnet @Mainnet @@ -94,7 +94,7 @@ Feature: Top Navigation - Popup view Given I close wallet synced toast When I am in the offline network mode: true Then I see network id with status: offline - And I see a toast with message: "general.errors.networkError" + And I see a toast with text: "Network Error" When I click the menu button Then wallet sync status component is visible And sync status displays "Not synced to the blockchain" state diff --git a/packages/e2e-tests/src/features/SettingsPageExtended.feature b/packages/e2e-tests/src/features/SettingsPageExtended.feature index fc96c00e0..b3f0f35e8 100644 --- a/packages/e2e-tests/src/features/SettingsPageExtended.feature +++ b/packages/e2e-tests/src/features/SettingsPageExtended.feature @@ -65,7 +65,7 @@ Feature: General Settings - Extended Browser View And I click on "Your keys" setting And I click on Show public key button And I click "Copy" button on "Show public key" page - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" And I see that content of "TestAutomationWallet" public key is in clipboard @LW-2674 @Mainnet @Testnet @@ -79,7 +79,7 @@ Feature: General Settings - Extended Browser View When I open settings from header menu And I click on "Network" setting When I click on "Preprod" radio button - Then I don't see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I don't see a toast with text: "Switched network" And I close the drawer by clicking close button When I navigate to Tokens extended page Then I see network id: "Preprod" @@ -90,7 +90,7 @@ Feature: General Settings - Extended Browser View When I open settings from header menu And I click on "Network" setting When I click on "Mainnet" radio button - Then I don't see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I don't see a toast with text: "Switched network" And I close the drawer by clicking close button When I navigate to Tokens extended page Then I do not see network id: "Mainnet" @@ -126,7 +126,7 @@ Feature: General Settings - Extended Browser View Scenario: Extended View - Settings - Toast displayed after switching network Given I am on Settings extended page When I switch network to: "Preview" without closing drawer - Then I see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I see a toast with text: "Switched network" @LW-2718 @Testnet Scenario: Extended View - Settings - Token/NFTs updated after network switching diff --git a/packages/e2e-tests/src/features/SettingsPagePopup.feature b/packages/e2e-tests/src/features/SettingsPagePopup.feature index a4e357eb1..1ee2ba223 100644 --- a/packages/e2e-tests/src/features/SettingsPagePopup.feature +++ b/packages/e2e-tests/src/features/SettingsPagePopup.feature @@ -74,7 +74,7 @@ Feature: General Settings - Popup View And I click on "Your keys" setting And I click on Show public key button And I click "Copy" button on "Show public key" page - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" And I see that content of "TestAutomationWallet" public key is in clipboard @LW-2716 @Mainnet @Testnet @@ -88,7 +88,7 @@ Feature: General Settings - Popup View When I open settings from header menu And I click on "Network" setting When I click on "Preprod" radio button - Then I don't see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I don't see a toast with text: "Switched network" And I close the drawer by clicking back button When I navigate to Tokens popup page Then I see network id: "Preprod" @@ -99,7 +99,7 @@ Feature: General Settings - Popup View When I open settings from header menu And I click on "Network" setting When I click on "Mainnet" radio button - Then I don't see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I don't see a toast with text: "Switched network" And I close the drawer by clicking back button When I navigate to Tokens popup page Then I do not see network id: "Mainnet" @@ -153,7 +153,7 @@ Feature: General Settings - Popup View Scenario: Popup View - Settings - Toast displayed after switching network When I open settings from header menu When I switch network to: "Preview" without closing drawer - Then I see a toast with message: "browserView.settings.wallet.network.networkSwitched" + Then I see a toast with text: "Switched network" @LW-2719 @Testnet Scenario: Popup View - Settings - Token/NFTs updated after network switching diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature index 3fdc19bfe..edcb948a4 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionSimpleExtendedE2E.feature @@ -34,7 +34,7 @@ Feature: Send Simple Transactions - Extended view - E2E Scenario: Extended-view - Self Transaction E2E And I click "Receive" button on page header And I click "Copy" button on "Receive" page for default wallet address - Then I see a toast with message: "core.infoWallet.addressCopied" + Then I see a toast with text: "Address copied" And I close the drawer by clicking close button And I click "Send" button on page header And I fill bundle 1 with "CopiedAddress" address with following assets: @@ -60,7 +60,7 @@ Feature: Send Simple Transactions - Extended view - E2E When I click "View transaction" button on submitted transaction page And I click on a transaction: 1 And I click on a transaction hash - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" And I see 1 opened tab(s) When I wait for the transaction history to be loaded and all transactions to be confirmed And I click on a transaction hash and save hash information diff --git a/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature b/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature index 1b600cbe5..1a56b65de 100644 --- a/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature +++ b/packages/e2e-tests/src/features/e2e/SendTransactionSimplePopupE2E.feature @@ -34,7 +34,7 @@ Feature: Send Simple Transactions - Popup view - E2E Scenario: Popup-view - Self Transaction E2E And I click "Receive" button on Tokens page in popup mode And I click "Copy" button on "Receive" page for default wallet address - Then I see a toast with message: "core.infoWallet.addressCopied" + Then I see a toast with text: "Address copied" And I close the drawer by clicking close button And I click "Send" button on Tokens page in popup mode And I fill bundle 1 with "CopiedAddress" address with following assets: @@ -60,7 +60,7 @@ Feature: Send Simple Transactions - Popup view - E2E When I click "View transaction" button on submitted transaction page And I click on a transaction: 1 And I click on a transaction hash - Then I see a toast with message: "general.clipboard.copiedToClipboard" + Then I see a toast with text: "Copied to clipboard" And I see 1 opened tab(s) When I wait for the transaction history to be loaded and all transactions to be confirmed And I click on a transaction hash and save hash information diff --git a/packages/e2e-tests/src/steps/AddressBookSteps.ts b/packages/e2e-tests/src/steps/AddressBookSteps.ts index 9a8985edb..aecd75ada 100644 --- a/packages/e2e-tests/src/steps/AddressBookSteps.ts +++ b/packages/e2e-tests/src/steps/AddressBookSteps.ts @@ -366,25 +366,6 @@ When( } ); -Then( - /^I see a toast with text: "(Edited successfully|Address added)"$/, - async (action: 'Edited successfully' | 'Address added') => { - let translationKey; - switch (action) { - case 'Edited successfully': - translationKey = 'browserView.addressBook.toast.editAddress'; - break; - case 'Address added': - translationKey = addressAddedToastTranslationKey; - break; - default: - throw new Error(`Unsupported action name: ${action}`); - } - - await ToastMessageAssert.assertSeeToastMessage(await t(translationKey), true); - } -); - Then( /^I add address with name: "([^"]*)" and address: "([^"]*)" to address book in (extended|popup) mode$/, async (name: string, address: string, mode: 'extended' | 'popup') => { diff --git a/packages/e2e-tests/src/steps/commonSteps.ts b/packages/e2e-tests/src/steps/commonSteps.ts index 09fdb002a..c800155d6 100755 --- a/packages/e2e-tests/src/steps/commonSteps.ts +++ b/packages/e2e-tests/src/steps/commonSteps.ts @@ -26,6 +26,7 @@ import { } from '../utils/window'; import { Given } from '@wdio/cucumber-framework'; import tokensPageObject from '../pageobject/tokensPageObject'; +import ToastMessage from '../elements/toastMessage'; import menuMainAssert from '../assert/menuMainAssert'; import LocalStorageAssert from '../assert/localStorageAssert'; import ToastMessageAssert from '../assert/toastMessageAssert'; @@ -107,6 +108,10 @@ When( } ); +When(/^I close a toast message$/, async () => { + await ToastMessage.clickCloseButton(); +}); + // TODO: deprecated step, to be removed when remaining usages are replaced inside StakingPageDelegatedFundsExtended.feature Then(/(An|No) "([^"]*)" text is displayed/, async (expectedResult: string, expectedText: string) => { await $(`//*[contains(text(), "${(await t(expectedText)) ?? expectedText}")]`).waitForDisplayed({ @@ -125,10 +130,36 @@ Then( } ); -Then(/^I (see|don't see) a toast with message: "([^"]*)"$/, async (shouldSee: string, toastText: string) => { +Then(/^I (see|don't see) a toast with text: "([^"]*)"$/, async (shouldSee: string, toastText: string) => { await settingsExtendedPageObject.closeWalletSyncedToast(); - await ToastMessageAssert.assertSeeToastMessage(await t(toastText), shouldSee === 'see'); - if (toastText === 'general.clipboard.copiedToClipboard') Logger.log(`Clipboard contain: ${await clipboard.read()}`); + + const toastTextToTranslationKeyMap: { [key: string]: string } = { + 'Handle copied': 'core.infoWallet.handleCopied', + 'Address copied': 'core.infoWallet.addressCopied', + 'NFTs added to folder': 'browserView.nfts.folderDrawer.toast.update', + 'NFT removed': 'browserView.nfts.folderDrawer.toast.delete', + 'Folder created successfully': 'browserView.nfts.folderDrawer.toast.create', + 'Folder deleted successfully': 'browserView.nfts.deleteFolderSuccess', + 'Folder renamed successfully': 'browserView.nfts.renameFolderSuccess', + 'Edited successfully': 'browserView.addressBook.toast.editAddress', + 'Address added': 'browserView.addressBook.toast.addAddress', + 'Given address already exists': 'addressBook.errors.givenAddressAlreadyExist', + 'Given name already exists': 'addressBook.errors.givenNameAlreadyExist', + 'Switched network': 'browserView.settings.wallet.network.networkSwitched', + 'Network Error': 'general.errors.networkError', + 'Copied to clipboard': 'general.clipboard.copiedToClipboard' + }; + + const translationKey = toastTextToTranslationKeyMap[toastText]; + if (!translationKey) { + throw new Error(`Unsupported toast text: ${toastText}`); + } + + await ToastMessageAssert.assertSeeToastMessage(await t(translationKey), shouldSee === 'see'); + + if (translationKey === 'general.clipboard.copiedToClipboard') { + Logger.log(`Clipboard contain: ${await clipboard.read()}`); + } }); Then(/^I don't see any toast message$/, async () => { diff --git a/packages/e2e-tests/src/steps/nftFoldersSteps.ts b/packages/e2e-tests/src/steps/nftFoldersSteps.ts index 7ea097384..bf505b967 100644 --- a/packages/e2e-tests/src/steps/nftFoldersSteps.ts +++ b/packages/e2e-tests/src/steps/nftFoldersSteps.ts @@ -5,8 +5,6 @@ import NftCreateFolderPage from '../elements/NFTs/nftCreateFolderPage'; import YoullHaveToStartAgainModal from '../elements/NFTs/youllHaveToStartAgainModal'; import mainMenuPageObject from '../pageobject/mainMenuPageObject'; import NftSelectNftsPage from '../elements/NFTs/nftSelectNftsPage'; -import ToastMessageAssert from '../assert/toastMessageAssert'; -import { t } from '../utils/translationService'; import NftSelectNftsAssert from '../assert/nftSelectNftsAssert'; import IndexedDB from '../fixture/indexedDB'; import { NFTFolder } from '../data/NFTFolder'; @@ -237,36 +235,6 @@ Then( } ); -Then( - /^I see a toast with text: "Folder (created|renamed|deleted) successfully"$/, - async (action: 'created' | 'deleted' | 'renamed') => { - let translationKey; - switch (action) { - case 'created': - translationKey = 'browserView.nfts.folderDrawer.toast.create'; - break; - case 'deleted': - translationKey = 'browserView.nfts.deleteFolderSuccess'; - break; - case 'renamed': - translationKey = 'browserView.nfts.renameFolderSuccess'; - break; - default: - throw new Error(`Unsupported action name: ${action}`); - } - - await ToastMessageAssert.assertSeeToastMessage(await t(translationKey), true); - } -); - -Then(/^I see a toast with text: "NFTs added to folder"$/, async () => { - await ToastMessageAssert.assertSeeToastMessage(await t('browserView.nfts.folderDrawer.toast.update'), true); -}); - -Then(/^I see a toast with text: "NFT removed"$/, async () => { - await ToastMessageAssert.assertSeeToastMessage(await t('browserView.nfts.folderDrawer.toast.delete'), true); -}); - Then(/^I select (\d+) NFTs$/, async (numberOfNFTs: number) => { await NftSelectNftsPage.selectNFTs(numberOfNFTs); }); diff --git a/packages/e2e-tests/src/steps/waletAddressPageSteps.ts b/packages/e2e-tests/src/steps/waletAddressPageSteps.ts index 252bd22f2..91a01f613 100644 --- a/packages/e2e-tests/src/steps/waletAddressPageSteps.ts +++ b/packages/e2e-tests/src/steps/waletAddressPageSteps.ts @@ -2,9 +2,6 @@ import { Then, When } from '@wdio/cucumber-framework'; import { getTestWallet } from '../support/walletConfiguration'; import walletAddressPageAssert from '../assert/walletAddressPageAssert'; import walletAddressPage from '../elements/walletAddressPage'; -import ToastMessageAssert from '../assert/toastMessageAssert'; -import { t } from '../utils/translationService'; -import ToastMessage from '../elements/toastMessage'; import MenuHeader from '../elements/menuHeader'; When(/^I see handles listed on the "Receive" screen$/, async () => { @@ -48,24 +45,6 @@ Then(/^The first ADA handle displayed on the list is the shortest$/, async () => await walletAddressPageAssert.assertSeeTheShortestHandleFirst(); }); -Then(/^I see a toast with text: "(Handle|Address) copied"$/, async (action: string) => { - let translationKey; - switch (action) { - case 'Handle': - translationKey = 'core.infoWallet.handleCopied'; - break; - case 'Address': - translationKey = 'core.infoWallet.addressCopied'; - break; - default: - throw new Error(`Unsupported action name: ${action}`); - } - - await ToastMessageAssert.assertSeeToastMessage(await t(translationKey), true); - await ToastMessage.clickCloseButton(); - await ToastMessageAssert.assertSeeToastMessage(await t(translationKey), false); -}); - Then(/^I see ADA handle with custom image on the "Wallet Address" page$/, async () => { await walletAddressPageAssert.assertSeeAdaHandleCardWithCustomImage(); }); From d7d5505b81f7ec4d96a6b9be4cc338f17da8fa79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Mas=C5=82owski?= Date: Mon, 15 Apr 2024 11:25:47 +0200 Subject: [PATCH 56/74] feat(extension,cardano,core): LW-9808 revamp hardware wallet onboarding (#1020) * feat(extension,cardano,core): revamp hardware wallet connection in onboarding * feat(extension,cardano,staking): bump hardware-ledger, web-extension and wallet SDK packages * feat(extension): changes after self review * feat(extension): improve error cases * feat(extension): improve error handling in creation and reorganize the code * feat(cardano): allow only supported ledger devices * test(extension): fix unit tests of useWalletManager hook * refactor(extension): use enums insted of string unions * feat(extension): update hw walet creation generic error message * feat(extension,cardano,core): add unit tests, restrict allowed HW devices * refactor(extension): remove redundant eslint disable comment * fix(extension): fix typo --------- Co-authored-by: Emir Hodzic --- apps/browser-extension-wallet/package.json | 4 +- .../hooks/__tests__/useWalletManager.test.tsx | 8 +- .../src/hooks/useWalletManager.ts | 96 ++++---- .../src/lib/translations/en.json | 34 ++- .../features/multi-wallet/MultiWallet.tsx | 8 +- .../hardware-wallet/HardwareWallet.test.tsx | 6 +- .../multi-wallet/hardware-wallet/context.tsx | 2 +- .../hardware-wallet/steps/Connect.tsx | 3 +- .../hardware-wallet/steps/ErrorHandling.tsx | 22 +- .../multi-wallet/hardware-wallet/types.ts | 2 +- ...isMultidelegationSupportedByDevice.test.ts | 71 ++++++ .../browser-view/features/staking/helpers.ts | 50 ---- .../browser-view/features/staking/index.ts | 2 +- .../isMultidelegationSupportedByDevice.ts | 30 +++ .../wallet-setup/components/ErrorDialog.tsx | 59 ----- .../components/HardwareWalletFlow.tsx | 225 ------------------ .../HardwareWalletFlow/HardwareWalletFlow.tsx | 172 +++++++++++++ .../HardwareWalletFlow/StepConnect.tsx | 138 +++++++++++ .../HardwareWalletFlow/StepCreate.tsx | 95 ++++++++ .../components/HardwareWalletFlow/index.ts | 2 + .../makeErrorDialog.module.scss} | 4 +- .../HardwareWalletFlow/makeErrorDialog.tsx | 43 ++++ .../wallet-setup/components/WalletSetup.tsx | 6 +- .../components/WalletSetupMainPage.tsx | 22 +- .../features/wallet-setup/helpers.ts | 56 ----- packages/cardano/package.json | 8 +- .../cardano/src/wallet/lib/hardware-wallet.ts | 150 +++++++++++- packages/cardano/src/wallet/types.ts | 20 +- .../WalletSetup/WalletSetupCreationStep.tsx | 28 --- .../src/ui/components/WalletSetup/index.ts | 1 - ...onnectHardwareWalletStepRevamp.module.scss | 20 ++ ...etSetupConnectHardwareWalletStepRevamp.tsx | 119 +++------ .../WalletSetupHWCreationStep.module.scss | 11 + .../WalletSetupHWCreationStep.tsx | 25 ++ .../WalletSetupNamePasswordStepRevamp.tsx | 2 +- .../WalletSetupSelectAccountsStepRevamp.tsx | 2 - .../WalletSetupStepLayoutRevamp.tsx | 23 +- .../ui/components/WalletSetupRevamp/index.ts | 5 +- packages/staking/package.json | 6 +- .../StakePoolConfirmationFooter.tsx | 23 +- yarn.lock | 107 ++++++--- 41 files changed, 1023 insertions(+), 687 deletions(-) create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/staking/__tests__/isMultidelegationSupportedByDevice.test.ts delete mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/staking/isMultidelegationSupportedByDevice.ts delete mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.tsx delete mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepCreate.tsx create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/index.ts rename apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/{ErrorDialog.module.scss => HardwareWalletFlow/makeErrorDialog.module.scss} (84%) create mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.tsx delete mode 100644 apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/helpers.ts delete mode 100644 packages/core/src/ui/components/WalletSetup/WalletSetupCreationStep.tsx create mode 100644 packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.module.scss create mode 100644 packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.module.scss create mode 100644 packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.tsx diff --git a/apps/browser-extension-wallet/package.json b/apps/browser-extension-wallet/package.json index cdc8667ac..4935b1ce5 100644 --- a/apps/browser-extension-wallet/package.json +++ b/apps/browser-extension-wallet/package.json @@ -45,8 +45,8 @@ "@cardano-sdk/input-selection": "0.12.26", "@cardano-sdk/tx-construction": "0.18.2", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.2", - "@cardano-sdk/web-extension": "0.26.1", + "@cardano-sdk/wallet": "0.36.0", + "@cardano-sdk/web-extension": "0.26.2", "@emurgo/cip14-js": "~3.0.1", "@koralabs/handles-public-api-interfaces": "^1.6.6", "@lace/cardano": "0.1.0", diff --git a/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx b/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx index 62d6c0510..102decfc5 100644 --- a/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx +++ b/apps/browser-extension-wallet/src/hooks/__tests__/useWalletManager.test.tsx @@ -13,6 +13,7 @@ import SpyInstance = jest.SpyInstance; const mockEmip3decrypt = jest.fn(); const mockEmip3encrypt = jest.fn(); const mockConnectDevice = jest.fn(); +const mockGetHwExtendedAccountPublicKey = jest.fn(); const mockRestoreWalletFromKeyAgent = jest.fn(); const mockSwitchKeyAgents = jest.fn(); const mockLedgerCheckDeviceConnection = jest.fn(); @@ -80,6 +81,7 @@ jest.mock('@lace/cardano', () => { restoreWalletFromKeyAgent: mockRestoreWalletFromKeyAgent, switchKeyAgents: mockSwitchKeyAgents, connectDevice: mockConnectDevice, + getHwExtendedAccountPublicKey: mockGetHwExtendedAccountPublicKey, KeyManagement: { ...actual.Wallet.KeyManagement, emip3decrypt: mockEmip3decrypt, @@ -382,7 +384,7 @@ describe('Testing useWalletManager hook', () => { describe('createHardwareWallet', () => { test('should use cardano manager to create wallet', async () => { const walletId = 'walletId'; - mockLedgerGetXpub.mockResolvedValue('pubkey'); + mockGetHwExtendedAccountPublicKey.mockResolvedValue('pubkey'); (walletApiUi.walletRepository as any).addWallet = jest.fn().mockResolvedValue(walletId); (walletApiUi.walletRepository as any).addAccount = jest.fn().mockResolvedValue(undefined); (walletApiUi.walletManager as any).activate = jest.fn().mockResolvedValue(undefined); @@ -696,11 +698,11 @@ describe('Testing useWalletManager hook', () => { }, { type: WalletType.Trezor, - prepare: () => mockTrezorGetXpub.mockResolvedValueOnce(extendedAccountPublicKey) + prepare: () => mockGetHwExtendedAccountPublicKey.mockResolvedValueOnce(extendedAccountPublicKey) }, { type: WalletType.Ledger, - prepare: () => mockLedgerGetXpub.mockResolvedValueOnce(extendedAccountPublicKey) + prepare: () => mockGetHwExtendedAccountPublicKey.mockResolvedValueOnce(extendedAccountPublicKey) } ]; diff --git a/apps/browser-extension-wallet/src/hooks/useWalletManager.ts b/apps/browser-extension-wallet/src/hooks/useWalletManager.ts index 7d230bdff..27b3c8d87 100644 --- a/apps/browser-extension-wallet/src/hooks/useWalletManager.ts +++ b/apps/browser-extension-wallet/src/hooks/useWalletManager.ts @@ -44,12 +44,6 @@ export interface CreateWallet { chainId?: Wallet.Cardano.ChainId; } -export interface SetWallet { - walletInstance: Wallet.CardanoWallet; - chainName?: Wallet.ChainName; - mnemonicVerificationFrequency?: string; -} - export interface CreateHardwareWallet { accountIndex?: number; name: string; @@ -66,6 +60,14 @@ type WalletManagerAddAccountProps = { type ActivateWalletProps = Omit; +type CreateHardwareWalletRevampedParams = { + accountIndex: number; + name: string; + connection: Wallet.HardwareWalletConnection; +}; + +type CreateHardwareWalletRevamped = (params: CreateHardwareWalletRevampedParams) => Promise; + export interface UseWalletManager { walletManager: WalletManagerApi; walletRepository: WalletRepositoryApi; @@ -78,7 +80,9 @@ export interface UseWalletManager { createWallet: (args: CreateWallet) => Promise; activateWallet: (args: Omit) => Promise; createHardwareWallet: (args: CreateHardwareWallet) => Promise; + createHardwareWalletRevamped: CreateHardwareWalletRevamped; connectHardwareWallet: (model: Wallet.HardwareWallets) => Promise; + connectHardwareWalletRevamped: typeof connectHardwareWalletRevamped; saveHardwareWallet: (wallet: Wallet.CardanoWallet, chainName?: Wallet.ChainName) => Promise; /** * @returns active wallet id after deleting the wallet; undefined if deleted the last wallet @@ -95,27 +99,6 @@ const clearBytes = (bytes: Uint8Array) => { } }; -const getHwExtendedAccountPublicKey = async ( - walletType: Wallet.HardwareWallets, - accountIndex: number, - deviceConnection?: Wallet.DeviceConnection -) => { - switch (walletType) { - case WalletType.Ledger: - await Wallet.Ledger.LedgerKeyAgent.checkDeviceConnection(Wallet.KeyManagement.CommunicationType.Web); - return Wallet.Ledger.LedgerKeyAgent.getXpub({ - communicationType: Wallet.KeyManagement.CommunicationType.Web, - deviceConnection: typeof deviceConnection !== 'boolean' ? deviceConnection : undefined, - accountIndex - }); - case WalletType.Trezor: - return Wallet.Trezor.TrezorKeyAgent.getXpub({ - communicationType: Wallet.KeyManagement.CommunicationType.Web, - accountIndex - }); - } -}; - const getExtendedAccountPublicKey = async ( wallet: AnyBip32Wallet, accountIndex: number, @@ -143,7 +126,7 @@ const getExtendedAccountPublicKey = async ( } case WalletType.Ledger: case WalletType.Trezor: - return getHwExtendedAccountPublicKey(wallet.type, accountIndex); + return Wallet.getHwExtendedAccountPublicKey(wallet.type, accountIndex); } }; @@ -210,6 +193,9 @@ const encryptMnemonic = async (mnemonic: string[], passphrase: Uint8Array) => { export const connectHardwareWallet = async (model: Wallet.HardwareWallets): Promise => await Wallet.connectDevice(model); +const connectHardwareWalletRevamped = async (usbDevice: USBDevice): Promise => + Wallet.connectDeviceRevamped(usbDevice); + export const useWalletManager = (): UseWalletManager => { const { walletLock, @@ -241,25 +227,21 @@ export const useWalletManager = (): UseWalletManager => { return (storedChain?.chainName && chainIdFromName(storedChain.chainName)) || DEFAULT_CHAIN_ID; }, [currentChain]); - /** - * Creates a Ledger or Trezor hardware wallet - * and saves it in browser storage with the data to lock/unlock it - */ - const createHardwareWallet = useCallback( - async ({ - accountIndex = 0, - deviceConnection, - name, - connectedDevice - }: CreateHardwareWallet): Promise => { - const extendedAccountPublicKey = await getHwExtendedAccountPublicKey( - connectedDevice, - accountIndex, - deviceConnection - ); + const createHardwareWalletRevamped = useCallback( + async ({ accountIndex, connection, name }) => { + let extendedAccountPublicKey; + try { + extendedAccountPublicKey = await Wallet.getHwExtendedAccountPublicKey( + connection.type, + accountIndex, + connection.type === WalletType.Ledger ? connection.value : undefined + ); + } catch (error: unknown) { + throw error; + } const addWalletProps: AddWalletProps = { metadata: { name, lastActiveAccountIndex: accountIndex }, - type: connectedDevice, + type: connection.type, accounts: [ { extendedAccountPublicKey, @@ -291,6 +273,28 @@ export const useWalletManager = (): UseWalletManager => { [getCurrentChainId] ); + /** + * Creates a Ledger or Trezor hardware wallet + * and saves it in browser storage with the data to lock/unlock it + */ + const createHardwareWallet = useCallback( + async ({ + accountIndex = 0, + deviceConnection, + name, + connectedDevice + }: CreateHardwareWallet): Promise => + createHardwareWalletRevamped({ + accountIndex, + connection: { + type: connectedDevice, + value: typeof deviceConnection !== 'boolean' ? deviceConnection : undefined + }, + name + }), + [createHardwareWalletRevamped] + ); + const tryMigrateToWalletRepository = useCallback(async (): Promise< AnyWallet[] | undefined > => { @@ -742,7 +746,9 @@ export const useWalletManager = (): UseWalletManager => { loadWallet, createWallet, createHardwareWallet, + createHardwareWalletRevamped, connectHardwareWallet, + connectHardwareWalletRevamped, saveHardwareWallet, deleteWallet, switchNetwork, diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 06bcf0320..5a6150caf 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -494,9 +494,6 @@ "browserView.walletSetup.mnemonicResetModal.content": "In order to keep you safe, we'll show you a new set of 24 words.", "browserView.walletSetup.mnemonicResetModal.cancel": "Cancel", "browserView.walletSetup.mnemonicResetModal.confirm": "OK", - "browserView.walletSetup.confirmExperimentalHwDapp.header": "Limited support for Dapp connection", - "browserView.walletSetup.confirmExperimentalHwDapp.content": "This current version does not support signing transactions through the Dapp connection feature with hardware wallets. Please stay tuned for upcoming releases and new features through @lace on Twitter.", - "browserView.walletSetup.confirmExperimentalHwDapp.confirm": "OK", "browserView.crypto.emptyDashboard.welcome": "Welcome", "browserView.crypto.emptyDashboard.addSomeFundsYoStartYourJourney": "Add some funds to start your journey", "browserView.crypto.emptyDashboard.useThisAddressOrScanTheQRCodeToTransferFunds": "Use this address or scan the QR code to transfer funds", @@ -647,13 +644,11 @@ "browserView.staking.details.noFundsModal.buttons.confirm": "Add funds", "browserView.staking.details.errors.utxoFullyDepleted": "UTxO has been fully depleted", "browserView.staking.details.errors.utxoBalanceInsufficient": "Balance Insufficient", - "browserView.onboarding.commonError.title": "Oops! Something went wrong", - "browserView.onboarding.commonError.description": "Please check your hardware device.", - "browserView.onboarding.commonError.ok": "OK", - "browserView.onboarding.notDetectedError.title": "Failed to detect device", - "browserView.onboarding.notDetectedError.description": "Please make sure your device is unlocked and the Cardano app is open.", - "browserView.onboarding.notDetectedError.agree": "Agree", - "browserView.onboarding.notDetectedError.trezorDescription": "Please make sure your device is unlocked.", + "browserView.onboarding.errorDialog.title": "Opps! Something went wrong", + "browserView.onboarding.errorDialog.cta": "OK", + "browserView.onboarding.errorDialog.messageDeviceDisconnected": "Please check your hardware device connection and for Ledger, if Cardano App is open", + "browserView.onboarding.errorDialog.messagePublicKeyExportRejected": "Public key export unsuccessful. User declined action on hardware wallet device.", + "browserView.onboarding.errorDialog.messageGeneric": "Try connecting you device again", "browserView.onboarding.startOver.title": "Are you sure you want to start again?", "browserView.onboarding.startOver.description": "Connection to this device will be cancelled and you will need to re-connect.", "browserView.onboarding.startOver.cancel": "Cancel", @@ -1263,6 +1258,18 @@ "core.walletSetupConnectHardwareWalletStep.supportedDevices": "Lace is supporting Ledger Nano X, Nano S and Nano S Plus", "core.walletSetupConnectHardwareWalletStep.connectDevice": "Just connect your device to your computer, unlock and open the Cardano app to hit continue.", "core.walletSetupConnectHardwareWalletStep.connectDeviceFull": "Just connect your device to your computer and unlock it to continue. If you're using a Ledger device, make sure you open the Cardano App.", + "core.walletSetupConnectHardwareWalletStepRevamp.title": "Connect your device", + "core.walletSetupConnectHardwareWalletStepRevamp.subTitleLedgerOnly": "Lace supports Ledger Nano X, Nano S, Nano S Plus. Unlock your device and open the Cardano app.", + "core.walletSetupConnectHardwareWalletStepRevamp.subTitle": "Lace supports Ledger Nano X, Nano S, Nano S Plus, Trezor Model T. Unlock your device and for Ledger, open the Cardano app.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.userGestureRequired": "It appears the page reload interrupted the search for connected hardware wallet devices.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.devicePickerRejected": "No hardware wallet device was chosen.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.deviceLocked": "Your hardware wallet device seems to be locked. Please unlock it to proceed.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.deviceBusy": "Your hardware wallet device seems to be busy with some other App. Please ensure it is free to connect.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.cardanoAppNotOpen": "Cardano App is not open on your hardware wallet device. Please open it to proceed.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.generic": "Something went wrong. Please try again.", + "core.walletSetupConnectHardwareWalletStepRevamp.errorCta": "Try again", + "core.walletSetupCreateStep.title": "Creating your wallet", + "core.walletSetupCreateStep.description": "Confirm exporting your public key on your hardware wallet device to create your Lace wallet.", "core.walletSetupRestoreStep.title": "Restoring your wallet", "core.walletSetupMnemonicStep.writePassphrase": "Write down your secret passphrase", "core.walletSetupMnemonicStep.enterPassphrase": "Enter your secret passphrase", @@ -1392,6 +1399,13 @@ "multiWallet.confirmationDialog.description": "You'll have to start over.", "multiWallet.confirmationDialog.cancel": "Go back", "multiWallet.confirmationDialog.confirm": "Proceed", + "multiWallet.errorDialog.commonError.title": "Oops! Something went wrong", + "multiWallet.errorDialog.commonError.description": "Please check your hardware device.", + "multiWallet.errorDialog.commonError.ok": "OK", + "multiWallet.errorDialog.notDetectedError.title": "Failed to detect device", + "multiWallet.errorDialog.notDetectedError.description": "Please make sure your device is unlocked and the Cardano app is open.", + "multiWallet.errorDialog.notDetectedError.agree": "Agree", + "multiWallet.errorDialog.notDetectedError.trezorDescription": "Please make sure your device is unlocked.", "multiWallet.activated.wallet": "Wallet \"{{walletName}}\" activated", "multiWallet.activated.account": "Account \"{{accountName}}\" activated", "multiWallet.popupHwAccountEnable": "Hardware wallets require the <0>expanded view to enable accounts", diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx index 1674ddf71..10865d408 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/MultiWallet.tsx @@ -42,7 +42,7 @@ export const SetupHardwareWallet = ({ shouldShowDialog$ }: ConfirmationDialog): const { t } = useTranslation(); const { connectHardwareWallet, createHardwareWallet, walletRepository } = useWalletManager(); const analytics = useAnalyticsContext(); - const disconnectHardwareWallet$ = useMemo(() => new Subject(), []); + const disconnectHardwareWallet$ = useMemo(() => new Subject(), []); const hardwareWalletProviders = useMemo( (): Providers => ({ @@ -83,14 +83,14 @@ export const SetupHardwareWallet = ({ shouldShowDialog$ }: ConfirmationDialog): ); useEffect(() => { - const onHardwareWalletDisconnect = (event: HIDConnectionEvent) => { + const onHardwareWalletDisconnect = (event: USBConnectionEvent) => { disconnectHardwareWallet$.next(event); }; - navigator.hid.addEventListener('disconnect', onHardwareWalletDisconnect); + navigator.usb.addEventListener('disconnect', onHardwareWalletDisconnect); return () => { - navigator.hid.removeEventListener('disconnect', onHardwareWalletDisconnect); + navigator.usb.removeEventListener('disconnect', onHardwareWalletDisconnect); disconnectHardwareWallet$.complete(); }; }, [disconnectHardwareWallet$]); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/HardwareWallet.test.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/HardwareWallet.test.tsx index 175f0eb64..2f36a529f 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/HardwareWallet.test.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/HardwareWallet.test.tsx @@ -59,7 +59,7 @@ describe('Multi Wallet Setup/Hardware Wallet', () => { let providers = {} as { connectHardwareWallet: jest.Mock; createWallet: jest.Mock; - disconnectHardwareWallet$: Subject; + disconnectHardwareWallet$: Subject; shouldShowDialog$: Subject; }; @@ -67,7 +67,7 @@ describe('Multi Wallet Setup/Hardware Wallet', () => { providers = { connectHardwareWallet: jest.fn(), createWallet: jest.fn(), - disconnectHardwareWallet$: new Subject(), + disconnectHardwareWallet$: new Subject(), shouldShowDialog$: new Subject() }; }); @@ -101,7 +101,7 @@ describe('Multi Wallet Setup/Hardware Wallet', () => { await selectAccountStep(); act(() => { - providers.disconnectHardwareWallet$.next({ device: { opened: true } } as HIDConnectionEvent); + providers.disconnectHardwareWallet$.next({ device: { opened: true } } as USBConnectionEvent); }); await waitFor(() => expect(screen.queryByText('Oops! Something went wrong')).toBeInTheDocument()); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/context.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/context.tsx index f8a9eb89e..767db218a 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/context.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/context.tsx @@ -15,7 +15,7 @@ interface State { setAccount: (account: number) => void; resetConnection: () => void; createWallet: () => Promise; - disconnectHardwareWallet$: Observable; + disconnectHardwareWallet$: Observable; } // eslint-disable-next-line unicorn/no-null diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/Connect.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/Connect.tsx index 542f35c05..fdf64c8ab 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/Connect.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/Connect.tsx @@ -2,7 +2,6 @@ import { WalletSetupConnectHardwareWalletStep } from '@lace/core'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useHistory } from 'react-router'; -import { isTrezorHWSupported } from '../../../wallet-setup/helpers'; import { Wallet } from '@lace/cardano'; import { useHardwareWallet } from '../context'; import { walletRoutePaths } from '@routes'; @@ -11,6 +10,8 @@ import { WalletType } from '@cardano-sdk/web-extension'; import { useAnalyticsContext } from '@providers'; import { PostHogAction } from '@lace/common'; +export const isTrezorHWSupported = (): boolean => process.env.USE_TREZOR_HW === 'true'; + interface State { error?: 'notDetectedLedger' | 'notDetectedTrezor'; } diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/ErrorHandling.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/ErrorHandling.tsx index 957dfe276..de4b88589 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/ErrorHandling.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/steps/ErrorHandling.tsx @@ -1,11 +1,29 @@ import React, { useEffect, useState } from 'react'; import { useHistory } from 'react-router'; import { useHardwareWallet } from '../context'; -import { ErrorDialog } from '../../../wallet-setup/components/ErrorDialog'; +import { makeErrorDialog } from '../../../wallet-setup/components/HardwareWalletFlow'; import { walletRoutePaths } from '@routes'; type Errors = 'notDetectedLedger' | 'notDetectedTrezor' | 'common'; +const ErrorDialog = makeErrorDialog({ + common: { + title: 'multiWallet.errorDialog.commonError.title', + description: 'multiWallet.errorDialog.commonError.description', + confirm: 'multiWallet.errorDialog.commonError.ok' + }, + notDetectedLedger: { + title: 'multiWallet.errorDialog.notDetectedError.title', + description: 'multiWallet.errorDialog.notDetectedError.description', + confirm: 'multiWallet.errorDialog.notDetectedError.agree' + }, + notDetectedTrezor: { + title: 'multiWallet.errorDialog.notDetectedError.title', + description: 'multiWallet.errorDialog.notDetectedError.trezorDescription', + confirm: 'multiWallet.errorDialog.notDetectedError.agree' + } +}); + interface State { error?: Errors; } @@ -21,7 +39,7 @@ export const ErrorHandling = ({ error, onRetry }: Props): JSX.Element => { const [state, setState] = useState({}); useEffect(() => { - const subscription = disconnectHardwareWallet$.subscribe((event: HIDConnectionEvent) => { + const subscription = disconnectHardwareWallet$.subscribe((event: USBConnectionEvent) => { if (event.device.opened) { setState({ error: 'common' }); } diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/types.ts b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/types.ts index c8105b223..9c9a130a6 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/types.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/multi-wallet/hardware-wallet/types.ts @@ -11,6 +11,6 @@ export interface Data { export interface Providers { createWallet: (params: Data) => Promise; connectHardwareWallet: (model: Wallet.HardwareWallets) => Promise; - disconnectHardwareWallet$: Observable; + disconnectHardwareWallet$: Observable; shouldShowDialog$: Subject; } diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/__tests__/isMultidelegationSupportedByDevice.test.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/__tests__/isMultidelegationSupportedByDevice.test.ts new file mode 100644 index 000000000..f64d9d889 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/__tests__/isMultidelegationSupportedByDevice.test.ts @@ -0,0 +1,71 @@ +/* eslint-disable import/imports-first */ +const initConnectionAndGetSoftwareVersionMock = jest.fn(); + +import { + LedgerMultidelegationMinAppVersion, + isMultidelegationSupportedByDevice +} from '../isMultidelegationSupportedByDevice'; +import { WalletType } from '@cardano-sdk/web-extension'; + +jest.mock('@lace/cardano', () => ({ + Wallet: { + initConnectionAndGetSoftwareVersion: initConnectionAndGetSoftwareVersionMock + } +})); + +describe('isMultidelegationSupportedByDevice', () => { + it('calls Wallet.isMultidelegationSupportedByDevice to get a version', async () => { + initConnectionAndGetSoftwareVersionMock.mockResolvedValue({ + major: 0, + minor: 0, + patch: 0 + }); + await isMultidelegationSupportedByDevice(WalletType.Ledger); + expect(initConnectionAndGetSoftwareVersionMock).toHaveBeenCalledWith(WalletType.Ledger); + }); + + it('returns false if the version is lower than expected', async () => { + initConnectionAndGetSoftwareVersionMock.mockResolvedValue({ + major: 0, + minor: 0, + patch: 0 + }); + expect(await isMultidelegationSupportedByDevice(WalletType.Trezor)).toEqual(false); + }); + + it('returns true if the version is greater on the major level', async () => { + initConnectionAndGetSoftwareVersionMock.mockResolvedValue({ + major: LedgerMultidelegationMinAppVersion.MAJOR + 1, + minor: 0, + patch: 0 + }); + expect(await isMultidelegationSupportedByDevice(WalletType.Ledger)).toEqual(true); + }); + + it('returns true if the version is greater on the minor level', async () => { + initConnectionAndGetSoftwareVersionMock.mockResolvedValue({ + major: LedgerMultidelegationMinAppVersion.MAJOR, + minor: LedgerMultidelegationMinAppVersion.MINOR + 1, + patch: 0 + }); + expect(await isMultidelegationSupportedByDevice(WalletType.Ledger)).toEqual(true); + }); + + it('returns true if the version is greater on the patch level', async () => { + initConnectionAndGetSoftwareVersionMock.mockResolvedValue({ + major: LedgerMultidelegationMinAppVersion.MAJOR, + minor: LedgerMultidelegationMinAppVersion.MINOR, + patch: LedgerMultidelegationMinAppVersion.PATCH + 1 + }); + expect(await isMultidelegationSupportedByDevice(WalletType.Ledger)).toEqual(true); + }); + + it('returns true if the version is equal to the minimal required', async () => { + initConnectionAndGetSoftwareVersionMock.mockResolvedValue({ + major: LedgerMultidelegationMinAppVersion.MAJOR, + minor: LedgerMultidelegationMinAppVersion.MINOR, + patch: LedgerMultidelegationMinAppVersion.PATCH + }); + expect(await isMultidelegationSupportedByDevice(WalletType.Trezor)).toEqual(true); + }); +}); diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts deleted file mode 100644 index 77452c9f9..000000000 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/helpers.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* eslint-disable no-magic-numbers */ -import { Wallet } from '@lace/cardano'; -import * as HardwareLedger from '@cardano-sdk/hardware-ledger'; -import * as HardwareTrezor from '@cardano-sdk/hardware-trezor'; -import { WalletType } from '@cardano-sdk/web-extension'; - -export enum LedgerMultidelegationMinAppVersion { - MAJOR = 6, - MINOR = 1, - PATCH = 2 -} - -export enum TrezorMultidelegationFirmwareMinVersion { - MAJOR = 2, - MINOR = 6, - PATCH = 4 -} - -export const isMultidelegationSupportedByDevice = async ( - walletType: Exclude -): Promise => { - switch (walletType) { - case WalletType.Ledger: { - const ledgerInfo = await HardwareLedger.LedgerKeyAgent.getAppVersion(Wallet.KeyManagement.CommunicationType.Web); - return ( - ledgerInfo.version.major >= LedgerMultidelegationMinAppVersion.MAJOR && - ledgerInfo.version.minor >= LedgerMultidelegationMinAppVersion.MINOR && - ledgerInfo.version.patch >= LedgerMultidelegationMinAppVersion.PATCH - ); - } - case WalletType.Trezor: { - // To allow checks once the app is refreshed. It won't affect the user flow - // TODO: Smarter Trezor initialization logic after onboarding revamp LW-9808 - await HardwareTrezor.TrezorKeyAgent.initializeTrezorTransport({ - manifest: Wallet.manifest, - communicationType: Wallet.KeyManagement.CommunicationType.Web - }); - const trezorInfo = await HardwareTrezor.TrezorKeyAgent.checkDeviceConnection( - Wallet.KeyManagement.CommunicationType.Web - ); - return ( - trezorInfo.major_version >= TrezorMultidelegationFirmwareMinVersion.MAJOR && - trezorInfo.minor_version >= TrezorMultidelegationFirmwareMinVersion.MINOR && - trezorInfo.patch_version >= TrezorMultidelegationFirmwareMinVersion.PATCH - ); - } - default: - return true; - } -}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/index.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/index.ts index 2ac30533f..5d897b296 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/staking/index.ts +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/index.ts @@ -1,2 +1,2 @@ export * from './components/StakingContainer'; -export * from './helpers'; +export * from './isMultidelegationSupportedByDevice'; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/staking/isMultidelegationSupportedByDevice.ts b/apps/browser-extension-wallet/src/views/browser-view/features/staking/isMultidelegationSupportedByDevice.ts new file mode 100644 index 000000000..c31f2fda0 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/staking/isMultidelegationSupportedByDevice.ts @@ -0,0 +1,30 @@ +/* eslint-disable no-magic-numbers */ +import { Wallet } from '@lace/cardano'; +import { WalletType } from '@cardano-sdk/web-extension'; + +export enum LedgerMultidelegationMinAppVersion { + MAJOR = 6, + MINOR = 1, + PATCH = 2 +} + +export enum TrezorMultidelegationFirmwareMinVersion { + MAJOR = 2, + MINOR = 6, + PATCH = 4 +} + +export const isMultidelegationSupportedByDevice = async (walletType: Wallet.HardwareWallets): Promise => { + const version = await Wallet.initConnectionAndGetSoftwareVersion(walletType); + const expectedVersion = + walletType === WalletType.Ledger ? LedgerMultidelegationMinAppVersion : TrezorMultidelegationFirmwareMinVersion; + + const higherOnMajor = version.major > expectedVersion.MAJOR; + const higherOnMinor = version.major === expectedVersion.MAJOR && version.minor > expectedVersion.MINOR; + const higherOrEqualOnPatch = + version.major === expectedVersion.MAJOR && + version.minor === expectedVersion.MINOR && + version.patch >= expectedVersion.PATCH; + + return higherOnMajor || higherOnMinor || higherOrEqualOnPatch; +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.tsx deleted file mode 100644 index ea823c23a..000000000 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import { Modal, Typography } from 'antd'; -import { Button } from '@lace/common'; -import styles from './ErrorDialog.module.scss'; -import { useTranslation } from 'react-i18next'; - -const { Title, Text } = Typography; - -export type HWErrorCode = 'common' | 'notDetectedLedger' | 'notDetectedTrezor'; - -interface ErrorDialogProps { - visible: boolean; - onRetry: () => void; - errorCode: HWErrorCode; -} - -export const ErrorDialog = ({ visible, onRetry, errorCode = 'common' }: ErrorDialogProps): React.ReactElement => { - const { t } = useTranslation(); - - const ERROR_MESSAGES = { - common: { - title: t('browserView.onboarding.commonError.title'), - description: t('browserView.onboarding.commonError.description'), - confirm: t('browserView.onboarding.commonError.ok') - }, - notDetectedLedger: { - title: t('browserView.onboarding.notDetectedError.title'), - description: t('browserView.onboarding.notDetectedError.description'), - confirm: t('browserView.onboarding.notDetectedError.agree') - }, - notDetectedTrezor: { - title: t('browserView.onboarding.notDetectedError.title'), - description: t('browserView.onboarding.notDetectedError.trezorDescription'), - confirm: t('browserView.onboarding.notDetectedError.agree') - } - }; - - const error = ERROR_MESSAGES[errorCode]; - - return ( - - - {error.title} - - {error.description} - - - ); -}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx deleted file mode 100644 index 8d0edd615..000000000 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow.tsx +++ /dev/null @@ -1,225 +0,0 @@ -// / -/* eslint-disable max-statements */ -/* eslint-disable react/no-multi-comp */ -import { useWalletManager, useTimeSpentOnPage, useLocalStorage } from '@hooks'; -import { WalletSetupSelectAccountsStepRevamp, WalletSetupConnectHardwareWalletStepRevamp } from '@lace/core'; -import React, { useState, useCallback, useEffect } from 'react'; -import { Switch, Route, useHistory, useLocation, Redirect } from 'react-router-dom'; -import { Wallet } from '@lace/cardano'; -import { WalletSetupLayout } from '@src/views/browser-view/components/Layout'; -import { ErrorDialog, HWErrorCode } from './ErrorDialog'; -import { StartOverDialog } from '@views/browser/features/wallet-setup/components/StartOverDialog'; -import { useTranslation } from 'react-i18next'; -import { EnhancedAnalyticsOptInStatus, postHogOnboardingActions } from '@providers/AnalyticsProvider/analyticsTracker'; -import { config } from '@src/config'; -import { walletRoutePaths } from '@routes/wallet-paths'; -import { getHWPersonProperties, isTrezorHWSupported } from '../helpers'; -import { useAnalyticsContext } from '@providers'; -import { ENHANCED_ANALYTICS_OPT_IN_STATUS_LS_KEY } from '@providers/AnalyticsProvider/config'; -import { SendOnboardingAnalyticsEvent } from '../types'; -import { WalletType } from '@cardano-sdk/web-extension'; - -const { CHAIN } = config(); -const { AVAILABLE_WALLETS } = Wallet; -export interface HardwareWalletFlowProps { - onCancel: () => void; - onAppReload: () => void; - sendAnalytics: SendOnboardingAnalyticsEvent; -} - -type HardwareWalletStep = 'connect' | 'setup'; - -const TOTAL_ACCOUNTS = 50; - -const route = (path: string) => `${walletRoutePaths.setup.hardware}/${path}`; - -export const HardwareWalletFlow = ({ - onCancel, - onAppReload, - sendAnalytics -}: HardwareWalletFlowProps): React.ReactElement => { - const history = useHistory(); - const location = useLocation(); - const { t } = useTranslation(); - const [isErrorDialogVisible, setIsErrorDialogVisible] = useState(false); - const [hardwareWalletErrorCode, setHardwareWalletErrorCode] = useState('common'); - const [isStartOverDialogVisible, setIsStartOverDialogVisible] = useState(false); - const showStartOverDialog = () => setIsStartOverDialogVisible(true); - const [walletCreated, setWalletCreated] = useState(); - const [deviceConnection, setDeviceConnection] = useState(); - const [connectedDevice, setConnectedDevice] = useState(); - const [accountIndex, setAccountIndex] = useState(0); - const [isSubmitting, setIsSubmitting] = useState(false); - const { createHardwareWallet, connectHardwareWallet, saveHardwareWallet } = useWalletManager(); - const { updateEnteredAtTime } = useTimeSpentOnPage(); - const analytics = useAnalyticsContext(); - - useEffect(() => { - updateEnteredAtTime(); - }, [location.pathname, updateEnteredAtTime]); - - const showHardwareWalletError = (errorCode: HWErrorCode) => { - setHardwareWalletErrorCode(errorCode); - setIsErrorDialogVisible(true); - }; - - const walletSetupConnectHardwareWalletStepTranslations = { - title: t('core.walletSetupConnectHardwareWalletStep.title'), - subTitle: t(`core.walletSetupConnectHardwareWalletStep.${isTrezorHWSupported() ? 'subTitleFull' : 'subTitle'}`), - supportedDevices: t( - `core.walletSetupConnectHardwareWalletStep.${isTrezorHWSupported() ? 'supportedDevicesFull' : 'supportedDevices'}` - ), - connectDevice: t( - `core.walletSetupConnectHardwareWalletStep.${isTrezorHWSupported() ? 'connectDeviceFull' : 'connectDevice'}` - ) - }; - - const navigateTo = useCallback( - (nexthPath: string) => { - history.replace(route(nexthPath)); - }, - [history] - ); - - const [enhancedAnalyticsStatus] = useLocalStorage( - ENHANCED_ANALYTICS_OPT_IN_STATUS_LS_KEY, - EnhancedAnalyticsOptInStatus.OptedOut - ); - - const handleCreateWallet = async (name: string) => { - try { - const cardanoWallet = await createHardwareWallet({ - accountIndex, - deviceConnection, - name, - connectedDevice - }); - setWalletCreated(cardanoWallet); - } catch (error) { - console.error('ERROR creating hardware wallet', { error }); - showHardwareWalletError('common'); - } - }; - - const handleConnect = async (model: Wallet.HardwareWallets) => { - try { - const connection = await connectHardwareWallet(model); - setDeviceConnection(connection); - setConnectedDevice(model); - } catch (error) { - console.error('ERROR connecting hardware wallet', error); - if (error.innerError?.innerError?.message === 'The device is already open.') { - setDeviceConnection(deviceConnection); - } else { - showHardwareWalletError(model === WalletType.Trezor ? 'notDetectedTrezor' : 'notDetectedLedger'); - } - } - }; - - const handleFinishCreation = () => saveHardwareWallet(walletCreated, CHAIN); - - const handleGoToMyWalletClick = async () => { - try { - const posthogProperties = await getHWPersonProperties(connectedDevice, deviceConnection); - await sendAnalytics(postHogOnboardingActions.hw.DONE_GO_TO_WALLET, { - ...posthogProperties, - // eslint-disable-next-line camelcase - $set: { wallet_accounts_quantity: '1' } - }); - } catch { - console.error('We were not able to send the analytics event'); - } finally { - await handleFinishCreation(); - if (enhancedAnalyticsStatus === EnhancedAnalyticsOptInStatus.OptedIn) { - await analytics.sendAliasEvent(); - } - } - }; - - const onHardwareWalletDisconnect = useCallback((event: HIDConnectionEvent) => { - if (event.device.opened) showHardwareWalletError('common'); - }, []); - - useEffect(() => { - navigator.hid.addEventListener('disconnect', onHardwareWalletDisconnect); - return () => { - navigator.hid.removeEventListener('disconnect', onHardwareWalletDisconnect); - }; - }, [onHardwareWalletDisconnect]); - - const handleEnterWallet = async (account: number, name: string) => { - sendAnalytics(postHogOnboardingActions.hw.SETUP_HW_WALLET_NEXT_CLICK); - setAccountIndex(account); - setIsSubmitting(true); - await handleCreateWallet(name); - setIsSubmitting(false); - await handleGoToMyWalletClick(); - }; - - const hardwareWalletStepRenderFunctions: Record JSX.Element> = { - connect: () => ( - { - analytics.sendEventToPostHog(postHogOnboardingActions.hw.CONNECT_HW_NEXT_CLICK); - navigateTo('setup'); - }} - isNextEnable={!!deviceConnection} - translations={walletSetupConnectHardwareWalletStepTranslations} - isHardwareWallet - /> - ), - setup: () => ( - - ) - }; - - const goBackToConnect = () => { - /* eslint-disable unicorn/no-useless-undefined */ - setDeviceConnection(undefined); - setConnectedDevice(undefined); - setAccountIndex(0); - setWalletCreated(undefined); - history.replace(route('connect')); - }; - - const onRetry = () => { - setIsErrorDialogVisible(false); - goBackToConnect(); - // TODO: Remove this workaround with full app reload when SDK allows to connect Hardware Wallet for the 2nd time. - onAppReload(); - }; - - const handleStartOver = () => { - setIsStartOverDialogVisible(false); - goBackToConnect(); - // TODO: Remove this workaround with full app reload when SDK allows to connect Hardware Wallet for the 2nd time. - onAppReload(); - }; - - return ( - <> - - setIsStartOverDialogVisible(false)} - /> - - - {hardwareWalletStepRenderFunctions.connect()} - {hardwareWalletStepRenderFunctions.setup()} - - - - - ); -}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx new file mode 100644 index 000000000..5c8c00e50 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx @@ -0,0 +1,172 @@ +/* eslint-disable unicorn/no-useless-undefined */ +import { useTimeSpentOnPage } from '@hooks'; +import { WalletSetupSelectAccountsStepRevamp } from '@lace/core'; +import React, { useCallback, useEffect, useState } from 'react'; +import { Redirect, Route, Switch, useHistory } from 'react-router-dom'; +import { Wallet } from '@lace/cardano'; +import { WalletSetupLayout } from '@src/views/browser-view/components/Layout'; +import { makeErrorDialog } from './makeErrorDialog'; +import { StartOverDialog } from '@views/browser/features/wallet-setup/components/StartOverDialog'; +import { walletRoutePaths } from '@routes/wallet-paths'; +import { StepConnect } from './StepConnect'; +import { WalletType } from '@cardano-sdk/web-extension'; +import { StepCreate, WalletData } from './StepCreate'; + +export interface HardwareWalletFlowProps { + onCancel: () => void; +} + +const TOTAL_ACCOUNTS = 50; +const route = (path: FlowStep) => `${walletRoutePaths.setup.hardware}/${path}`; + +enum FlowStep { + Connect = 'Connect', + Setup = 'Setup', + Create = 'Create' +} + +enum ErrorDialogCode { + DeviceDisconnected = 'DeviceDisconnected', + PublicKeyExportRejected = 'PublicKeyExportRejected', + Generic = 'Generic' +} + +const commonErrorDialogTranslationKeys = { + title: 'browserView.onboarding.errorDialog.title' as const, + confirm: 'browserView.onboarding.errorDialog.cta' as const +}; +const ErrorDialog = makeErrorDialog({ + [ErrorDialogCode.DeviceDisconnected]: { + ...commonErrorDialogTranslationKeys, + description: 'browserView.onboarding.errorDialog.messageDeviceDisconnected' + }, + [ErrorDialogCode.PublicKeyExportRejected]: { + ...commonErrorDialogTranslationKeys, + description: 'browserView.onboarding.errorDialog.messagePublicKeyExportRejected' + }, + [ErrorDialogCode.Generic]: { + ...commonErrorDialogTranslationKeys, + description: 'browserView.onboarding.errorDialog.messageGeneric' + } +}); + +export const HardwareWalletFlow = ({ onCancel }: HardwareWalletFlowProps): React.ReactElement => { + const history = useHistory(); + const [connectedUsbDevice, setConnectedUsbDevice] = useState(); + const [errorDialogCode, setErrorDialogCode] = useState(); + const [isStartOverDialogVisible, setIsStartOverDialogVisible] = useState(false); + const [connection, setConnection] = useState(); + const [walletData, setWalletData] = useState(); + const { updateEnteredAtTime } = useTimeSpentOnPage(); + + useEffect(() => { + updateEnteredAtTime(); + }, [history.location.pathname, updateEnteredAtTime]); + + useEffect(() => { + const onHardwareWalletDisconnect = (event: USBConnectionEvent) => { + if (event.device !== connectedUsbDevice || !connection) return; + setErrorDialogCode(ErrorDialogCode.DeviceDisconnected); + }; + + navigator.usb.addEventListener('disconnect', onHardwareWalletDisconnect); + return () => { + navigator.usb.removeEventListener('disconnect', onHardwareWalletDisconnect); + }; + }, [connectedUsbDevice, connection]); + + const navigateTo = useCallback( + (nexthPath: FlowStep) => { + history.replace(route(nexthPath)); + }, + [history] + ); + + const onConnected = useCallback( + (result?: Wallet.HardwareWalletConnection) => { + if (result) { + setConnection(result); + } + navigateTo(FlowStep.Setup); + }, + [navigateTo] + ); + + const closeConnection = () => { + if (connection.type === WalletType.Ledger) { + void connection.value.transport.close(); + } + }; + + const onAccountAndNameSubmit = async (accountIndex: number, name: string) => { + setWalletData({ + accountIndex, + name + }); + navigateTo(FlowStep.Create); + }; + + const cleanupConnectionState = () => { + setConnection(undefined); + navigateTo(FlowStep.Connect); + closeConnection(); + }; + + const onRetry = () => { + setErrorDialogCode(undefined); + cleanupConnectionState(); + }; + + const handleStartOver = () => { + setIsStartOverDialogVisible(false); + cleanupConnectionState(); + }; + + const onWalletCreateError = (error: Error) => { + let errorCode: ErrorDialogCode = ErrorDialogCode.Generic; + + const ledgerPkRejection = + error.message.includes('Failed to export extended account public key') && + error.message.includes('Action rejected by user'); + const trezorPkRejection = error.message.includes('Trezor transport failed'); + if (ledgerPkRejection || trezorPkRejection) { + errorCode = ErrorDialogCode.PublicKeyExportRejected; + } + + setErrorDialogCode(errorCode); + closeConnection(); + }; + + return ( + <> + {!!errorDialogCode && } + setIsStartOverDialogVisible(false)} + /> + + + + + + {!!connection && ( + <> + + setIsStartOverDialogVisible(true)} + onSubmit={onAccountAndNameSubmit} + /> + + + + + + )} + + + + + ); +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx new file mode 100644 index 000000000..e77290b23 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx @@ -0,0 +1,138 @@ +/* eslint-disable unicorn/no-null */ +import { UseWalletManager, useWalletManager } from '@hooks'; +import { Wallet } from '@lace/cardano'; +import { WalletSetupConnectHardwareWalletStepRevamp } from '@lace/core'; +import { TranslationKey } from '@lib/translations/types'; +import { TFunction } from 'i18next'; +import React, { useCallback, useEffect, useState, VFC } from 'react'; +import { useTranslation } from 'react-i18next'; + +export const isTrezorHWSupported = (): boolean => process.env.USE_TREZOR_HW === 'true'; + +const requestHardwareWalletConnection = (): Promise => + navigator.usb.requestDevice({ + filters: isTrezorHWSupported() ? Wallet.supportedHwUsbDescriptors : Wallet.ledgerDescriptors + }); + +const threeSecondsTimeout = 3000; +const timeoutErrorMessage = 'Timeout. Connecting too long.'; + +const isTimeoutError = (error: Error): boolean => error.message === timeoutErrorMessage; + +const useConnectHardwareWalletWithTimeout = (connect: UseWalletManager['connectHardwareWalletRevamped']) => + useCallback( + async (usbDevice: USBDevice) => { + const result = await Promise.race([ + connect(usbDevice), + new Promise<'timeout'>((resolve) => setTimeout(() => resolve('timeout'), threeSecondsTimeout)) + ]); + + if (result === 'timeout') { + throw new Error(timeoutErrorMessage); + } + + return result; + }, + [connect] + ); + +type ConnectionError = + | 'userGestureRequired' + | 'devicePickerRejected' + | 'deviceLocked' + | 'deviceBusy' + | 'cardanoAppNotOpen' + | 'generic'; + +const connectionSubtitleErrorTranslationsMap: Record = { + cardanoAppNotOpen: 'core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.cardanoAppNotOpen', + deviceLocked: 'core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.deviceLocked', + deviceBusy: 'core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.deviceBusy', + devicePickerRejected: 'core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.devicePickerRejected', + userGestureRequired: 'core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.userGestureRequired', + generic: 'core.walletSetupConnectHardwareWalletStepRevamp.errorMessage.generic' +}; + +const makeTranslations = ({ connectionError, t }: { connectionError: ConnectionError; t: TFunction }) => ({ + title: t('core.walletSetupConnectHardwareWalletStepRevamp.title'), + subTitle: isTrezorHWSupported() + ? t('core.walletSetupConnectHardwareWalletStepRevamp.subTitle') + : t('core.walletSetupConnectHardwareWalletStepRevamp.subTitleLedgerOnly'), + errorMessage: connectionError ? t(connectionSubtitleErrorTranslationsMap[connectionError]) : '', + errorCta: t('core.walletSetupConnectHardwareWalletStepRevamp.errorCta') +}); + +const parseConnectionError = (error: Error): ConnectionError | null => { + if (error instanceof DOMException) { + if (error.message.includes('user gesture')) return 'userGestureRequired'; + if (error.message.includes('No device selected')) return 'devicePickerRejected'; + } + if (isTimeoutError(error)) return 'deviceBusy'; + if (error.message.includes('Cannot communicate with Ledger Cardano App')) { + if (error.message.includes('General error 0x5515')) return 'deviceLocked'; + if (error.message.includes('General error 0x6e01')) return 'cardanoAppNotOpen'; + } + return 'generic'; +}; + +enum DiscoveryState { + Idle = 'Idle', + Requested = 'Requested', + Running = 'Running' +} + +type StepConnectProps = { + onBack: () => void; + onConnected: (result?: Wallet.HardwareWalletConnection) => void; + onUsbDeviceChange: (usbDevice: USBDevice) => void; +}; + +export const StepConnect: VFC = ({ onBack, onConnected, onUsbDeviceChange }) => { + const { t } = useTranslation(); + const [discoveryState, setDiscoveryState] = useState(DiscoveryState.Requested); + const [connectionError, setConnectionError] = useState(null); + const { connectHardwareWalletRevamped } = useWalletManager(); + + const translations = makeTranslations({ connectionError, t }); + const connect = useConnectHardwareWalletWithTimeout(connectHardwareWalletRevamped); + + const onRetry = useCallback(() => { + setDiscoveryState(DiscoveryState.Requested); + setConnectionError(null); + }, []); + + useEffect(() => { + (async () => { + if (discoveryState !== DiscoveryState.Requested) return; + + setDiscoveryState(DiscoveryState.Running); + let connectionResult: Wallet.HardwareWalletConnection; + try { + const usbDevice = await requestHardwareWalletConnection(); + onUsbDeviceChange(usbDevice); + connectionResult = await connect(usbDevice); + onConnected(connectionResult); + setDiscoveryState(DiscoveryState.Idle); + } catch (error) { + setDiscoveryState(DiscoveryState.Idle); + console.error('ERROR connecting hardware wallet', error); + + if (error.innerError?.innerError?.message === 'The device is already open.') { + onConnected(); + return; + } + + setConnectionError(parseConnectionError(error)); + } + })(); + }, [connect, discoveryState, onUsbDeviceChange, onConnected]); + + return ( + + ); +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepCreate.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepCreate.tsx new file mode 100644 index 000000000..ca209fed9 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepCreate.tsx @@ -0,0 +1,95 @@ +import { Wallet } from '@lace/cardano'; +import { WalletSetupHWCreationStep } from '@lace/core'; +import { EnhancedAnalyticsOptInStatus, postHogOnboardingActions } from '@providers/AnalyticsProvider/analyticsTracker'; +import { TFunction } from 'i18next'; +import React, { VFC, useMemo, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useLocalStorage, useWalletManager } from '@hooks'; +import { config } from '@src/config'; +import { useAnalyticsContext } from '@providers'; +import { ENHANCED_ANALYTICS_OPT_IN_STATUS_LS_KEY } from '@providers/AnalyticsProvider/config'; + +const { CHAIN } = config(); + +const makeWalletSetupCreateStepTranslations = (t: TFunction) => ({ + title: t('core.walletSetupCreateStep.title'), + description: t('core.walletSetupCreateStep.description') +}); + +enum CreationState { + Idle = 'Idle', + Working = 'Working' +} + +export type WalletData = { + accountIndex: number; + name: string; +}; + +type StepCreateProps = { + connection: Wallet.HardwareWalletConnection; + onError: (error: Error) => void; + walletData: WalletData; +}; + +export const StepCreate: VFC = ({ connection, onError, walletData }) => { + const { t } = useTranslation(); + const [status, setStatus] = useState(CreationState.Idle); + const { createHardwareWalletRevamped, saveHardwareWallet } = useWalletManager(); + const analytics = useAnalyticsContext(); + const [enhancedAnalyticsStatus] = useLocalStorage( + ENHANCED_ANALYTICS_OPT_IN_STATUS_LS_KEY, + EnhancedAnalyticsOptInStatus.OptedOut + ); + + const walletSetupCreateStepTranslations = useMemo(() => makeWalletSetupCreateStepTranslations(t), [t]); + + useEffect(() => { + (async () => { + if (status !== CreationState.Idle) return; + setStatus(CreationState.Working); + + void analytics.sendEventToPostHog(postHogOnboardingActions.hw.SETUP_HW_WALLET_NEXT_CLICK); + + let cardanoWallet: Wallet.CardanoWallet; + try { + cardanoWallet = await createHardwareWalletRevamped({ + connection, + ...walletData + }); + } catch (error) { + console.error('ERROR creating hardware wallet', { error }); + onError(error); + throw error; + } + + const deviceSpec = await Wallet.getDeviceSpec(connection); + await analytics.sendEventToPostHog(postHogOnboardingActions.hw.DONE_GO_TO_WALLET, { + /* eslint-disable camelcase */ + $set_once: { + initial_hardware_wallet_model: deviceSpec.model, + initial_firmware_version: deviceSpec?.firmwareVersion, + initial_cardano_app_version: deviceSpec?.cardanoAppVersion + }, + $set: { wallet_accounts_quantity: '1' } + /* eslint-enable camelcase */ + }); + + await saveHardwareWallet(cardanoWallet, CHAIN); + if (enhancedAnalyticsStatus === EnhancedAnalyticsOptInStatus.OptedIn) { + await analytics.sendAliasEvent(); + } + })(); + }, [ + analytics, + connection, + createHardwareWalletRevamped, + enhancedAnalyticsStatus, + onError, + saveHardwareWallet, + status, + walletData + ]); + + return ; +}; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/index.ts b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/index.ts new file mode 100644 index 000000000..c7bf754c0 --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/index.ts @@ -0,0 +1,2 @@ +export { HardwareWalletFlow } from './HardwareWalletFlow'; +export { makeErrorDialog } from './makeErrorDialog'; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.module.scss b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.module.scss similarity index 84% rename from apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.module.scss rename to apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.module.scss index 5e7985546..79059e6cb 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/ErrorDialog.module.scss +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.module.scss @@ -1,5 +1,5 @@ -@import '../../../../../../../../packages/common/src/ui/styles/theme.scss'; -@import '../../../../../../../../packages/common/src/ui/styles/abstracts/typography'; +@import '../../../../../../../../../packages/common/src/ui/styles/theme'; +@import '../../../../../../../../../packages/common/src/ui/styles/abstracts/typography'; .errorDialog { :global(.ant-modal-content) { diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.tsx new file mode 100644 index 000000000..b22f2ca2a --- /dev/null +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/makeErrorDialog.tsx @@ -0,0 +1,43 @@ +import { TranslationKey } from '@lib/translations/types'; +import React from 'react'; +import { Modal, Typography } from 'antd'; +import { Button } from '@lace/common'; +import styles from './makeErrorDialog.module.scss'; +import { useTranslation } from 'react-i18next'; + +const { Title, Text } = Typography; + +type TranslationKeys = Record<'title' | 'description' | 'confirm', TranslationKey>; + +interface ErrorDialogProps { + visible: boolean; + onRetry: () => void; + errorCode?: ErrorCode; +} + +export const makeErrorDialog = + (translationsMap: Record) => + ({ visible, onRetry, errorCode }: ErrorDialogProps): React.ReactElement => { + const { t } = useTranslation(); + const errorTranslationKeys = translationsMap[errorCode]; + + return ( + + + {t(errorTranslationKeys.title)} + + {t(errorTranslationKeys.description)} + + + ); + }; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetup.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetup.tsx index 9e7b7635b..4c6bff0ef 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetup.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetup.tsx @@ -107,11 +107,7 @@ export const WalletSetup = ({ initialStep = WalletSetupSteps.Mnemonic }: WalletS /> - location.reload()} - sendAnalytics={sendAnalyticsHandler} - /> + )} diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupMainPage.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupMainPage.tsx index 52ac42238..085fb99d5 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupMainPage.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/WalletSetupMainPage.tsx @@ -22,9 +22,8 @@ import { useHistory } from 'react-router-dom'; export const WalletSetupMainPage = (): ReactElement => { const history = useHistory(); - const [isDappConnectorWarningOpen, setIsDappConnectorWarningOpen] = useState(false); const [isAnalyticsModalOpen, setIsAnalyticsModalOpen] = useState(false); - const { t: translate, Trans } = useTranslate(); + const { t: translate } = useTranslate(); const analytics = useAnalyticsContext(); const [enhancedAnalyticsStatus, { updateLocalStorage: setDoesUserAllowAnalytics }] = useLocalStorage( @@ -53,7 +52,7 @@ export const WalletSetupMainPage = (): ReactElement => { }; const handleStartHardwareOnboarding = () => { - setIsDappConnectorWarningOpen(true); + history.push(walletRoutePaths.setup.hardware); analytics.sendEventToPostHog(postHogOnboardingActions.hw?.SETUP_OPTION_CLICK); }; @@ -100,23 +99,6 @@ export const WalletSetupMainPage = (): ReactElement => { onRestoreWalletRequest={handleRestoreWallet} translations={walletSetupOptionsStepTranslations} /> - -

- -

-
- } - visible={isDappConnectorWarningOpen} - confirmLabel={translate('browserView.walletSetup.confirmExperimentalHwDapp.confirm')} - onCancel={() => setIsDappConnectorWarningOpen(false)} - onConfirm={() => { - setIsDappConnectorWarningOpen(false); - history.push(walletRoutePaths.setup.hardware); - }} - /> process.env.USE_TREZOR_HW === 'true'; -export const isHardwareWalletAvailable = (wallet: Wallet.HardwareWallets): boolean => - wallet !== WalletType.Trezor || isTrezorHWSupported(); -type HardwareWalletPersonProperties = { - model: string; - firmwareVersion?: string; - cardanoAppVersion?: string; -}; - -export const getTrezorSpecifications = async (): Promise => { - const { model, major_version, minor_version, patch_version } = - await HardwareTrezor.TrezorKeyAgent.checkDeviceConnection(Wallet.KeyManagement.CommunicationType.Web); - return { - model: `${WalletType.Trezor} model ${model}`, - firmwareVersion: `${major_version}.${minor_version}.${patch_version}` - }; -}; - -export const getLedgerSpecifications = async ( - deviceConnection: HardwareLedger.LedgerKeyAgent['deviceConnection'] -): Promise => { - const cardanoApp = await deviceConnection.getVersion(); - return { - model: deviceConnection.transport.deviceModel.id, - cardanoAppVersion: `${cardanoApp.version.major}.${cardanoApp.version.minor}.${cardanoApp.version.patch}` - }; -}; - -export const getHWPersonProperties = async ( - connectedDevice: Wallet.HardwareWallets, - deviceConnection: Wallet.DeviceConnection -): Promise => { - // TODO: Remove these hardcoded specs once we have a logic that will prevent additional interaction with 3rd party Trezor Connect popup - const trezorSpecificationsHC: HardwareWalletPersonProperties = { - // We are only accepting Model T for now - model: 'Trezor model T' - }; - const HWSpecifications = - connectedDevice === WalletType.Trezor - ? trezorSpecificationsHC - : await getLedgerSpecifications(deviceConnection as HardwareLedger.LedgerKeyAgent['deviceConnection']); - return { - $set_once: { - initial_hardware_wallet_model: HWSpecifications.model, - initial_firmware_version: HWSpecifications?.firmwareVersion, - initial_cardano_app_version: HWSpecifications?.cardanoAppVersion - } - }; -}; diff --git a/packages/cardano/package.json b/packages/cardano/package.json index 484414c1b..eb10574bb 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -41,14 +41,16 @@ "@cardano-sdk/cardano-services-client": "0.18.0", "@cardano-sdk/core": "0.30.0", "@cardano-sdk/crypto": "0.1.22", - "@cardano-sdk/hardware-ledger": "0.8.19", + "@cardano-sdk/hardware-ledger": "0.9.0", "@cardano-sdk/hardware-trezor": "0.4.19", "@cardano-sdk/key-management": "0.20.1", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.2", - "@cardano-sdk/web-extension": "0.26.1", + "@cardano-sdk/wallet": "0.36.0", + "@cardano-sdk/web-extension": "0.26.2", "@lace/common": "0.1.0", + "@ledgerhq/devices": "^8.2.1", "@stablelib/chacha20poly1305": "1.0.1", + "@trezor/transport": "^1.1.18", "bignumber.js": "9.0.1", "buffer": "6.0.3", "classnames": "2.3.1", diff --git a/packages/cardano/src/wallet/lib/hardware-wallet.ts b/packages/cardano/src/wallet/lib/hardware-wallet.ts index ded662d2c..883d35fab 100644 --- a/packages/cardano/src/wallet/lib/hardware-wallet.ts +++ b/packages/cardano/src/wallet/lib/hardware-wallet.ts @@ -1,10 +1,12 @@ /* eslint-disable unicorn/no-null */ +import { Bip32PublicKeyHex } from '@cardano-sdk/crypto'; import * as KeyManagement from '@cardano-sdk/key-management'; -import { DeviceConnection, HardwareWallets } from '../types'; +import { HardwareWalletConnection, DeviceConnection, HardwareWallets, LedgerConnection } from '../types'; import * as HardwareLedger from '@cardano-sdk/hardware-ledger'; import * as HardwareTrezor from '@cardano-sdk/hardware-trezor'; import { WalletType } from '@cardano-sdk/web-extension'; -// Using nodejs CML version to satisfy the tests requirements, but this gets replaced by webpack to the browser version in the build +import { ledgerUSBVendorId } from '@ledgerhq/devices'; +import { TREZOR_USB_DESCRIPTORS } from '@trezor/transport'; const isTrezorHWSupported = (): boolean => process.env.USE_TREZOR_HW === 'true'; @@ -20,16 +22,150 @@ export const manifest: KeyManagement.TrezorConfig['manifest'] = { email: process.env.EMAIL_ADDRESS }; +const initializeTrezor = () => + HardwareTrezor.TrezorKeyAgent.initializeTrezorTransport({ + manifest, + communicationType: DEFAULT_COMMUNICATION_TYPE + }); + const connectDevices: Record Promise> = { [WalletType.Ledger]: async () => await HardwareLedger.LedgerKeyAgent.checkDeviceConnection(DEFAULT_COMMUNICATION_TYPE), ...(AVAILABLE_WALLETS.includes(WalletType.Trezor) && { - [WalletType.Trezor]: async () => - await HardwareTrezor.TrezorKeyAgent.initializeTrezorTransport({ - manifest, - communicationType: DEFAULT_COMMUNICATION_TYPE - }) + [WalletType.Trezor]: async () => await initializeTrezor() }) }; export const connectDevice = async (model: HardwareWallets): Promise => await connectDevices[model](); + +type Descriptor = Partial; +type DescriptorEntries = [keyof T, T[keyof T]][]; +const isDeviceDescribedBy = (device: USBDevice, descriptors: Descriptor[]) => + descriptors.some((descriptor) => + (Object.entries(descriptor) as DescriptorEntries).every(([key, value]) => device[key] === value) + ); + +const ledgerNanoSWithNoAppOpenProductId = 4113; +const ledgerNanoSWithCardanoAppOpenProductId = 4117; +const ledgerNanoSPlusWithNoAppOpenProductId = 20_497; +const ledgerNanoSPlusWithCardanoAppOpenProductId = 20_501; +const ledgerNanoXWithNoAppOpenProductId = 16_401; +const ledgerNanoXWithCardanoAppOpenProductId = 16_405; +export const ledgerDescriptors = [ + ledgerNanoSWithNoAppOpenProductId, + ledgerNanoSWithCardanoAppOpenProductId, + ledgerNanoSPlusWithNoAppOpenProductId, + ledgerNanoSPlusWithCardanoAppOpenProductId, + ledgerNanoXWithNoAppOpenProductId, + ledgerNanoXWithCardanoAppOpenProductId +].map((productId) => ({ + vendorId: ledgerUSBVendorId, + productId +})); + +// eslint-disable-next-line unicorn/number-literal-case +const trezorModelTProductId = 0x53_c1; +const trezorDescriptors = TREZOR_USB_DESCRIPTORS.filter(({ productId }) => productId === trezorModelTProductId); +export const supportedHwUsbDescriptors = [...ledgerDescriptors, ...trezorDescriptors]; + +export const connectDeviceRevamped = async (usbDevice: USBDevice): Promise => { + if (isDeviceDescribedBy(usbDevice, ledgerDescriptors)) { + return { + type: WalletType.Ledger, + value: await HardwareLedger.LedgerKeyAgent.establishDeviceConnection(DEFAULT_COMMUNICATION_TYPE, usbDevice) + }; + } + if (isTrezorHWSupported() && isDeviceDescribedBy(usbDevice, trezorDescriptors)) { + await initializeTrezor(); + return { + type: WalletType.Trezor + }; + } + + throw new Error('Could not recognize the device'); +}; + +const invalidDeviceError = new Error('Invalid device type'); + +export const getHwExtendedAccountPublicKey = async ( + walletType: HardwareWallets, + accountIndex: number, + ledgerConnection?: LedgerConnection +): Promise => { + if (walletType === WalletType.Ledger) { + return HardwareLedger.LedgerKeyAgent.getXpub({ + communicationType: DEFAULT_COMMUNICATION_TYPE, + deviceConnection: ledgerConnection, + accountIndex + }); + } + if (isTrezorHWSupported() && walletType === WalletType.Trezor) { + return HardwareTrezor.TrezorKeyAgent.getXpub({ + communicationType: DEFAULT_COMMUNICATION_TYPE, + accountIndex + }); + } + throw invalidDeviceError; +}; + +type DeviceSpec = { + model: string; + firmwareVersion?: string; + cardanoAppVersion?: string; +}; + +const makeVersion = (major: number, minor: number, patch: number) => `${major}.${minor}.${patch}`; + +export const getDeviceSpec = async (connection: HardwareWalletConnection): Promise => { + if (connection.type === WalletType.Ledger) { + const { version } = await connection.value.getVersion(); + return { + model: connection.value.transport.deviceModel.id, + cardanoAppVersion: makeVersion(version.major, version.minor, version.patch) + }; + } + if (isTrezorHWSupported() && connection.type === WalletType.Trezor) { + // TODO: Remove these hardcoded specs once we have a logic that will prevent additional interaction with 3rd party Trezor Connect popup + const hardcodeTrezorSpec = true; + if (hardcodeTrezorSpec) { + return { + model: 'Trezor model T' + }; + } + + const features = await HardwareTrezor.TrezorKeyAgent.checkDeviceConnection(DEFAULT_COMMUNICATION_TYPE); + return { + model: `${WalletType.Trezor} model ${features.model}`, + firmwareVersion: makeVersion(features.major_version, features.minor_version, features.patch_version) + }; + } + + throw invalidDeviceError; +}; + +type SoftwareVersion = { + major: number; + minor: number; + patch: number; +}; + +export const initConnectionAndGetSoftwareVersion = async (type: HardwareWallets): Promise => { + if (type === WalletType.Ledger) { + const connection = await HardwareLedger.LedgerKeyAgent.establishDeviceConnection(DEFAULT_COMMUNICATION_TYPE); + const { version } = await connection.getVersion(); + return version; + } + if (isTrezorHWSupported() && type === WalletType.Trezor) { + // To allow checks once the app is refreshed. It won't affect the user flow + // TODO: Smarter Trezor initialization logic after onboarding revamp LW-9808 + await initializeTrezor(); + const features = await HardwareTrezor.TrezorKeyAgent.checkDeviceConnection(DEFAULT_COMMUNICATION_TYPE); + return { + major: features.major_version, + minor: features.minor_version, + patch: features.patch_version + }; + } + + throw invalidDeviceError; +}; diff --git a/packages/cardano/src/wallet/types.ts b/packages/cardano/src/wallet/types.ts index 21e2dd835..2942522c5 100644 --- a/packages/cardano/src/wallet/types.ts +++ b/packages/cardano/src/wallet/types.ts @@ -2,10 +2,21 @@ import { Cardano, Paginated } from '@cardano-sdk/core'; import type { LedgerKeyAgent } from '@cardano-sdk/hardware-ledger'; import { WalletType } from '@cardano-sdk/web-extension'; -export type DeviceConnection = LedgerKeyAgent['deviceConnection'] | boolean; +export type LedgerConnection = LedgerKeyAgent['deviceConnection']; + +export type DeviceConnection = LedgerConnection | boolean; export type HardwareWallets = WalletType.Trezor | WalletType.Ledger; +export type HardwareWalletConnection = + | { + type: WalletType.Trezor; + } + | { + type: WalletType.Ledger; + value: LedgerConnection; + }; + export type StakePoolSearchResults = Paginated; export type DappInfo = { @@ -69,10 +80,3 @@ export enum WalletManagerProviderTypes { } export type ChainName = keyof typeof Cardano.ChainIds; - -export interface CreateHardwareWalletArgs { - deviceConnection: DeviceConnection; - name: string; - accountIndex: number; - activeChainId: Cardano.ChainId; -} diff --git a/packages/core/src/ui/components/WalletSetup/WalletSetupCreationStep.tsx b/packages/core/src/ui/components/WalletSetup/WalletSetupCreationStep.tsx deleted file mode 100644 index 922fca549..000000000 --- a/packages/core/src/ui/components/WalletSetup/WalletSetupCreationStep.tsx +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable no-magic-numbers */ -import React from 'react'; -import cn from 'classnames'; -import { WalletSetupStepLayout, WalletTimelineSteps } from './WalletSetupStepLayout'; -import { TranslationsFor } from '@ui/utils/types'; -import styles from './WalletSetupFinalStep.module.scss'; -import { Loader } from '@lace/common'; - -type TranslationKeys = 'title' | 'description'; -export interface WalletSetupCreationStepProps { - translations: TranslationsFor; - isHardwareWallet?: boolean; -} -export const WalletSetupCreationStep = ({ - translations, - isHardwareWallet = false -}: WalletSetupCreationStepProps): React.ReactElement => ( - -
- -
-
-); diff --git a/packages/core/src/ui/components/WalletSetup/index.ts b/packages/core/src/ui/components/WalletSetup/index.ts index a67bcfde6..0977ff451 100644 --- a/packages/core/src/ui/components/WalletSetup/index.ts +++ b/packages/core/src/ui/components/WalletSetup/index.ts @@ -3,7 +3,6 @@ export * from './WalletSetupStepLayout'; export * from './WalletSetupRegisterStep'; export * from './WalletSetupMnemonicIntroStep'; export * from './WalletSetupMnemonicStep'; -export * from './WalletSetupCreationStep'; export * from './WalletSetupModeStep'; export * from './WalletSetupFinalStep'; export * from './WalletSetupMnemonicVerificationStep'; diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.module.scss b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.module.scss new file mode 100644 index 000000000..120e3e063 --- /dev/null +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.module.scss @@ -0,0 +1,20 @@ +@import '../../styles/theme.scss'; +@import '../../../../../common/src/ui/styles/abstracts/typography'; + +.wrapper { + align-items: center; + display: flex; + flex-direction: column; + gap: size_unit(1); + height: 100%; + justify-content: center; +} + +.loader { + transform: translate(50%); +} + +.errorImage { + flex-grow: 1; + width: size_unit(16); +} diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx index d24207145..1e645e54f 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupConnectHardwareWalletStepRevamp.tsx @@ -1,94 +1,45 @@ -import React, { useState } from 'react'; -import { WalletTimelineSteps } from '../WalletSetup/WalletSetupStepLayout'; -import styles from '../WalletSetup/WalletSetupConnectHardwareWalletStep.module.scss'; -import Icon from '@ant-design/icons'; -import { ReactComponent as LedgerLogo } from '../../assets/icons/ledger-wallet.component.svg'; -import { ReactComponent as TrezorLogo } from '../../assets/icons/trezor-wallet.component.svg'; -import { Typography } from 'antd'; +import { Banner, Loader } from '@lace/common'; import { TranslationsFor } from '@ui/utils/types'; -import classnames from 'classnames'; +import React from 'react'; +import ExclamationCircleIcon from '../../assets/icons/exclamation-circle.svg'; +import { WalletTimelineSteps } from '../WalletSetup'; +import styles from './WalletSetupConnectHardwareWalletStepRevamp.module.scss'; import { WalletSetupStepLayoutRevamp } from './WalletSetupStepLayoutRevamp'; -const { Text } = Typography; - -const logoMap = { - Ledger: LedgerLogo, - Trezor: TrezorLogo -}; - -export interface WalletSetupConnectHardwareWalletStepRevampProps { - wallets: string[]; +export interface WalletSetupConnectHardwareWalletStepProps { onBack: () => void; - onNext: () => void; - onConnect: (model: string) => Promise; - isNextEnable: boolean; - translations: TranslationsFor<'title' | 'subTitle' | 'supportedDevices' | 'connectDevice'>; - isHardwareWallet?: boolean; + translations: TranslationsFor<'title' | 'subTitle' | 'errorMessage' | 'errorCta'>; + state: 'loading' | 'error'; + onRetry?: () => void; } export const WalletSetupConnectHardwareWalletStepRevamp = ({ - wallets, onBack, - onNext, - onConnect, - isNextEnable, translations, - isHardwareWallet = false -}: WalletSetupConnectHardwareWalletStepRevampProps): React.ReactElement => { - const [walletModel, setWalletModel] = useState(); - const [isConnecting, setIsConnecting] = useState(false); - const isButtonActive = (model: string) => model === walletModel; - - const handleConnect = async (model: string) => { - setIsConnecting(true); - setWalletModel(model); - try { - await onConnect(model); - } finally { - setIsConnecting(false); - } - }; - - return ( - - - {translations.subTitle} - -
-
- {wallets.map((wallet: string) => ( - - ))} + state, + onRetry +}: WalletSetupConnectHardwareWalletStepProps): React.ReactElement => ( + +
+ {state === 'loading' && ( +
+
- - {translations.supportedDevices} - -
- - {translations.connectDevice} - -
- ); -}; + )} + {state === 'error' && ( + <> + hardware wallet connection error image + + + )} +
+ +); diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.module.scss b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.module.scss new file mode 100644 index 000000000..5376ed944 --- /dev/null +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.module.scss @@ -0,0 +1,11 @@ +.walletCreationStep { + align-items: center; + display: flex; + flex-direction: column; + height: 100%; + justify-content: center; +} + +.loader { + transform: translate(50%); +} diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.tsx new file mode 100644 index 000000000..f8f387e54 --- /dev/null +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupHWCreationStep.tsx @@ -0,0 +1,25 @@ +import { Loader } from '@lace/common'; +import { TranslationsFor } from '@ui/utils/types'; +import React from 'react'; +import { WalletTimelineSteps } from '../WalletSetup'; +import styles from './WalletSetupHWCreationStep.module.scss'; +import { WalletSetupStepLayoutRevamp } from './WalletSetupStepLayoutRevamp'; + +type WalletSetupCreationStepProps = { + translations: TranslationsFor<'title' | 'description'>; +}; + +export const WalletSetupHWCreationStep = ({ translations }: WalletSetupCreationStepProps): React.ReactElement => ( + +
+
+ +
+
+
+); diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx index 4bc0e486b..3e6c142bd 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupNamePasswordStepRevamp.tsx @@ -11,7 +11,7 @@ import { } from '../WalletSetup/WalletSetupNamePasswordStep/utils'; import { WalletNameInput } from '../WalletSetup/WalletSetupNamePasswordStep/WalletNameInput'; import { WalletPasswordConfirmationInput } from '../WalletSetup/WalletSetupNamePasswordStep/WalletPasswordConfirmationInput'; -import { WalletSetupStepLayoutRevamp } from '../WalletSetupRevamp/WalletSetupStepLayoutRevamp'; +import { WalletSetupStepLayoutRevamp } from './WalletSetupStepLayoutRevamp'; import { TranslationsFor } from '@ui/utils/types'; import { useTranslate } from '@ui/hooks'; import { passwordComplexity } from '@ui/utils/password-complexity'; diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx index 69a31ace8..157ac9d2a 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx @@ -15,8 +15,6 @@ export interface WalletSetupSelectAccountsStepRevampProps { accounts: number; onBack: () => void; onSubmit: (accountIndex: number, name: string) => void; - isHardwareWallet?: boolean; - wallet?: string; isNextLoading?: boolean; } diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx index 333732cc2..0d295f4e5 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupStepLayoutRevamp.tsx @@ -3,10 +3,10 @@ import styles from './WalletSetupStepLayoutRevamp.module.scss'; import cn from 'classnames'; import { Button, Timeline } from '@lace/common'; import { Tooltip } from 'antd'; -import { urls } from '../../utils/constants'; +import { urls } from '@ui/utils/constants'; import { useTranslate } from '@ui/hooks'; import i18n from '@ui/lib/i18n'; -import { WalletSetupFlow, useWalletSetupFlow, WalletTimelineSteps } from '../WalletSetup'; +import { WalletTimelineSteps } from '../WalletSetup'; export interface WalletSetupStepLayoutRevampProps { title: React.ReactNode; @@ -27,16 +27,7 @@ export interface WalletSetupStepLayoutRevampProps { isHardwareWallet?: boolean; } -const removeLegalAndAnalyticsStep = ( - steps: { - key: WalletTimelineSteps; - name: string; - }[] -) => { - steps.shift(); -}; - -const getTimelineSteps = (currentStep: WalletTimelineSteps, isHardwareWallet: boolean, flow: WalletSetupFlow) => { +const getTimelineSteps = (currentStep: WalletTimelineSteps, isHardwareWallet: boolean) => { const inMemoryWalletSteps = [ { key: WalletTimelineSteps.RECOVERY_PHRASE, name: i18n.t('core.walletSetupStep.recoveryPhrase') }, { key: WalletTimelineSteps.WALLET_SETUP, name: i18n.t('core.walletSetupStep.walletSetup') }, @@ -51,11 +42,6 @@ const getTimelineSteps = (currentStep: WalletTimelineSteps, isHardwareWallet: bo const walletSteps = isHardwareWallet ? hardwareWalletSteps : inMemoryWalletSteps; - if (flow === WalletSetupFlow.ADD_WALLET) { - // remove legal and analytics step - removeLegalAndAnalyticsStep(walletSteps); - } - if (typeof currentStep !== 'undefined') { const currentStepIndex = walletSteps.findIndex((step) => step.key === currentStep); return walletSteps.map((step, index) => ({ ...step, active: index <= currentStepIndex })); @@ -83,7 +69,6 @@ export const WalletSetupStepLayoutRevamp = ({ }: WalletSetupStepLayoutRevampProps): React.ReactElement => { const { t } = useTranslate(); const nextButtonContainerRef = useRef(null); - const flow = useWalletSetupFlow(); const defaultLabel = { next: t('core.walletSetupStep.next'), @@ -91,7 +76,7 @@ export const WalletSetupStepLayoutRevamp = ({ skip: t('core.walletSetupStep.skip') }; - const timelineSteps = getTimelineSteps(currentTimelineStep, isHardwareWallet, flow); + const timelineSteps = getTimelineSteps(currentTimelineStep, isHardwareWallet); return (
diff --git a/packages/core/src/ui/components/WalletSetupRevamp/index.ts b/packages/core/src/ui/components/WalletSetupRevamp/index.ts index 3892e3136..4587a7601 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/index.ts +++ b/packages/core/src/ui/components/WalletSetupRevamp/index.ts @@ -1,8 +1,9 @@ -export * from './WalletSetupSelectAccountsStepRevamp'; -export * from './WalletSetupOptionsStepRevamp'; +export { WalletSetupSelectAccountsStepRevamp } from './WalletSetupSelectAccountsStepRevamp'; +export { WalletSetupOptionsStepRevamp } from './WalletSetupOptionsStepRevamp'; export { MnemonicVideoPopupContent } from './MnemonicVideoPopupContent'; export { WalletSetupMnemonicStepRevamp } from './WalletSetupMnemonicStepRevamp'; export { WalletSetupMnemonicVerificationStepRevamp } from './WalletSetupMnemonicStepRevamp'; export { WalletSetupStepLayoutRevamp } from './WalletSetupStepLayoutRevamp'; export { WalletSetupNamePasswordStepRevamp } from './WalletSetupNamePasswordStepRevamp'; export { WalletSetupConnectHardwareWalletStepRevamp } from './WalletSetupConnectHardwareWalletStepRevamp'; +export { WalletSetupHWCreationStep } from './WalletSetupHWCreationStep'; diff --git a/packages/staking/package.json b/packages/staking/package.json index 334a12ce9..becb11234 100644 --- a/packages/staking/package.json +++ b/packages/staking/package.json @@ -75,6 +75,8 @@ "@cardano-sdk/input-selection": "0.12.26", "@cardano-sdk/tx-construction": "0.18.2", "@cardano-sdk/util": "0.15.0", + "@cardano-sdk/wallet": "0.36.0", + "@cardano-sdk/web-extension": "0.26.2", "@storybook/addon-actions": "^7.6.7", "@storybook/addon-essentials": "^7.6.7", "@storybook/addon-interactions": "^7.6.7", @@ -123,8 +125,8 @@ "@cardano-sdk/input-selection": "0.12.26", "@cardano-sdk/tx-construction": "0.18.2", "@cardano-sdk/util": "0.15.0", - "@cardano-sdk/wallet": "0.35.2", - "@cardano-sdk/web-extension": "0.26.1", + "@cardano-sdk/wallet": "0.36.0", + "@cardano-sdk/web-extension": "0.26.2", "@lace/cardano": "^0.1.0", "@lace/common": "^0.1.0", "@lace/core": "0.1.0", diff --git a/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx b/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx index 62ae3df8b..90ac80e91 100644 --- a/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx +++ b/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx @@ -37,14 +37,14 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation const [openPoolsManagementConfirmationModal, setOpenPoolsManagementConfirmationModal] = useState(null); - const isInMemory = walletType === WalletType.InMemory; + const isHardwareWallet = walletType === WalletType.Trezor || walletType === WalletType.Ledger; // TODO unify const signAndSubmitTransaction = useCallback(async () => { if (!delegationTxBuilder) throw new Error('Unable to submit transaction. The delegationTxBuilder not available'); const isMultidelegation = draftPortfolio && draftPortfolio.length > 1; - if (!isInMemory && isMultidelegation) { + if (isHardwareWallet && isMultidelegation) { const isSupported = await isMultidelegationSupportedByDevice(walletType); if (!isSupported) { throw new Error('MULTIDELEGATION_NOT_SUPPORTED'); @@ -52,11 +52,18 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation } const signedTx = await delegationTxBuilder.build().sign(); await inMemoryWallet.submitTx(signedTx); - }, [delegationTxBuilder, inMemoryWallet, isInMemory, isMultidelegationSupportedByDevice, walletType, draftPortfolio]); + }, [ + delegationTxBuilder, + inMemoryWallet, + isHardwareWallet, + isMultidelegationSupportedByDevice, + walletType, + draftPortfolio, + ]); const handleSubmission = useCallback(async () => { setOpenPoolsManagementConfirmationModal(null); - if (isInMemory) { + if (!isHardwareWallet) { portfolioMutators.executeCommand({ type: 'DrawerContinue' }); return; } @@ -76,8 +83,8 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation setIsConfirmingTx(false); } }, [ - currentPortfolio, - isInMemory, + currentPortfolio.length, + isHardwareWallet, portfolioMutators, setIsRestaking, signAndSubmitTransaction, @@ -105,14 +112,14 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation }, [analytics, currentPortfolio, draftPortfolio, handleSubmission]); const confirmLabel = useMemo(() => { - if (!isInMemory) { + if (!isHardwareWallet) { const staleLabels = popupView ? t('drawer.confirmation.button.continueInAdvancedView') : t('drawer.confirmation.button.confirmWithDevice', { hardwareWallet: walletType }); return isConfirmingTx ? t('drawer.confirmation.button.signing') : staleLabels; } return t('drawer.confirmation.button.confirm'); - }, [isConfirmingTx, isInMemory, walletType, popupView, t]); + }, [isConfirmingTx, isHardwareWallet, walletType, popupView, t]); return ( <> diff --git a/yarn.lock b/yarn.lock index 6dedb7380..6a4e1308c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7756,9 +7756,9 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/hardware-ledger@npm:0.8.19, @cardano-sdk/hardware-ledger@npm:~0.8.19": - version: 0.8.19 - resolution: "@cardano-sdk/hardware-ledger@npm:0.8.19" +"@cardano-sdk/hardware-ledger@npm:0.9.0, @cardano-sdk/hardware-ledger@npm:~0.9.0": + version: 0.9.0 + resolution: "@cardano-sdk/hardware-ledger@npm:0.9.0" dependencies: "@cardano-foundation/ledgerjs-hw-app-cardano": ^6.0.0 "@cardano-sdk/core": ~0.30.0 @@ -7768,10 +7768,11 @@ __metadata: "@cardano-sdk/util": ~0.15.0 "@ledgerhq/hw-transport": ^6.28.1 "@ledgerhq/hw-transport-node-hid-noevents": ^6.27.12 - "@ledgerhq/hw-transport-webhid": ^6.27.12 + "@ledgerhq/hw-transport-webusb": ^6.27.12 + node-hid: ^2.1.2 ts-custom-error: ^3.2.0 ts-log: ^2.2.4 - checksum: ac2cb838d10de6cde097a5c1536fbc0f783fe2594bfa9ff00b00a0bf1d98c0dfb919a2439929e6b3969bf17fdd245c919fb3de26834027f29613d10886284cce + checksum: 46811188b5e055de130fc661c8235dfc40881427b570ff4ed301620808e413c7693e4f052e94b2c49947262310b3a44e65e44caa96055c9b930e5eb1f63cc2ff languageName: node linkType: hard @@ -7895,14 +7896,14 @@ __metadata: languageName: node linkType: hard -"@cardano-sdk/wallet@npm:0.35.2, @cardano-sdk/wallet@npm:~0.35.2": - version: 0.35.2 - resolution: "@cardano-sdk/wallet@npm:0.35.2" +"@cardano-sdk/wallet@npm:0.36.0, @cardano-sdk/wallet@npm:~0.36.0": + version: 0.36.0 + resolution: "@cardano-sdk/wallet@npm:0.36.0" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/dapp-connector": ~0.12.14 - "@cardano-sdk/hardware-ledger": ~0.8.19 + "@cardano-sdk/hardware-ledger": ~0.9.0 "@cardano-sdk/hardware-trezor": ~0.4.19 "@cardano-sdk/input-selection": ~0.12.26 "@cardano-sdk/key-management": ~0.20.1 @@ -7918,24 +7919,24 @@ __metadata: rxjs: ^7.4.0 ts-custom-error: ^3.2.0 ts-log: ^2.2.3 - checksum: af592b22f6290c6640b71234a28781c251f6c4b7ca2f8fb6cb608bb4e9581fb386ba75b1698bb83a81f638202ab55a300f7a395e2138eb96cc96ba9490c2c69d + checksum: 9fadb07be8ab4b603383e41b6712f2b09ed32075d60480bcf9bbfe798d2d858a769fceb308febea842b83d3473a225984f665834b190e9bee050386917a1a23c languageName: node linkType: hard -"@cardano-sdk/web-extension@npm:0.26.1": - version: 0.26.1 - resolution: "@cardano-sdk/web-extension@npm:0.26.1" +"@cardano-sdk/web-extension@npm:0.26.2": + version: 0.26.2 + resolution: "@cardano-sdk/web-extension@npm:0.26.2" dependencies: "@cardano-sdk/core": ~0.30.0 "@cardano-sdk/crypto": ~0.1.22 "@cardano-sdk/dapp-connector": ~0.12.14 - "@cardano-sdk/hardware-ledger": ~0.8.19 + "@cardano-sdk/hardware-ledger": ~0.9.0 "@cardano-sdk/hardware-trezor": ~0.4.19 "@cardano-sdk/key-management": ~0.20.1 "@cardano-sdk/tx-construction": ~0.18.2 "@cardano-sdk/util": ~0.15.0 "@cardano-sdk/util-rxjs": ~0.7.9 - "@cardano-sdk/wallet": ~0.35.2 + "@cardano-sdk/wallet": ~0.36.0 backoff-rxjs: ^7.0.0 lodash: ^4.17.21 rxjs: ^7.4.0 @@ -7943,7 +7944,7 @@ __metadata: ts-log: ^2.2.3 uuid: ^8.3.2 webextension-polyfill: ^0.8.0 - checksum: 3362f8fedb1af6ad2b167763edc640eb879c87d25ac27ec3f12be6506c61951d02fea4ad3de9f9817528333d7e6cb11298309b81f450cd58eac35d7a302f5476 + checksum: 08bb56073d783f730b254e7c6a8e51448e29d9ce436d0156da6f32c4420868ad1890126b2e24085721d150ceea693fa7d3e6640e24f02f97790263e6b718b3f9 languageName: node linkType: hard @@ -10583,8 +10584,8 @@ __metadata: "@cardano-sdk/input-selection": 0.12.26 "@cardano-sdk/tx-construction": 0.18.2 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/wallet": 0.35.2 - "@cardano-sdk/web-extension": 0.26.1 + "@cardano-sdk/wallet": 0.36.0 + "@cardano-sdk/web-extension": 0.26.2 "@emurgo/cardano-message-signing-asmjs": 1.0.1 "@emurgo/cip14-js": ~3.0.1 "@koralabs/handles-public-api-interfaces": ^1.6.6 @@ -10650,16 +10651,18 @@ __metadata: "@cardano-sdk/cardano-services-client": 0.18.0 "@cardano-sdk/core": 0.30.0 "@cardano-sdk/crypto": 0.1.22 - "@cardano-sdk/hardware-ledger": 0.8.19 + "@cardano-sdk/hardware-ledger": 0.9.0 "@cardano-sdk/hardware-trezor": 0.4.19 "@cardano-sdk/key-management": 0.20.1 "@cardano-sdk/util": 0.15.0 "@cardano-sdk/util-dev": 0.19.18 - "@cardano-sdk/wallet": 0.35.2 - "@cardano-sdk/web-extension": 0.26.1 + "@cardano-sdk/wallet": 0.36.0 + "@cardano-sdk/web-extension": 0.26.2 "@emurgo/cardano-message-signing-browser": 1.0.1 "@lace/common": 0.1.0 + "@ledgerhq/devices": ^8.2.1 "@stablelib/chacha20poly1305": 1.0.1 + "@trezor/transport": ^1.1.18 bignumber.js: 9.0.1 buffer: 6.0.3 classnames: 2.3.1 @@ -10821,6 +10824,8 @@ __metadata: "@cardano-sdk/input-selection": 0.12.26 "@cardano-sdk/tx-construction": 0.18.2 "@cardano-sdk/util": 0.15.0 + "@cardano-sdk/wallet": 0.36.0 + "@cardano-sdk/web-extension": 0.26.2 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0 @@ -10887,8 +10892,8 @@ __metadata: "@cardano-sdk/input-selection": 0.12.26 "@cardano-sdk/tx-construction": 0.18.2 "@cardano-sdk/util": 0.15.0 - "@cardano-sdk/wallet": 0.35.2 - "@cardano-sdk/web-extension": 0.26.1 + "@cardano-sdk/wallet": 0.36.0 + "@cardano-sdk/web-extension": 0.26.2 "@lace/cardano": ^0.1.0 "@lace/common": ^0.1.0 "@lace/core": 0.1.0 @@ -10989,6 +10994,18 @@ __metadata: languageName: node linkType: hard +"@ledgerhq/devices@npm:^8.2.1": + version: 8.2.1 + resolution: "@ledgerhq/devices@npm:8.2.1" + dependencies: + "@ledgerhq/errors": ^6.16.2 + "@ledgerhq/logs": ^6.12.0 + rxjs: ^7.8.1 + semver: ^7.3.5 + checksum: 5c7fa3004a4ebd30b0dcb8563642db308478bbec115102e5404dd0affcc99f880d094137e88c1f2cc064f78d65a5e946d5ebd8db89141977e32860885ea23ebe + languageName: node + linkType: hard + "@ledgerhq/errors@npm:^6.12.3": version: 6.12.3 resolution: "@ledgerhq/errors@npm:6.12.3" @@ -10996,6 +11013,13 @@ __metadata: languageName: node linkType: hard +"@ledgerhq/errors@npm:^6.16.2": + version: 6.16.2 + resolution: "@ledgerhq/errors@npm:6.16.2" + checksum: 2dd796c78b8428339c8906cfe2325e62c211f484576835198a9bf4efc8fed38b4ca5d342bfb08aef6c623720753ea3e5ce77e50367f2808ad5610e3ff54cec70 + languageName: node + linkType: hard + "@ledgerhq/hw-transport-node-hid-noevents@npm:^6.27.12": version: 6.27.12 resolution: "@ledgerhq/hw-transport-node-hid-noevents@npm:6.27.12" @@ -11009,15 +11033,15 @@ __metadata: languageName: node linkType: hard -"@ledgerhq/hw-transport-webhid@npm:^6.27.12": - version: 6.27.12 - resolution: "@ledgerhq/hw-transport-webhid@npm:6.27.12" +"@ledgerhq/hw-transport-webusb@npm:^6.27.12": + version: 6.28.4 + resolution: "@ledgerhq/hw-transport-webusb@npm:6.28.4" dependencies: - "@ledgerhq/devices": ^8.0.0 - "@ledgerhq/errors": ^6.12.3 - "@ledgerhq/hw-transport": ^6.28.1 - "@ledgerhq/logs": ^6.10.1 - checksum: 5f5253417ba6f5eb4d979e031a24c8bef9a642bb6e317c49a6abec33e316061c13aac6de8a7b353a907a11bbb7dd14ef16716be4017f4a008a6cc9136a366cdd + "@ledgerhq/devices": ^8.2.1 + "@ledgerhq/errors": ^6.16.2 + "@ledgerhq/hw-transport": ^6.30.4 + "@ledgerhq/logs": ^6.12.0 + checksum: 41e3c71b11c9cc8363e42c11874d00f3b4673a3ea6dde738d8f483ea08a4bfe7c529e5db92e17c6e3fe9d585f22eafd90d8c803320fe2e79de09fb31e998240b languageName: node linkType: hard @@ -11032,6 +11056,18 @@ __metadata: languageName: node linkType: hard +"@ledgerhq/hw-transport@npm:^6.30.4": + version: 6.30.4 + resolution: "@ledgerhq/hw-transport@npm:6.30.4" + dependencies: + "@ledgerhq/devices": ^8.2.1 + "@ledgerhq/errors": ^6.16.2 + "@ledgerhq/logs": ^6.12.0 + events: ^3.3.0 + checksum: f4878e0b1ea093c69d2905c94bc9567c1c6694af9cff034634dd9639bd318666f131f2394f67c95d3cfd96b64693e14e4735883136545a19d8df902e3b59bf5e + languageName: node + linkType: hard + "@ledgerhq/logs@npm:^6.10.1": version: 6.10.1 resolution: "@ledgerhq/logs@npm:6.10.1" @@ -11039,6 +11075,13 @@ __metadata: languageName: node linkType: hard +"@ledgerhq/logs@npm:^6.12.0": + version: 6.12.0 + resolution: "@ledgerhq/logs@npm:6.12.0" + checksum: 53fb9ceaf26b2a9fd6e7639b19119f4fef2f814d465fdd910e69c9486dce78137a1790e24f019a03bfabc87e19b2e6683f4da93a7fd203a61117a709fdf6484c + languageName: node + linkType: hard + "@leichtgewicht/ip-codec@npm:^2.0.1": version: 2.0.4 resolution: "@leichtgewicht/ip-codec@npm:2.0.4" @@ -18800,7 +18843,7 @@ __metadata: languageName: node linkType: hard -"@trezor/transport@npm:1.1.18": +"@trezor/transport@npm:1.1.18, @trezor/transport@npm:^1.1.18": version: 1.1.18 resolution: "@trezor/transport@npm:1.1.18" dependencies: 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 57/74] 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 5a05ab44f..3abefd644 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 d54705a36..ba3fceb66 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 a0b538118..69d27b640 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 58/74] 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 3fe3f6ffb..e38af3f71 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 d35d8e577..a296741be 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 d6311743d..7c738530d 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 3543d5846..bfe82ddb4 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 ef31ac490..000000000 --- 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 12f702cd7..1793d9bcb 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 000000000..1c90d58b6 --- /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 91e855d6e..335fc1244 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 000000000..b1975ca61 --- /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 4cd6d3f21..000000000 --- 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 ba9e1a07e..8b3c3bb02 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 f61f7b43f..553979ca9 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 414ad5568..000000000 --- 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 8bbe8713c..8fa11fa1f 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 f89e4f049..ba2382abc 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 000000000..22b91c4cd --- /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 be72dc088..d8edd320b 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 4151e756c..24738e188 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 59/74] 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 18576f082..b87bdfd6a 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 fca7546d3..bae32b93e 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 60/74] 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 9aa1930ee..036b3e079 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 1e645e54f..c1be02422 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 000000000..5e5b761e7 --- /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 db1f3b31d..000000000 --- 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 000000000..6649de9d5 --- /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 bf702eb08..000000000 --- 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 4ab90655e..0a5b85a7c 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 b6d5c2751..66ad86a78 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 628672b68..6c7ebfd82 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 61/74] 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 4935b1ce5..d9fafa054 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 bf927ebba..ac55acc54 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 56f0beb42..0ce143d7e 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 99785804c..7e40ce065 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 e4a6aaac5..ea9f95dd5 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 62/74] 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 523adc068..54ce65a6c 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 4fbfff55e..06423a09f 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 000000000..e4690c226 --- /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' +} From 6bf8d4742674494df12da949f6f3a5ff1f7b8e83 Mon Sep 17 00:00:00 2001 From: Dominik Guzei Date: Fri, 19 Apr 2024 18:36:53 +0200 Subject: [PATCH 63/74] feat(ui): Flag addresses as own or foreign [LW-10061] (#998) * feat(ui): add address tag ui component LW-10061 * feat(extension): flag own, address book and foreign addresses in dapp txs LW-10061 * feat(extension): flag own, address book and foreign addresses in activity details LW-10061 --- .../DappTransactionContainer.tsx | 9 ++- .../DappTransactionContainer.test.tsx | 13 ++-- .../src/lib/translations/en.json | 2 + .../src/utils/mocks/tx.ts | 14 ++-- .../components/TransactionDetailsProxy.tsx | 17 +++-- .../TransactionDetails.module.scss | 23 ++----- .../ActivityDetail/TransactionDetails.tsx | 30 +++++---- .../ActivityDetail/TransactionInputOutput.tsx | 20 ++++-- .../__tests__/TransactionDetails.test.tsx | 19 ++++++ .../DappAddressSections.tsx | 35 ++++++---- .../DappTransaction.stories.tsx | 51 ++++++++++++++- .../DappTransaction/DappTransaction.tsx | 9 ++- packages/core/src/ui/hooks/useTranslate.tsx | 2 +- .../__tests__/render-address-tag.test.ts | 33 ++++++++++ packages/core/src/ui/utils/index.ts | 1 + .../core/src/ui/utils/render-address-tag.tsx | 27 ++++++++ packages/core/wallaby.js | 7 ++ .../address-tags/address-tag.component.tsx | 31 +++++++++ .../address-tags/address-tag.css.ts | 33 ++++++++++ .../address-tags/address-tags.stories.tsx | 65 +++++++++++++++++++ .../src/design-system/address-tags/index.ts | 2 + .../src/design-system/address-tags/types.ts | 5 ++ packages/ui/src/design-system/index.ts | 1 + packages/ui/src/design-tokens/colors.data.ts | 7 ++ .../src/design-tokens/theme/dark-theme.css.ts | 10 +++ .../design-tokens/theme/light-theme.css.ts | 10 +++ 26 files changed, 409 insertions(+), 67 deletions(-) create mode 100644 packages/core/src/ui/utils/__tests__/render-address-tag.test.ts create mode 100644 packages/core/src/ui/utils/render-address-tag.tsx create mode 100644 packages/core/wallaby.js create mode 100644 packages/ui/src/design-system/address-tags/address-tag.component.tsx create mode 100644 packages/ui/src/design-system/address-tags/address-tag.css.ts create mode 100644 packages/ui/src/design-system/address-tags/address-tags.stories.tsx create mode 100644 packages/ui/src/design-system/address-tags/index.ts create mode 100644 packages/ui/src/design-system/address-tags/types.ts diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx index 5654c2393..d5de946fa 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx @@ -5,7 +5,7 @@ import { Flex } from '@lace/ui'; import { useViewsFlowContext } from '@providers/ViewFlowProvider'; import { Wallet } from '@lace/cardano'; -import { withAddressBookContext } from '@src/features/address-book/context'; +import { useAddressBookContext, withAddressBookContext } from '@src/features/address-book/context'; import { useWalletStore } from '@stores'; import { useFetchCoinPrice, useChainHistoryProvider } from '@hooks'; import { @@ -23,6 +23,7 @@ import { useCurrencyStore, useAppSettingsContext } from '@providers'; import { logger } from '@lib/wallet-api-ui'; import { useComputeTxCollateral } from '@hooks/useComputeTxCollateral'; import { utxoAndBackendChainHistoryResolver } from '@src/utils/utxo-chain-history-resolver'; +import { AddressBookSchema, useDbStateValue } from '@lib/storage'; interface DappTransactionContainerProps { errorMessage?: string; @@ -43,6 +44,10 @@ export const DappTransactionContainer = withAddressBookContext( walletState } = useWalletStore(); + const ownAddresses = useObservable(inMemoryWallet.addresses$)?.map((a) => a.address); + const { list: addressBook } = useAddressBookContext() as useDbStateValue; + const addressToNameMap = new Map(addressBook?.map((entry) => [entry.address as string, entry.name])); + const { fiatCurrency } = useCurrencyStore(); const { priceResult } = useFetchCoinPrice(); @@ -146,6 +151,8 @@ export const DappTransactionContainer = withAddressBookContext( errorMessage={errorMessage} toAddress={toAddressTokens} collateral={txCollateral} + ownAddresses={ownAddresses} + addressToNameMap={addressToNameMap} /> ) : ( diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx index 98afa976f..029399959 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/__tests__/DappTransactionContainer.test.tsx @@ -30,12 +30,13 @@ import { DappTransactionContainer } from '../DappTransactionContainer'; import '@testing-library/jest-dom'; import { BehaviorSubject } from 'rxjs'; import { act } from 'react-dom/test-utils'; -import { buildMockTx } from '@src/utils/mocks/tx'; +import { buildMockTx, sendingAddress } from '@src/utils/mocks/tx'; import { Wallet } from '@lace/cardano'; import { SignTxData } from '../types'; import { getWrapper } from '../testing.utils'; import { TransactionWitnessRequest } from '@cardano-sdk/web-extension'; import { cardanoCoin } from '@src/utils/constants'; +import { AddressBookSchema } from '@lib/storage'; const { Cardano, Crypto } = Wallet; @@ -51,6 +52,7 @@ const mockedAssetsInfo = new Map([['id', 'data']]); const assetInfo$ = new BehaviorSubject(mockedAssetsInfo); const available$ = new BehaviorSubject([]); const signed$ = new BehaviorSubject([]); +const addresses$ = new BehaviorSubject([sendingAddress]); const rewardAccounts$ = new BehaviorSubject([ { // eslint-disable-next-line unicorn/consistent-destructuring @@ -65,6 +67,7 @@ const protocolParameters$ = new BehaviorSubject({ }); const inMemoryWallet = { + addresses$, assetInfo$, balance: { utxo: { @@ -133,12 +136,12 @@ jest.mock('react-i18next', () => { }; }); -const addressList = ['addressList']; +const addressBook: AddressBookSchema[] = []; jest.mock('@src/features/address-book/context', () => ({ // eslint-disable-next-line @typescript-eslint/no-explicit-any ...jest.requireActual('@src/features/address-book/context'), withAddressBookContext: mockWithAddressBookContext, - useAddressBookContext: () => ({ list: addressList }) + useAddressBookContext: () => ({ list: addressBook }) })); jest.mock('antd', () => { @@ -332,7 +335,9 @@ describe('Testing DappTransactionContainer component', () => { errorMessage, coinSymbol: 'ADA', collateral: BigInt(1_000_000), - txInspectionDetails + txInspectionDetails, + ownAddresses: [sendingAddress.address], + addressToNameMap: new Map() }, {} ); diff --git a/apps/browser-extension-wallet/src/lib/translations/en.json b/apps/browser-extension-wallet/src/lib/translations/en.json index 5a6150caf..ce7bf838d 100644 --- a/apps/browser-extension-wallet/src/lib/translations/en.json +++ b/apps/browser-extension-wallet/src/lib/translations/en.json @@ -1368,6 +1368,8 @@ "core.receive.usedAddresses.copy": "Copy Address", "core.receive.usedAddresses.addressCopied": "Address copied", "core.receive.showUsedAddresses": "Show used addresses", + "core.addressTags.own": "own", + "core.addressTags.foreign": "foreign", "addressesDiscovery.overlay.title": "Your wallet is syncing, this might take a few minutes", "addressesDiscovery.toast.errorText": "Wallet failed to sync", "addressesDiscovery.toast.successText": "Wallet synced successfully", diff --git a/apps/browser-extension-wallet/src/utils/mocks/tx.ts b/apps/browser-extension-wallet/src/utils/mocks/tx.ts index 8ea60d13c..a41522f82 100644 --- a/apps/browser-extension-wallet/src/utils/mocks/tx.ts +++ b/apps/browser-extension-wallet/src/utils/mocks/tx.ts @@ -1,11 +1,13 @@ /* eslint-disable no-magic-numbers */ import { Wallet } from '@lace/cardano'; -const sendingAddress = Wallet.Cardano.PaymentAddress( - 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' -); +export const sendingAddress = { + address: Wallet.Cardano.PaymentAddress( + 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' + ) +} as Wallet.KeyManagement.GroupedAddress; -const receivingAddress = Wallet.Cardano.PaymentAddress( +export const receivingAddress = Wallet.Cardano.PaymentAddress( 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' ); @@ -34,7 +36,7 @@ export const buildMockTx = ( ]), inputs: args.inputs ?? [ { - address: sendingAddress, + address: sendingAddress.address, index: 0, txId: Wallet.Cardano.TransactionId('bb217abaca60fc0ca68c1555eca6a96d2478547818ae76ce6836133f3cc546e0') } @@ -64,7 +66,7 @@ export const buildMockTx = ( } }, { - address: sendingAddress, + address: sendingAddress.address, value: { assets: new Map([ [Wallet.Cardano.AssetId('659f2917fb63f12b33667463ee575eeac1845bbc736b9c0bbc40ba8254534c41'), BigInt(1)] diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx index 412cb9a52..e0223dbe9 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx @@ -9,6 +9,7 @@ import { TxDirections } from '@src/types'; import { APP_MODE_POPUP } from '@src/utils/constants'; import { config } from '@src/config'; import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; +import { useObservable } from '@lace/common'; type TransactionDetailsProxyProps = { name: string; @@ -21,13 +22,23 @@ export const TransactionDetailsProxy = withAddressBookContext( ({ name, activityInfo, direction, status, amountTransformer }: TransactionDetailsProxyProps): ReactElement => { const analytics = useAnalyticsContext(); const { + inMemoryWallet, walletInfo, environmentName, walletUI: { cardanoCoin, appMode } } = useWalletStore(); const isPopupView = appMode === APP_MODE_POPUP; const openExternalLink = useExternalLinkOpener(); + + // Prepare own addresses of active account + const walletAddresses = useObservable(inMemoryWallet.addresses$)?.map((a) => a.address); + + // Prepare address book data as Map const { list: addressList } = useAddressBookContext(); + const addressToNameMap = useMemo( + () => new Map(addressList?.map((item: AddressListType) => [item.address, item.name])), + [addressList] + ); const { CEXPLORER_BASE_URL, CEXPLORER_URL_PATHS } = config(); const explorerBaseUrl = useMemo( @@ -72,11 +83,6 @@ export const TransactionDetailsProxy = withAddressBookContext( externalLink && status === ActivityStatus.SUCCESS && openExternalLink(externalLink); }; - const addressToNameMap = useMemo( - () => new Map(addressList?.map((item: AddressListType) => [item.address, item.name])), - [addressList] - ); - return ( // eslint-disable-next-line react/jsx-pascal-case ; isPopupView?: boolean; openExternalLink?: (url: string) => void; @@ -122,6 +126,7 @@ export const TransactionDetails = ({ txSummary = [], coinSymbol, pools, + ownAddresses, addressToNameMap, isPopupView, openExternalLink, @@ -353,22 +358,19 @@ export const TransactionDetails = ({
)} {(summary.addr as string[]).map((addr) => { - const addrName = addressToNameMap?.get(addr); const address = isPopupView ? ( ) : ( {addr} ); return ( -
- {addrName ? ( -
- {addrName} - {address} -
- ) : ( - address - )} +
+ {address} + {renderAddressTag(addr, getAddressTagTranslations(t), ownAddresses, addressToNameMap)}
); })} @@ -590,6 +592,8 @@ export const TransactionDetails = ({ coinSymbol={coinSymbol} withSeparatorLine sendAnalytics={sendAnalyticsInputs} + ownAddresses={ownAddresses} + addressToNameMap={addressToNameMap} /> )} {addrOutputs?.length > 0 && ( @@ -604,6 +608,8 @@ export const TransactionDetails = ({ }} coinSymbol={coinSymbol} sendAnalytics={sendAnalyticsOutputs} + ownAddresses={ownAddresses} + addressToNameMap={addressToNameMap} /> )} {metadata?.length > 0 && ( diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionInputOutput.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionInputOutput.tsx index bb9369e65..0d930d140 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionInputOutput.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionInputOutput.tsx @@ -2,14 +2,18 @@ import React, { useState } from 'react'; import { Tooltip } from 'antd'; import cn from 'classnames'; -import { addEllipsis, Button } from '@lace/common'; import { InfoCircleOutlined, DownOutlined } from '@ant-design/icons'; +import { addEllipsis, Button } from '@lace/common'; + import { TxOutputInput } from './TransactionDetailAsset'; import { TranslationsFor } from '../../utils/types'; import { ReactComponent as BracketDown } from '../../assets/icons/bracket-down.component.svg'; import styles from './TransactionInputOutput.module.scss'; +import { Flex } from '@lace/ui'; +import { getAddressTagTranslations, renderAddressTag } from '@ui/utils'; +import { useTranslate } from '@ui/hooks'; const rotateOpen: React.CSSProperties = { transform: 'rotate(180deg)', @@ -30,6 +34,8 @@ export interface TransactionInputOutputProps { translations: TranslationsFor<'address' | 'sent'>; coinSymbol: string; withSeparatorLine?: boolean; + ownAddresses: string[]; + addressToNameMap: Map; sendAnalytics?: () => void; } @@ -42,9 +48,12 @@ export const TransactionInputOutput = ({ translations, coinSymbol, withSeparatorLine, + ownAddresses, + addressToNameMap, sendAnalytics }: TransactionInputOutputProps): React.ReactElement => { const [isVisible, setIsVisible] = useState(); + const { t } = useTranslate(); const animation = isVisible ? rotateOpen : rotateClose; const Icon = BracketDown ? : ; @@ -79,9 +88,12 @@ export const TransactionInputOutput = ({
{translations.address}
-
- {addEllipsis(inputAddress, 8, 8)} -
+ +
+ {addEllipsis(inputAddress, 8, 8)} +
+ {renderAddressTag(inputAddress, getAddressTagTranslations(t), ownAddresses, addressToNameMap)} +
diff --git a/packages/core/src/ui/components/ActivityDetail/__tests__/TransactionDetails.test.tsx b/packages/core/src/ui/components/ActivityDetail/__tests__/TransactionDetails.test.tsx index 73522e5c2..26a3d9558 100644 --- a/packages/core/src/ui/components/ActivityDetail/__tests__/TransactionDetails.test.tsx +++ b/packages/core/src/ui/components/ActivityDetail/__tests__/TransactionDetails.test.tsx @@ -35,6 +35,7 @@ describe('Testing ActivityDetailsBrowser component', () => { ], amountTransformer: (amount) => `${amount} $`, coinSymbol: 'ADA', + ownAddresses: [], addressToNameMap: new Map() }; @@ -83,4 +84,22 @@ describe('Testing ActivityDetailsBrowser component', () => { const { queryByTestId: query } = render(); expect(query('tx-metadata')).not.toBeInTheDocument(); }); + + test('should show address tag for inputs', async () => { + // use empty addrOutputs (so we get only one toggle button for inputs) + const { findByTestId } = render(); + const inputsSectionToggle = await findByTestId('tx-addr-list_toggle'); + fireEvent.click(inputsSectionToggle); + + expect(await findByTestId('address-tag')).toBeVisible(); + }); + + test('should show address tag for outputs', async () => { + // use empty addrOutputs (so we get only one toggle button for outputs) + const { findByTestId } = render(); + const outputsSectionToggle = await findByTestId('tx-addr-list_toggle'); + fireEvent.click(outputsSectionToggle); + + expect(await findByTestId('address-tag')).toBeVisible(); + }); }); diff --git a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx index 34abb6ebd..69a065701 100644 --- a/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx +++ b/packages/core/src/ui/components/DappAddressSections/DappAddressSections.tsx @@ -8,8 +8,9 @@ import { Typography } from 'antd'; import styles from './DappAddressSections.module.scss'; import { useTranslate } from '@src/ui/hooks'; -import { TransactionAssets, SummaryExpander, DappTransactionSummary, Tooltip } from '@lace/ui'; +import { Flex, TransactionAssets, SummaryExpander, DappTransactionSummary, Tooltip } from '@lace/ui'; import classNames from 'classnames'; +import { getAddressTagTranslations, renderAddressTag } from '@ui/utils/render-address-tag'; interface GroupedAddressAssets { nfts: Array; @@ -23,6 +24,8 @@ export interface DappAddressSectionProps { isToAddressesEnabled: boolean; isFromAddressesEnabled: boolean; coinSymbol: string; + ownAddresses: string[]; + addressToNameMap?: Map; } const tryDecodeAsUtf8 = ( @@ -101,7 +104,9 @@ export const DappAddressSections = ({ groupedToAddresses, isToAddressesEnabled, isFromAddressesEnabled, - coinSymbol + coinSymbol, + ownAddresses, + addressToNameMap }: DappAddressSectionProps): React.ReactElement => { const { t } = useTranslate(); @@ -121,11 +126,14 @@ export const DappAddressSections = ({ {t('core.dappTransaction.address')} - - - {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} - - + + + + {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} + + + {renderAddressTag(address, getAddressTagTranslations(t), ownAddresses, addressToNameMap)} +
{(addressData.tokens.length > 0 || addressData.coins.length > 0) && ( <> @@ -179,11 +187,14 @@ export const DappAddressSections = ({ {t('core.dappTransaction.address')} - - - {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} - - + + + + {addEllipsis(address, charBeforeEllipsisName, charAfterEllipsisName)} + + + {renderAddressTag(address, getAddressTagTranslations(t), ownAddresses, addressToNameMap)} +
{(addressData.tokens.length > 0 || addressData.coins.length > 0) && ( <> diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx index f734549d4..691d4b8b8 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.stories.tsx @@ -3,6 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { DappTransaction } from './DappTransaction'; import { ComponentProps } from 'react'; import { Wallet } from '@lace/cardano'; +import { AssetInfoWithAmount, TokenTransferValue } from '@cardano-sdk/core'; const meta: Meta = { title: 'DappTransaction', @@ -15,14 +16,60 @@ const meta: Meta = { export default meta; type Story = StoryObj; +const fromAddress = Wallet.Cardano.PaymentAddress( + 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' +); + +const toAddress = Wallet.Cardano.PaymentAddress( + 'addr_test1qpfhhfy2qgls50r9u4yh0l7z67xpg0a5rrhkmvzcuqrd0znuzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q9gw0lz' +); + +const toAddressBookAddress = Wallet.Cardano.PaymentAddress( + 'addr_test1qzqgfww9svrzelxrnlml0nmdq4yevwke7ck7ae27u5ptmq5dwuq25p4hr0yxhg4pce0d6t7v4c0msy3vr3xppygn9ktqe77950' +); + +const PXLAssetId = Wallet.Cardano.AssetId('1ec85dcee27f2d90ec1f9a1e4ce74a667dc9be8b184463223f9c960150584c'); +const PXLPolicyId = Wallet.Cardano.AssetId.getPolicyId(PXLAssetId); +const PXLAssetName = Wallet.Cardano.AssetId.getAssetName(PXLAssetId); +const PXLAssetInfo: AssetInfoWithAmount = { + // eslint-disable-next-line no-magic-numbers + amount: BigInt(100_000), + assetInfo: { + assetId: PXLAssetId, + name: PXLAssetName, + policyId: PXLPolicyId, + fingerprint: Wallet.Cardano.AssetFingerprint.fromParts(PXLPolicyId, PXLAssetName), + // eslint-disable-next-line no-magic-numbers + supply: BigInt(11_242_452_000), + quantity: BigInt(1) + } +}; + +const fromAddressTokens: TokenTransferValue = { + // eslint-disable-next-line no-magic-numbers + coins: BigInt(-100_000), + assets: new Map([[PXLAssetId, { ...PXLAssetInfo, amount: -PXLAssetInfo.amount }]]) +}; + +const toAddressTokens: TokenTransferValue = { + // eslint-disable-next-line no-magic-numbers + coins: BigInt(100_000), + assets: new Map([[PXLAssetId, PXLAssetInfo]]) +}; + const data: ComponentProps = { dappInfo: { name: 'Mint' }, coinSymbol: 'tAda', fiatCurrencyCode: 'usd', - fromAddress: new Map(), - toAddress: new Map(), + ownAddresses: [fromAddress], + addressToNameMap: new Map([[toAddressBookAddress, 'test']]), + fromAddress: new Map([[fromAddress, fromAddressTokens]]), + toAddress: new Map([ + [toAddress, toAddressTokens], + [toAddressBookAddress, toAddressTokens] + ]), // eslint-disable-next-line no-magic-numbers collateral: 150_000 as unknown as bigint, txInspectionDetails: { diff --git a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx index aed6f5cfd..15ea1a1b0 100644 --- a/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx +++ b/packages/core/src/ui/components/DappTransaction/DappTransaction.tsx @@ -30,6 +30,8 @@ export interface DappTransactionProps { /** tokens send to being sent to or from the user */ fromAddress: Map; toAddress: Map; + ownAddresses?: string[]; + addressToNameMap?: Map; collateral?: bigint; } @@ -57,6 +59,7 @@ const groupAddresses = (addresses: Map { const { t } = useTranslate(); @@ -205,6 +210,8 @@ export const DappTransaction = ({ groupedFromAddresses={groupedFromAddresses} groupedToAddresses={groupedToAddresses} coinSymbol={coinSymbol} + ownAddresses={ownAddresses} + addressToNameMap={addressToNameMap} />
diff --git a/packages/core/src/ui/hooks/useTranslate.tsx b/packages/core/src/ui/hooks/useTranslate.tsx index 8e20494d3..773093b4a 100644 --- a/packages/core/src/ui/hooks/useTranslate.tsx +++ b/packages/core/src/ui/hooks/useTranslate.tsx @@ -6,7 +6,7 @@ import fallbackInstance from '../lib/i18n'; // If no i18n instance found in context then use the local one setI18n(fallbackInstance); -interface UseTranslate { +export interface UseTranslate { t: (key: string | string[], defaultValue?: string, options?: TOptions) => string; Trans: typeof Trans; i18n: typeof i18n; diff --git a/packages/core/src/ui/utils/__tests__/render-address-tag.test.ts b/packages/core/src/ui/utils/__tests__/render-address-tag.test.ts new file mode 100644 index 000000000..153b40e63 --- /dev/null +++ b/packages/core/src/ui/utils/__tests__/render-address-tag.test.ts @@ -0,0 +1,33 @@ +import { render } from '@testing-library/react'; +import '@testing-library/jest-dom'; +import { Wallet } from '@lace/cardano'; +import { AddressTagTranslations, renderAddressTag } from '@ui/utils'; + +const address = Wallet.Cardano.PaymentAddress( + 'addr_test1qq585l3hyxgj3nas2v3xymd23vvartfhceme6gv98aaeg9muzcjqw982pcftgx53fu5527z2cj2tkx2h8ux2vxsg475q2g7k3g' +); + +const translations: AddressTagTranslations = { + own: 'own', + foreign: 'foreign' +}; + +describe('rendering correct tags for addresses', () => { + test('should tag own addresses', async () => { + const ownAddresses = [address]; + const { findByTestId } = render(renderAddressTag(address, translations, ownAddresses)); + expect(await findByTestId('address-tag')).toContainHTML(translations.own); + }); + + test('should tag foreign addresses', async () => { + const { findByTestId } = render(renderAddressTag(address, translations)); + expect(await findByTestId('address-tag')).toContainHTML(translations.foreign); + }); + + test('should tag address book addresses', async () => { + const addressName = 'test'; + const addressToNameMap = new Map([[address, addressName]]); + const { findByTestId } = render(renderAddressTag(address, translations, [], addressToNameMap)); + expect(await findByTestId('address-tag')).toContainHTML(`${translations.foreign}/${addressName}`); + }); +}); diff --git a/packages/core/src/ui/utils/index.ts b/packages/core/src/ui/utils/index.ts index 2983ba98d..1892c7749 100644 --- a/packages/core/src/ui/utils/index.ts +++ b/packages/core/src/ui/utils/index.ts @@ -1,3 +1,4 @@ export * from './sanitize-number'; export * from './handle'; export * from './address-form'; +export * from './render-address-tag'; diff --git a/packages/core/src/ui/utils/render-address-tag.tsx b/packages/core/src/ui/utils/render-address-tag.tsx new file mode 100644 index 000000000..b5cf677fe --- /dev/null +++ b/packages/core/src/ui/utils/render-address-tag.tsx @@ -0,0 +1,27 @@ +import * as React from 'react'; +import { AddressTag, AddressTagVariants } from '@lace/ui'; +import { UseTranslate } from '@ui/hooks'; + +export type AddressTagTranslations = { own: string; foreign: string }; + +export const getAddressTagTranslations = (t: UseTranslate['t']): AddressTagTranslations => ({ + own: t('core.addressTags.own'), + foreign: t('core.addressTags.foreign') +}); + +export const renderAddressTag = ( + address: string, + translations: AddressTagTranslations, + ownAddresses: string[] = [], + addressToNameMap: Map = new Map() // address, name +): JSX.Element => { + const matchingAddressName = addressToNameMap.get(address); + return ownAddresses.includes(address) ? ( + {translations.own} + ) : ( + + {translations.foreign} + {matchingAddressName ? `/${matchingAddressName}` : ''} + + ); +}; diff --git a/packages/core/wallaby.js b/packages/core/wallaby.js new file mode 100644 index 000000000..6b7c500d8 --- /dev/null +++ b/packages/core/wallaby.js @@ -0,0 +1,7 @@ +module.exports = () => { + return { + testFramework: { + configFile: 'test/jest.config.js' + } + }; +}; diff --git a/packages/ui/src/design-system/address-tags/address-tag.component.tsx b/packages/ui/src/design-system/address-tags/address-tag.component.tsx new file mode 100644 index 000000000..9f9e1cf89 --- /dev/null +++ b/packages/ui/src/design-system/address-tags/address-tag.component.tsx @@ -0,0 +1,31 @@ +import type { PropsWithChildren, HTMLAttributes } from 'react'; +import React from 'react'; + +import cs from 'classnames'; + +import * as cx from './address-tag.css'; + +import type { AddressTagVariants } from './types'; + +export type AddressBaseTageProps = PropsWithChildren< + HTMLAttributes & { + variant: AddressTagVariants; + testId?: string; + } +>; + +export const AddressTag = ({ + children, + className, + variant, + testId = 'address-tag', + ...restProps +}: Readonly): JSX.Element => ( +
+ {children} +
+); diff --git a/packages/ui/src/design-system/address-tags/address-tag.css.ts b/packages/ui/src/design-system/address-tags/address-tag.css.ts new file mode 100644 index 000000000..604787839 --- /dev/null +++ b/packages/ui/src/design-system/address-tags/address-tag.css.ts @@ -0,0 +1,33 @@ +import { recipe } from '@vanilla-extract/recipes'; + +import { vars } from '../../design-tokens'; + +import { AddressTagVariants } from './types'; + +export const addressTag = recipe({ + base: { + fontSize: vars.fontSizes.$12, + fontWeight: vars.fontWeights.$medium, + borderRadius: vars.radius.$medium, + padding: `0 ${vars.spacing.$8}`, + display: 'flex', + alignItems: 'center', + height: vars.spacing.$24, + }, + variants: { + scheme: { + [AddressTagVariants.Own]: { + color: vars.colors.$address_tag_own_color, + backgroundColor: vars.colors.$address_tag_own_bgColor, + }, + [AddressTagVariants.Handle]: { + color: vars.colors.$address_tag_handle_color, + backgroundColor: vars.colors.$address_tag_handle_bgColor, + }, + [AddressTagVariants.Foreign]: { + color: vars.colors.$address_tag_foreign_color, + backgroundColor: vars.colors.$address_tag_foreign_bgColor, + }, + }, + }, +}); diff --git a/packages/ui/src/design-system/address-tags/address-tags.stories.tsx b/packages/ui/src/design-system/address-tags/address-tags.stories.tsx new file mode 100644 index 000000000..227884074 --- /dev/null +++ b/packages/ui/src/design-system/address-tags/address-tags.stories.tsx @@ -0,0 +1,65 @@ +import React from 'react'; + +import type { Meta } from '@storybook/react'; + +import { ThemeColorScheme, LocalThemeProvider } from '../../design-tokens'; +import { page, Section, Variants } from '../decorators'; +import { Cell, Grid } from '../grid'; + +import { AddressTag } from './address-tag.component'; +import { AddressTagVariants } from './types'; + +export default { + title: 'AddressTag', + decorators: [ + page({ + title: 'Address Tag', + subtitle: 'Simple component to flag addresses as own, handle or foreign.', + }), + ], +} as Meta; + +export const Overview = (): JSX.Element => { + const variantsData = [ + { + Component: AddressTag, + variant: AddressTagVariants.Own, + }, + { + Component: AddressTag, + variant: AddressTagVariants.Handle, + }, + { + Component: AddressTag, + variant: AddressTagVariants.Foreign, + }, + ]; + const renderTable = (showHeader = false): JSX.Element => ( + v.variant) : []} + > + + {variantsData.map(({ Component, variant }) => ( + + {variant} + + ))} + + + ); + + return ( + + +
+ <> + {renderTable(true)} + +
{renderTable()}
+
+ +
+
+
+ ); +}; diff --git a/packages/ui/src/design-system/address-tags/index.ts b/packages/ui/src/design-system/address-tags/index.ts new file mode 100644 index 000000000..4b40d22cb --- /dev/null +++ b/packages/ui/src/design-system/address-tags/index.ts @@ -0,0 +1,2 @@ +export { AddressTag } from './address-tag.component'; +export { AddressTagVariants } from './types'; diff --git a/packages/ui/src/design-system/address-tags/types.ts b/packages/ui/src/design-system/address-tags/types.ts new file mode 100644 index 000000000..d0721894e --- /dev/null +++ b/packages/ui/src/design-system/address-tags/types.ts @@ -0,0 +1,5 @@ +export enum AddressTagVariants { + Own = 'Own', + Handle = 'Handle', + Foreign = 'Foreign', +} diff --git a/packages/ui/src/design-system/index.ts b/packages/ui/src/design-system/index.ts index dcc11a288..a4704d4d5 100644 --- a/packages/ui/src/design-system/index.ts +++ b/packages/ui/src/design-system/index.ts @@ -53,3 +53,4 @@ export { SummaryExpander } from './summary-expander'; export * from './auto-suggest-box'; export * from './table'; export { InfoBar } from './info-bar'; +export * from './address-tags'; diff --git a/packages/ui/src/design-tokens/colors.data.ts b/packages/ui/src/design-tokens/colors.data.ts index 4c9ef372b..12be7aea9 100644 --- a/packages/ui/src/design-tokens/colors.data.ts +++ b/packages/ui/src/design-tokens/colors.data.ts @@ -308,6 +308,13 @@ export const colors = { $info_bar_container_bgColor: '', $info_bar_message_color: '', $info_bar_icon_color: '', + + $address_tag_own_color: '', + $address_tag_own_bgColor: '', + $address_tag_handle_color: '', + $address_tag_handle_bgColor: '', + $address_tag_foreign_color: '', + $address_tag_foreign_bgColor: '', }; export type Colors = typeof colors; diff --git a/packages/ui/src/design-tokens/theme/dark-theme.css.ts b/packages/ui/src/design-tokens/theme/dark-theme.css.ts index 159d6e1b5..01e0b0551 100644 --- a/packages/ui/src/design-tokens/theme/dark-theme.css.ts +++ b/packages/ui/src/design-tokens/theme/dark-theme.css.ts @@ -409,6 +409,16 @@ const colors: Colors = { $info_bar_container_bgColor: darkColorScheme.$primary_dark_grey_plus, $info_bar_icon_color: darkColorScheme.$secondary_data_pink, $info_bar_message_color: darkColorScheme.$primary_light_grey, + + $address_tag_own_color: darkColorScheme.$secondary_data_pink, + $address_tag_own_bgColor: rgba(darkColorScheme.$secondary_data_pink, 0.1), + $address_tag_handle_color: darkColorScheme.$primary_accent_purple, + $address_tag_handle_bgColor: rgba( + darkColorScheme.$primary_accent_purple, + 0.1, + ), + $address_tag_foreign_color: darkColorScheme.$primary_dark_grey, + $address_tag_foreign_bgColor: darkColorScheme.$primary_light_grey, }; const elevation: Elevation = { diff --git a/packages/ui/src/design-tokens/theme/light-theme.css.ts b/packages/ui/src/design-tokens/theme/light-theme.css.ts index 8ba4cc1f0..fd380d355 100644 --- a/packages/ui/src/design-tokens/theme/light-theme.css.ts +++ b/packages/ui/src/design-tokens/theme/light-theme.css.ts @@ -437,6 +437,16 @@ const colors: Colors = { $info_bar_container_bgColor: lightColorScheme.$secondary_cream, $info_bar_icon_color: lightColorScheme.$secondary_data_pink, $info_bar_message_color: lightColorScheme.$primary_black, + + $address_tag_own_color: lightColorScheme.$secondary_data_pink, + $address_tag_own_bgColor: rgba(lightColorScheme.$secondary_data_pink, 0.1), + $address_tag_handle_color: lightColorScheme.$primary_accent_purple, + $address_tag_handle_bgColor: rgba( + lightColorScheme.$primary_accent_purple, + 0.1, + ), + $address_tag_foreign_color: lightColorScheme.$primary_dark_grey, + $address_tag_foreign_bgColor: lightColorScheme.$primary_light_grey, }; export const elevation: Elevation = { From 57a5863da91467c68df140d80bb07575345cfee4 Mon Sep 17 00:00:00 2001 From: John Oshalusi Date: Fri, 19 Apr 2024 19:12:32 +0100 Subject: [PATCH 64/74] feat: add analytics to the hardware wallet flow (#1073) * feat: add analytics to the hardware wallet flow * fix: name analytics event properly --- .../AnalyticsProvider/analyticsTracker/events.ts | 9 +++++---- .../AnalyticsProvider/analyticsTracker/types.ts | 8 ++++---- .../HardwareWalletFlow/HardwareWalletFlow.tsx | 6 ++++++ .../components/HardwareWalletFlow/StepConnect.tsx | 10 ++++++++-- .../components/HardwareWalletFlow/StepCreate.tsx | 4 +--- packages/common/src/analytics/types.ts | 14 ++++++-------- .../WalletSetupSelectAccountsStepRevamp.tsx | 9 +++++++-- 7 files changed, 37 insertions(+), 23 deletions(-) diff --git a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts index 56053b8ce..7fb4c7136 100644 --- a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts +++ b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/events.ts @@ -26,11 +26,12 @@ export const postHogOnboardingActions: PostHogOnboardingActionsType = { PostHogAction.OnboardingRestoreEnterRecoveryPhrasePasteFromClipboardClick }, hw: { - WALLET_NAME_NEXT_CLICK: PostHogAction.OnboardingHWNameNextClick, - CONNECT_HW_NEXT_CLICK: PostHogAction.OnboardingHWConnectNextClick, - SETUP_HW_WALLET_NEXT_CLICK: PostHogAction.OnboardingHWSelectAccountNextClick, SETUP_OPTION_CLICK: PostHogAction.OnboardingHWClick, - DONE_GO_TO_WALLET: PostHogAction.OnboardingHWDoneGoToWallet + CONNECT_HW_VIEW: PostHogAction.OnboardingHWConnectView, + HW_POPUP_CONNECT_CLICK: PostHogAction.OnboardingHWPopupConnectClick, + CONNECT_HW_TRY_AGAIN_CLICK: PostHogAction.OnboardingHWConnectTryAgainClick, + SETUP_HW_ACCOUNT_NO_CLICK: PostHogAction.OnboardingHWSetupWalletAccountNoClick, + ENTER_WALLET: PostHogAction.OnboardingHWEnterWalletClick }, // eslint-disable-next-line camelcase forgot_password: { diff --git a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts index 1e9f72dc9..0d8ae5e8c 100644 --- a/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts +++ b/apps/browser-extension-wallet/src/providers/AnalyticsProvider/analyticsTracker/types.ts @@ -44,15 +44,15 @@ export type PostHogActionsKeys = | 'ANALYTICS_AGREE_CLICK' | 'LEARN_MORE_CLICK' | 'ANALYTICS_REJECT_CLICK' - | 'WALLET_NAME_NEXT_CLICK' | 'SAVE_RECOVERY_PHRASE_NEXT_CLICK' | 'ENTER_RECOVERY_PHRASE_NEXT_CLICK' | 'ENTER_WALLET' | 'GOT_IT_CLICK' | 'PIN_EXTENSION_CLICK' - | 'CONNECT_HW_NEXT_CLICK' - | 'SETUP_HW_WALLET_NEXT_CLICK' - | 'DONE_GO_TO_WALLET' + | 'CONNECT_HW_VIEW' + | 'HW_POPUP_CONNECT_CLICK' + | 'CONNECT_HW_TRY_AGAIN_CLICK' + | 'SETUP_HW_ACCOUNT_NO_CLICK' | 'WALLET_NAME_PASSWORD_NEXT_CLICK' | 'RECOVERY_PHRASE_INTRO_WATCH_VIDEO_CLICK' | 'RECOVERY_PHRASE_INTRO_VIDEO_GOTIT_CLICK' diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx index 5c8c00e50..2fcdcf58f 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/HardwareWalletFlow.tsx @@ -11,6 +11,8 @@ import { walletRoutePaths } from '@routes/wallet-paths'; import { StepConnect } from './StepConnect'; import { WalletType } from '@cardano-sdk/web-extension'; import { StepCreate, WalletData } from './StepCreate'; +import { useAnalyticsContext } from '@providers'; +import { postHogOnboardingActions } from '@providers/AnalyticsProvider/analyticsTracker'; export interface HardwareWalletFlowProps { onCancel: () => void; @@ -58,6 +60,7 @@ export const HardwareWalletFlow = ({ onCancel }: HardwareWalletFlowProps): React const [connection, setConnection] = useState(); const [walletData, setWalletData] = useState(); const { updateEnteredAtTime } = useTimeSpentOnPage(); + const analytics = useAnalyticsContext(); useEffect(() => { updateEnteredAtTime(); @@ -157,6 +160,9 @@ export const HardwareWalletFlow = ({ onCancel }: HardwareWalletFlowProps): React accounts={TOTAL_ACCOUNTS} onBack={() => setIsStartOverDialogVisible(true)} onSubmit={onAccountAndNameSubmit} + onSelectedAccountChange={() => { + void analytics.sendEventToPostHog(postHogOnboardingActions.hw?.SETUP_HW_ACCOUNT_NO_CLICK); + }} /> diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx index e77290b23..d852733eb 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/wallet-setup/components/HardwareWalletFlow/StepConnect.tsx @@ -6,6 +6,8 @@ import { TranslationKey } from '@lib/translations/types'; import { TFunction } from 'i18next'; import React, { useCallback, useEffect, useState, VFC } from 'react'; import { useTranslation } from 'react-i18next'; +import { useAnalyticsContext } from '@providers'; +import { postHogOnboardingActions } from '@providers/AnalyticsProvider/analyticsTracker'; export const isTrezorHWSupported = (): boolean => process.env.USE_TREZOR_HW === 'true'; @@ -92,6 +94,7 @@ export const StepConnect: VFC = ({ onBack, onConnected, onUsbD const [discoveryState, setDiscoveryState] = useState(DiscoveryState.Requested); const [connectionError, setConnectionError] = useState(null); const { connectHardwareWalletRevamped } = useWalletManager(); + const analytics = useAnalyticsContext(); const translations = makeTranslations({ connectionError, t }); const connect = useConnectHardwareWalletWithTimeout(connectHardwareWalletRevamped); @@ -99,7 +102,8 @@ export const StepConnect: VFC = ({ onBack, onConnected, onUsbD const onRetry = useCallback(() => { setDiscoveryState(DiscoveryState.Requested); setConnectionError(null); - }, []); + void analytics.sendEventToPostHog(postHogOnboardingActions.hw?.CONNECT_HW_TRY_AGAIN_CLICK); + }, [analytics]); useEffect(() => { (async () => { @@ -108,10 +112,12 @@ export const StepConnect: VFC = ({ onBack, onConnected, onUsbD setDiscoveryState(DiscoveryState.Running); let connectionResult: Wallet.HardwareWalletConnection; try { + void analytics.sendEventToPostHog(postHogOnboardingActions.hw?.CONNECT_HW_VIEW); const usbDevice = await requestHardwareWalletConnection(); onUsbDeviceChange(usbDevice); connectionResult = await connect(usbDevice); onConnected(connectionResult); + void analytics.sendEventToPostHog(postHogOnboardingActions.hw?.HW_POPUP_CONNECT_CLICK); setDiscoveryState(DiscoveryState.Idle); } catch (error) { setDiscoveryState(DiscoveryState.Idle); @@ -125,7 +131,7 @@ export const StepConnect: VFC = ({ onBack, onConnected, onUsbD setConnectionError(parseConnectionError(error)); } })(); - }, [connect, discoveryState, onUsbDeviceChange, onConnected]); + }, [connect, discoveryState, onUsbDeviceChange, onConnected, analytics]); return ( = ({ connection, onError, walletDa if (status !== CreationState.Idle) return; setStatus(CreationState.Working); - void analytics.sendEventToPostHog(postHogOnboardingActions.hw.SETUP_HW_WALLET_NEXT_CLICK); - let cardanoWallet: Wallet.CardanoWallet; try { cardanoWallet = await createHardwareWalletRevamped({ @@ -64,7 +62,7 @@ export const StepCreate: VFC = ({ connection, onError, walletDa } const deviceSpec = await Wallet.getDeviceSpec(connection); - await analytics.sendEventToPostHog(postHogOnboardingActions.hw.DONE_GO_TO_WALLET, { + void analytics.sendEventToPostHog(postHogOnboardingActions.hw.ENTER_WALLET, { /* eslint-disable camelcase */ $set_once: { initial_hardware_wallet_model: deviceSpec.model, diff --git a/packages/common/src/analytics/types.ts b/packages/common/src/analytics/types.ts index e903e7d1b..d20a6ba0c 100644 --- a/packages/common/src/analytics/types.ts +++ b/packages/common/src/analytics/types.ts @@ -7,14 +7,12 @@ export enum PostHogAction { OnboardingMainViewPinExtensionClick = 'wallet | onboarding | lace main view | pin the wallet extension | click', OnboardingMainViewMultiAddressModalGotItClick = 'wallet | onboarding | lace main view | multi-address modal | got it | click', // Hardware wallet connect - OnboardingHWAnalyticsAgreeClick = 'onboarding | hardware wallet | analytics | agree | click', - OnboardingHWAnalyticsSkipClick = 'onboarding | hardware wallet | analytics | skip | click', - OnboardingHWClick = 'onboarding | hardware wallet | connect | click', - OnboardinHWLaceTermsOfUseNextClick = 'onboarding | hardware wallet | lace terms of use | next | click', - OnboardingHWConnectNextClick = 'onboarding | hardware wallet | connect hw | next | click', - OnboardingHWSelectAccountNextClick = 'onboarding | hardware wallet | select hw account | next | click', - OnboardingHWNameNextClick = 'onboarding | hardware wallet | name hw wallet | next | click', - OnboardingHWDoneGoToWallet = 'onboarding | hardware wallet | all done | go to my wallet | click', + OnboardingHWClick = 'onboarding | hardware wallet revamp | connect | click', + OnboardingHWConnectView = 'onboarding | hardware wallet revamp | connect your device | view', + OnboardingHWPopupConnectClick = 'onboarding | hardware wallet revamp | native browser pop-up with HWs | connect | click', + OnboardingHWConnectTryAgainClick = 'onboarding | hardware wallet revamp | connect your device | try again | click', + OnboardingHWSetupWalletAccountNoClick = "onboarding | hardware wallet revamp | let's set up your wallet | Account No | click", + OnboardingHWEnterWalletClick = "onboarding | hardware wallet revamp | let's set up your wallet | enter wallet | click", // Restore wallet OnboardingRestoreClick = 'onboarding | restore wallet revamp | restore | click', OnboardingRestoreEnterRecoveryPhraseNextClick = 'onboarding | restore wallet revamp | enter your recovery phrase | next | click', diff --git a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx index 157ac9d2a..7b5da5be0 100644 --- a/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx +++ b/packages/core/src/ui/components/WalletSetupRevamp/WalletSetupSelectAccountsStepRevamp.tsx @@ -16,13 +16,15 @@ export interface WalletSetupSelectAccountsStepRevampProps { onBack: () => void; onSubmit: (accountIndex: number, name: string) => void; isNextLoading?: boolean; + onSelectedAccountChange?: () => void; } export const WalletSetupSelectAccountsStepRevamp = ({ accounts, onBack, onSubmit, - isNextLoading + isNextLoading, + onSelectedAccountChange }: WalletSetupSelectAccountsStepRevampProps): React.ReactElement => { const [selectedAccount, setSelectedAccount] = useState('0'); const [walletName, setWalletName] = useState(INITIAL_WALLET_NAME); @@ -72,7 +74,10 @@ export const WalletSetupSelectAccountsStepRevamp = ({ setSelectedAccount(value)} + onValueChange={(value) => { + setSelectedAccount(value); + onSelectedAccountChange?.(); + }} showArrow withOutline selectedValue={selectedAccount} From f9a202c0fa167d4ce7bdb35cefa9c62f64f4f1b0 Mon Sep 17 00:00:00 2001 From: bslabiak <112852128+bslabiak@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:03:26 +0200 Subject: [PATCH 65/74] test(extension): fix smoke tests (#1086) --- .../e2e-tests/src/assert/dAppConnectorAssert.ts | 2 +- .../src/assert/transactionDetailsAssert.ts | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts index 1fab9e989..c67ada570 100644 --- a/packages/e2e-tests/src/assert/dAppConnectorAssert.ts +++ b/packages/e2e-tests/src/assert/dAppConnectorAssert.ts @@ -223,7 +223,7 @@ class DAppConnectorAssert { ); const dAppWalletLovelaceBalance = Math.trunc(Number(await ExampleDAppPage.walletBalance.getText()) / 10_000); - expect(dAppWalletLovelaceBalance).to.be.closeTo(actualWalletLovelaceBalance, 1); + expect(dAppWalletLovelaceBalance).to.be.closeTo(actualWalletLovelaceBalance, 2); expect(await ExampleDAppPage.walletChangeAddress.getText()).to.equal( getTestWallet(TestWalletName.TestAutomationWallet).address diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index a97a63f46..88f661316 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -109,13 +109,8 @@ class TransactionsDetailsAssert { for (let i = 0; i <= rowsNumber && i < 10; i++) { await TransactionsPage.clickOnTransactionRow(i); await TransactionDetailsPage.transactionDetailsDescription.waitForClickable({ timeout: 15_000 }); - await TransactionDetailsPage.transactionDetailsHash.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsStatus.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsTimestamp.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsInputsSection.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsOutputsSection.waitForDisplayed(); const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); - if (!txType.includes(stakeKeyRegistration)) { + if (!txType.includes(stakeKeyRegistration) && !txType.includes('Rewards')) { await TransactionDetailsPage.transactionDetailsFeeADA.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsFeeFiat.waitForDisplayed(); } @@ -124,6 +119,13 @@ class TransactionsDetailsAssert { await TransactionDetailsPage.transactionDetailsStakepoolTicker.waitForDisplayed(); await TransactionDetailsPage.transactionDetailsStakePoolId.waitForDisplayed(); } + if (!txType.includes('Rewards')) { + await TransactionDetailsPage.transactionDetailsTimestamp.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsInputsSection.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsOutputsSection.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsStatus.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsHash.waitForDisplayed(); + } await TransactionDetailsPage.closeActivityDetails(mode); } From 54c213fa3311f6e15171f5027c4494f58b43dff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojtek=20K=C5=82os?= <114915819+wklos-iohk@users.noreply.github.com> Date: Mon, 22 Apr 2024 16:51:20 +0200 Subject: [PATCH 66/74] test(extension): refactor Transaction Bundle class POM (#1081) --- packages/e2e-tests/src/actor/webTester.ts | 36 ---------------- .../src/assert/drawerSendExtendedAssert.ts | 18 ++++---- .../transaction/transactionBundleAssert.ts | 16 +++---- .../newTransaction/transactionBundle.ts | 42 +++++++++---------- .../newTransaction/transactionNewPage.ts | 5 --- .../newTransactionExtendedPageObject.ts | 6 --- .../src/steps/sendTransactionBundleSteps.ts | 3 +- .../src/steps/sendTransactionSimpleSteps.ts | 2 +- 8 files changed, 35 insertions(+), 93 deletions(-) diff --git a/packages/e2e-tests/src/actor/webTester.ts b/packages/e2e-tests/src/actor/webTester.ts index 6a379e3b5..0da22a325 100755 --- a/packages/e2e-tests/src/actor/webTester.ts +++ b/packages/e2e-tests/src/actor/webTester.ts @@ -7,42 +7,6 @@ import { Logger } from '../support/logger'; export type LocatorStrategy = 'css selector' | 'xpath'; export default new (class WebTester { - async seeElement(selector: string, reverseOrder = false, timeoutMs = 3000) { - Logger.log(`Assert see element ${selector}, reverse = ${reverseOrder}`); - const shouldBeFound = reverseOrder ? 'should not be found' : 'should be found'; - await $(selector).waitForDisplayed({ - timeout: timeoutMs, - interval: 500, - reverse: reverseOrder, - timeoutMsg: `element: ${selector} ${shouldBeFound} after: ${timeoutMs}ms` - }); - } - - async seeWebElement(element: WebElement) { - await this.seeElement(element.toJSLocator()); - } - - async clickOnElement(selector: string, locatorStrategy?: LocatorStrategy): Promise { - Logger.log(`Click on ${selector} [strategy=${locatorStrategy ?? 'css selector'}]`); - const element = await $(selector); - await element.waitForDisplayed(); - await element - .waitForClickable({ timeout: 5000 }) - .then(async () => await $(element).click()) - .catch(() => { - throw new Error(`Element ${selector} not clickable`); - }); - } - - async clickElement(element: WebElement, retries?: number): Promise { - if (retries && retries > 0 && (await (await $(element.toJSLocator())).isDisplayed())) { - await browser.pause(500); - await this.clickElement(element, retries - 1); - } else { - await this.clickOnElement(element.toJSLocator(), element.locatorStrategy()); - } - } - async getTextValueFromElement(element: WebElement): Promise { return await this.getTextValue(element.toJSLocator()); } diff --git a/packages/e2e-tests/src/assert/drawerSendExtendedAssert.ts b/packages/e2e-tests/src/assert/drawerSendExtendedAssert.ts index 2e7b03fe4..48aeadf0b 100644 --- a/packages/e2e-tests/src/assert/drawerSendExtendedAssert.ts +++ b/packages/e2e-tests/src/assert/drawerSendExtendedAssert.ts @@ -206,8 +206,7 @@ class DrawerSendExtendedAssert { async assertDefaultInputsDoNotContainValues() { const coinConfigure = new CoinConfigure(); - const addressInput = new AddressInput(); - expect(await addressInput.input.getValue()).to.be.empty; + expect(await new AddressInput().input.getValue()).to.be.empty; expect(await coinConfigure.input.getValue()).to.equal('0.00'); } @@ -304,10 +303,10 @@ class DrawerSendExtendedAssert { expect(await TransactionNewPage.metadataInputField.getValue()).to.be.empty; } - async assertSeeIncorrectAddressError(shouldSee: boolean) { - await new TransactionBundle().bundleAddressInputError.waitForDisplayed({ reverse: !shouldSee }); + async assertSeeIncorrectAddressError(bundleIndex: number, shouldSee: boolean) { + await new TransactionBundle(bundleIndex).bundleAddressInputError.waitForDisplayed({ reverse: !shouldSee }); if (shouldSee) { - expect(await new TransactionBundle().bundleAddressInputError.getText()).to.equal( + expect(await new TransactionBundle(bundleIndex).bundleAddressInputError.getText()).to.equal( await t('general.errors.incorrectAddress') ); } @@ -368,8 +367,7 @@ class DrawerSendExtendedAssert { } async assertSeeIconForInvalidAdaHandle(shouldBeDisplayed: boolean) { - const addressInput = new AddressInput(); - await addressInput.invalidAdaHandleIcon.waitForDisplayed({ reverse: !shouldBeDisplayed }); + await new AddressInput().invalidAdaHandleIcon.waitForDisplayed({ reverse: !shouldBeDisplayed }); } async assertSeeAdaHandleError(shouldBeDisplayed: boolean) { @@ -381,13 +379,11 @@ class DrawerSendExtendedAssert { } async assertSeeSearchLoader(shouldBeDisplayed: boolean) { - const addressInput = new AddressInput(); - await addressInput.searchLoader.waitForDisplayed({ reverse: !shouldBeDisplayed, interval: 50 }); + await new AddressInput().searchLoader.waitForDisplayed({ reverse: !shouldBeDisplayed, interval: 50 }); } async assertAddressBookButtonEnabled(bundleIndex: number, shouldBeEnabled: boolean) { - const addressInput = new AddressInput(bundleIndex); - await addressInput.ctaButton.waitForEnabled({ reverse: !shouldBeEnabled }); + await new AddressInput(bundleIndex).ctaButton.waitForEnabled({ reverse: !shouldBeEnabled }); } async assertSeeReviewAddressBanner(handle: string) { diff --git a/packages/e2e-tests/src/assert/transaction/transactionBundleAssert.ts b/packages/e2e-tests/src/assert/transaction/transactionBundleAssert.ts index df7706ab0..7750423c1 100644 --- a/packages/e2e-tests/src/assert/transaction/transactionBundleAssert.ts +++ b/packages/e2e-tests/src/assert/transaction/transactionBundleAssert.ts @@ -1,5 +1,4 @@ import { expect } from 'chai'; -import webTester from '../../actor/webTester'; import { TransactionBundle } from '../../elements/newTransaction/transactionBundle'; import coinConfigureAssert from '../coinConfigureAssert'; import assetInputAssert from '../assetInputAssert'; @@ -7,19 +6,17 @@ import TransactionNewPage from '../../elements/newTransaction/transactionNewPage import { t } from '../../utils/translationService'; import { CoinConfigure } from '../../elements/newTransaction/coinConfigure'; import { AssetInput } from '../../elements/newTransaction/assetInput'; -import { AddressInput } from '../../elements/AddressInput'; class TransactionBundleAssert { assertSeeBundles = async (expectedNumberOfBundles: number) => { for (let i = 1; i <= expectedNumberOfBundles; i++) { const bundle = new TransactionBundle(i); if (expectedNumberOfBundles > 1) { - expect(await webTester.getTextValueFromElement(bundle.bundleTitle())).to.equal( - `${await t('core.outputSummaryList.output')} ${i}` - ); - await webTester.seeWebElement(bundle.bundleRemoveButton()); + await bundle.bundleTitle.waitForDisplayed(); + expect(await bundle.bundleTitle.getText()).to.equal(`${await t('core.outputSummaryList.output')} ${i}`); + await bundle.bundleRemoveButton.waitForDisplayed(); } - await new AddressInput(i).input.waitForDisplayed(); + await bundle.bundleAddressInput.input.waitForDisplayed(); await coinConfigureAssert.assertSeeCoinConfigure(); await assetInputAssert.assertSeeAssetInput(i); } @@ -27,15 +24,14 @@ class TransactionBundleAssert { async assertSeeTokenNameInBundleAndCoinConfigure(expectedName: string, bundleIndex: number) { await TransactionNewPage.cancelTransactionButton.waitForStable(); - const tokenName = await new TransactionBundle(bundleIndex) - .bundleAssetInput() + const tokenName = await new TransactionBundle(bundleIndex).bundleAssetInput .coinConfigure(bundleIndex, expectedName.replace('...', '')) .nameElement.getText(); expect(tokenName).to.contain(expectedName); } async assertSeeAssetNameAndValueInBundle(expectedName: string, expectedValue: number, bundleIndex: number) { - const asset = new TransactionBundle(bundleIndex).bundleAssetInput().coinConfigure(bundleIndex, expectedName); + const asset = new TransactionBundle(bundleIndex).bundleAssetInput.coinConfigure(bundleIndex, expectedName); const tokenName = await asset.nameElement.getText(); const tokenValue = await asset.getAmount(); diff --git a/packages/e2e-tests/src/elements/newTransaction/transactionBundle.ts b/packages/e2e-tests/src/elements/newTransaction/transactionBundle.ts index e29962602..6e7507a3e 100644 --- a/packages/e2e-tests/src/elements/newTransaction/transactionBundle.ts +++ b/packages/e2e-tests/src/elements/newTransaction/transactionBundle.ts @@ -1,50 +1,46 @@ /* eslint-disable no-undef */ -import { LocatorStrategy } from '../../actor/webTester'; -import { WebElement, WebElementFactory as Factory } from '../webElement'; import { AddressInput } from '../AddressInput'; import { AssetInput } from './assetInput'; import { ChainablePromiseElement } from 'webdriverio'; -export class TransactionBundle extends WebElement { +export class TransactionBundle { protected CONTAINER = '//div[@data-testid="asset-bundle-container"]'; private BUNDLE_TITLE = '//h5[@data-testid="asset-bundle-title"]'; private BUNDLE_REMOVE_BUTTON = '//button[@data-testid="asset-bundle-remove-button"]'; private ADDRESS_INPUT_ERROR = '[data-testid="address-input-error"]'; + readonly index: number = 1; - constructor(index?: number) { - super(); - this.CONTAINER = typeof index === 'undefined' ? this.CONTAINER : `(${this.CONTAINER})[${index}]`; + constructor(index = 1) { + this.index = index; + this.CONTAINER = `(${this.CONTAINER})[${index}]`; } - container(): WebElement { - return Factory.fromSelector(`${this.CONTAINER}`, 'xpath'); + get container(): ChainablePromiseElement { + return $(this.CONTAINER); } - bundleTitle(): WebElement { - return Factory.fromSelector(`${this.CONTAINER}${this.BUNDLE_TITLE}`, 'xpath'); + get bundleTitle(): ChainablePromiseElement { + return $(`${this.CONTAINER}${this.BUNDLE_TITLE}`); } - bundleAddressInput(): AddressInput { - return new AddressInput(); + get bundleAddressInput(): AddressInput { + return new AddressInput(this.index); } get bundleAddressInputError(): ChainablePromiseElement { return $(this.CONTAINER).$(this.ADDRESS_INPUT_ERROR); } - bundleAssetInput(): AssetInput { - return new AssetInput(); + get bundleAssetInput(): AssetInput { + return new AssetInput(this.index); } - bundleRemoveButton(): WebElement { - return Factory.fromSelector(`${this.CONTAINER}${this.BUNDLE_REMOVE_BUTTON}`, 'xpath'); + get bundleRemoveButton(): ChainablePromiseElement { + return $(`${this.CONTAINER}${this.BUNDLE_REMOVE_BUTTON}`); } - toJSLocator(): string { - return this.CONTAINER; - } - - locatorStrategy(): LocatorStrategy { - return 'xpath'; - } + clickRemoveBundleButton = async (): Promise => { + await this.bundleRemoveButton.waitForClickable(); + await this.bundleRemoveButton.click(); + }; } diff --git a/packages/e2e-tests/src/elements/newTransaction/transactionNewPage.ts b/packages/e2e-tests/src/elements/newTransaction/transactionNewPage.ts index 621358129..41ee89dc1 100644 --- a/packages/e2e-tests/src/elements/newTransaction/transactionNewPage.ts +++ b/packages/e2e-tests/src/elements/newTransaction/transactionNewPage.ts @@ -1,7 +1,6 @@ /* eslint-disable no-undef */ import { CoinConfigure } from './coinConfigure'; import { AddressInput } from '../AddressInput'; -import { TransactionBundle } from './transactionBundle'; import { Asset } from '../../data/Asset'; import { ChainablePromiseElement } from 'webdriverio'; import Banner from '../banner'; @@ -40,10 +39,6 @@ class TransactionNewPage extends CommonDrawerElements { return new CoinConfigure(bundleIndex, assetName); } - transactionBundle(index?: number): TransactionBundle { - return new TransactionBundle(index); - } - get title() { return this.drawerNavigationTitle; } diff --git a/packages/e2e-tests/src/pageobject/newTransactionExtendedPageObject.ts b/packages/e2e-tests/src/pageobject/newTransactionExtendedPageObject.ts index b2fcc189b..8c5799a69 100644 --- a/packages/e2e-tests/src/pageobject/newTransactionExtendedPageObject.ts +++ b/packages/e2e-tests/src/pageobject/newTransactionExtendedPageObject.ts @@ -1,6 +1,4 @@ -import webTester from '../actor/webTester'; import TransactionNewPage from '../elements/newTransaction/transactionNewPage'; -import { TransactionBundle } from '../elements/newTransaction/transactionBundle'; import TokenSelectionPage from '../elements/newTransaction/tokenSelectionPage'; import { Asset } from '../data/Asset'; import extensionUtils from '../utils/utils'; @@ -9,10 +7,6 @@ import { AssetInput } from '../elements/newTransaction/assetInput'; import { AddressInput } from '../elements/AddressInput'; export default new (class NewTransactionExtendedPageObject { - clickRemoveBundleButton = async (outputIndex: number) => { - await webTester.clickElement(new TransactionBundle(outputIndex).bundleRemoveButton()); - }; - async setTwoAssetsForBundle(bundleIndex: number, assetValue1: number, assetValue2: number) { await new AddressInput(bundleIndex).fillAddress(byron.getAddress()); await new AssetInput(bundleIndex).clickAddAssetButton(); diff --git a/packages/e2e-tests/src/steps/sendTransactionBundleSteps.ts b/packages/e2e-tests/src/steps/sendTransactionBundleSteps.ts index bbce36c69..945fe724f 100644 --- a/packages/e2e-tests/src/steps/sendTransactionBundleSteps.ts +++ b/packages/e2e-tests/src/steps/sendTransactionBundleSteps.ts @@ -11,13 +11,14 @@ import TransactionNewPage from '../elements/newTransaction/transactionNewPage'; import { AssetInput } from '../elements/newTransaction/assetInput'; import { AddressInput } from '../elements/AddressInput'; import TransactionSubmittedPage from '../elements/newTransaction/transactionSubmittedPage'; +import { TransactionBundle } from '../elements/newTransaction/transactionBundle'; Then(/^I see (\d) bundle rows$/, async (expectedNumberOfBundles: number) => { await transactionBundlesAssert.assertSeeBundles(expectedNumberOfBundles); }); When(/^I remove bundle (\d)$/, async (index: number) => { - await transactionExtendedPageObject.clickRemoveBundleButton(index); + await new TransactionBundle(index).clickRemoveBundleButton(); }); When(/^I set multiple outputs for advanced transaction$/, async () => { diff --git a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts index 15a03e6da..32c1374c6 100644 --- a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts +++ b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts @@ -534,7 +534,7 @@ Then(/^Metadata input is empty$/, async () => { }); Then(/^"Incorrect address" error (is|is not) displayed under address input field$/, async (state: 'is' | 'is not') => { - await drawerSendExtendedAssert.assertSeeIncorrectAddressError(state === 'is'); + await drawerSendExtendedAssert.assertSeeIncorrectAddressError(1, state === 'is'); }); Then(/^"Review transaction" button is (enabled|disabled) on "Send" page$/, async (state: 'enabled' | 'disabled') => { From 4356326cc95d69b9869378f13b96adecd0eece31 Mon Sep 17 00:00:00 2001 From: bslabiak <112852128+bslabiak@users.noreply.github.com> Date: Mon, 22 Apr 2024 17:22:27 +0200 Subject: [PATCH 67/74] test(extension): fix tx details after adding foreign tag (#1087) --- packages/e2e-tests/src/assert/transactionDetailsAssert.ts | 6 ++++++ packages/e2e-tests/src/elements/transactionDetails.ts | 7 ++++++- packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts | 6 +++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index 88f661316..098b1993c 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -21,6 +21,7 @@ export type PoolData = { export type TransactionData = { address: string; + addressTag?: string; ada: string; assets?: string[]; }; @@ -73,6 +74,11 @@ class TransactionsDetailsAssert { expect(expectedAddress.startsWith(actualAddressSplit[0])).to.be.true; expect(expectedAddress.endsWith(actualAddressSplit[1])).to.be.true; } + + if (expectedActivityDetails.transactionData[i].addressTag) { + const actualAddressTag = await TransactionDetailsPage.transactionDetailsToAddressTag(i).getText(); + expect(expectedActivityDetails.transactionData[i].addressTag).to(actualAddressTag); + } } } diff --git a/packages/e2e-tests/src/elements/transactionDetails.ts b/packages/e2e-tests/src/elements/transactionDetails.ts index 9ed70b419..f6644090c 100644 --- a/packages/e2e-tests/src/elements/transactionDetails.ts +++ b/packages/e2e-tests/src/elements/transactionDetails.ts @@ -15,7 +15,8 @@ class ActivityDetailsPage extends CommonDrawerElements { private TRANSACTION_DETAILS_SENT_TOKEN = '[data-testid="tx-sent-detail-token"]'; private TRANSACTION_DETAILS_SENT_ADA = '[data-testid="tx-sent-detail-ada"]'; private TRANSACTION_DETAILS_SENT_FIAT = '[data-testid="tx-sent-detail-fiat"]'; - private TRANSACTION_DETAILS_TO_ADDRESS = '[data-testid="tx-to-detail"]'; + private TRANSACTION_DETAILS_TO_ADDRESS = '[data-testid="tx-to-detail"] span'; + private TRANSACTION_DETAILS_TO_ADDRESS_TAG = '[data-testid="tx-to-detail"] [data-testid="address-tag"]'; private TRANSACTION_DETAILS_STATUS = '[data-testid="tx-status"]'; private TRANSACTION_DETAILS_TIMESTAMP = '[data-testid="tx-timestamp"]'; private TRANSACTION_DETAILS_FEE_ADA = '[data-testid="tx-amount-fee-amount"]'; @@ -86,6 +87,10 @@ class ActivityDetailsPage extends CommonDrawerElements { return $$(this.TRANSACTION_DETAILS_BUNDLE)[index].$(this.TRANSACTION_DETAILS_TO_ADDRESS); } + transactionDetailsToAddressTag(index = 0): ChainablePromiseElement { + return $$(this.TRANSACTION_DETAILS_BUNDLE)[index].$(this.TRANSACTION_DETAILS_TO_ADDRESS_TAG); + } + get transactionDetailsStatus(): ChainablePromiseElement { return $(this.TRANSACTION_DETAILS_STATUS); } diff --git a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts index 32c1374c6..42e3f7ca2 100644 --- a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts +++ b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts @@ -389,7 +389,11 @@ Then( transactionDescription: `${await t(type)}\n(1)`, hash: testContext.load('txHashValue'), transactionData: [ - { ada: `${adaValue} ${Asset.CARDANO.ticker}`, address: String(getTestWallet(walletName).address) } + { + ada: `${adaValue} ${Asset.CARDANO.ticker}`, + address: String(getTestWallet(walletName).address), + addressTag: 'foreign' + } ], status: 'Success' }; From 9149f67a6eea97270b8d860a60c76b37e0db0ece Mon Sep 17 00:00:00 2001 From: Janusz Janus Date: Mon, 22 Apr 2024 23:04:59 +0200 Subject: [PATCH 68/74] test(extension): fetch token prices tests (#1077) --- .../e2e-tests/src/assert/tokensPageAssert.ts | 28 +++++++++++ packages/e2e-tests/src/elements/tokensPage.ts | 10 ++++ .../SendTransactionBundlesExtended.feature | 2 +- ...endTransactionSimpleExtended.part3.feature | 2 +- .../SendTransactionSimplePopup.part3.feature | 2 +- .../src/features/TokensPageExtended.feature | 48 +++++++++++++++++-- .../src/features/TokensPagePopup.feature | 47 +++++++++++++++++- .../StakingSwitchingPoolsExtendedE2E.feature | 2 +- packages/e2e-tests/src/steps/commonSteps.ts | 36 ++++++++++---- .../e2e-tests/src/steps/tokensPageSteps.ts | 16 ++++++- .../e2e-tests/src/utils/browserStorage.ts | 26 ++++++++++ .../e2e-tests/src/utils/networkManager.ts | 29 +++++++++-- 12 files changed, 226 insertions(+), 22 deletions(-) diff --git a/packages/e2e-tests/src/assert/tokensPageAssert.ts b/packages/e2e-tests/src/assert/tokensPageAssert.ts index c03d4466b..d1dcf7108 100644 --- a/packages/e2e-tests/src/assert/tokensPageAssert.ts +++ b/packages/e2e-tests/src/assert/tokensPageAssert.ts @@ -15,6 +15,8 @@ type ExpectedTokenDetails = { }; class TokensPageAssert { + ADA_PRICE_CHECK_INTERVAL = 65_000; + assertSeeTitle = async () => { await TokensPage.title.waitForDisplayed({ timeout: 10_000 }); }; @@ -252,6 +254,32 @@ class TokensPageAssert { expect(tickerDisplayed).to.equal(expectedTicker); } + + async seePriceFetchExpiredErrorMessage(shouldBeVisible: boolean) { + await TokensPage.priceFetchErrorDescription.waitForDisplayed({ + reverse: !shouldBeVisible, + timeout: this.ADA_PRICE_CHECK_INTERVAL * 3 + }); + if (shouldBeVisible) { + const expiredErrorMessageToMatch = (await t('general.warnings.priceDataExpired')).split(':')[0]; + expect(await TokensPage.priceFetchErrorDescription.getText()) + .to.include(expiredErrorMessageToMatch) + .to.include(new Date().getFullYear()) + .to.include(new Date().getDate()) + .to.include(new Date().getMinutes()); + } + } + + async seePriceFetchFailedErrorMessage(shouldBeVisible: boolean) { + await TokensPage.priceFetchErrorDescription.waitForDisplayed({ + reverse: !shouldBeVisible, + timeout: this.ADA_PRICE_CHECK_INTERVAL * 3 + }); + if (shouldBeVisible) + expect(await TokensPage.priceFetchErrorDescription.getText()).to.equal( + await t('general.warnings.cannotFetchPrice') + ); + } } export default new TokensPageAssert(); diff --git a/packages/e2e-tests/src/elements/tokensPage.ts b/packages/e2e-tests/src/elements/tokensPage.ts index f992e9347..4282d8759 100644 --- a/packages/e2e-tests/src/elements/tokensPage.ts +++ b/packages/e2e-tests/src/elements/tokensPage.ts @@ -2,6 +2,7 @@ import SectionTitle from './sectionTitle'; import { ChainablePromiseElement } from 'webdriverio'; import { ChainablePromiseArray } from 'webdriverio/build/types'; +import TokensPageAssert from '../assert/tokensPageAssert'; class TokensPage { private BALANCE_LABEL = '[data-testid="portfolio-balance-label"]'; @@ -21,6 +22,7 @@ class TokensPage { private OPENED_EYE_ICON = '[data-testid="opened-eye-icon"]'; private VIEW_ALL_BUTTON = '[data-testid="view-all-button"]'; private TOKEN_ROW_SKELETON = '.ant-skeleton'; + private PRICE_FETCH_ERROR_DESCRIPTION = '[data-testid="banner-description"]'; get sendButtonPopupMode(): ChainablePromiseElement { return $(this.SEND_BUTTON_POPUP_MODE); @@ -103,6 +105,10 @@ class TokensPage { return $(this.TOKEN_ROW_SKELETON); } + get priceFetchErrorDescription(): ChainablePromiseElement { + return $(this.PRICE_FETCH_ERROR_DESCRIPTION); + } + async getRows(): Promise { return $$(this.TOKENS_TABLE_ROW); } @@ -143,6 +149,10 @@ class TokensPage { async getTokensCounterAsNumber(): Promise { return SectionTitle.getCounterAsNumber(); } + + async waitForPricesToBeFetched() { + await this.totalBalanceValue.waitForDisplayed({ timeout: TokensPageAssert.ADA_PRICE_CHECK_INTERVAL }); + } } export default new TokensPage(); diff --git a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature index 105fff29e..b6f787e3f 100644 --- a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature +++ b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature @@ -265,7 +265,7 @@ Feature: Send - Extended Browser View (Advanced Tx) @LW-3578 Scenario: Extended-view - Transaction error screen displayed for multiple bundles on transaction submit error - Given I enable network interception to fail request: "*/tx-submit/submit" with error 400 + Given I enable network interception to finish request: "*/tx-submit/submit" with error 400 And I click "Send" button on page header And I set 2 bundles with the same assets And I click "Review transaction" button on "Send" page diff --git a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part3.feature b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part3.feature index f3d9ffdf7..efd300400 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part3.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part3.feature @@ -125,7 +125,7 @@ Feature: LW-484: Send & Receive - Extended Browser View (Simple Tx) @LW-2374 @Testnet Scenario: Extended-view - Transaction error screen displayed on transaction submit error - Given I enable network interception to fail request: "*/tx-submit/submit" with error 400 + Given I enable network interception to finish request: "*/tx-submit/submit" with error 400 And I click "Send" button on page header And I’ve entered accepted values for all fields of simple Tx And I click "Review transaction" button on "Send" page diff --git a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part3.feature b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part3.feature index 88910b51c..3d905a2b2 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part3.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part3.feature @@ -103,7 +103,7 @@ Feature: LW-484: Send & Receive - Popup View (Simple Tx) @LW-2408 @Testnet Scenario: Popup-view - Transaction error screen displayed on transaction submit error - Given I enable network interception to fail request: "*/tx-submit/submit" with error 400 + Given I enable network interception to finish request: "*/tx-submit/submit" with error 400 And I click "Send" button on Tokens page in popup mode And I’ve entered accepted values for all fields of simple Tx And I click "Review transaction" button on "Send" page diff --git a/packages/e2e-tests/src/features/TokensPageExtended.feature b/packages/e2e-tests/src/features/TokensPageExtended.feature index c59877f53..2cfceeb12 100644 --- a/packages/e2e-tests/src/features/TokensPageExtended.feature +++ b/packages/e2e-tests/src/features/TokensPageExtended.feature @@ -108,7 +108,7 @@ Feature: LW: Tokens tab - extended view And I see total wallet balance in USD And balance and FIAT balance for each token are visible - @Testnet @Mainnet @LW-7121 @LW-7123 + @LW-7121 @LW-7123 @Testnet @Mainnet Scenario Outline: Extended View - Hide my balance - keep state after the page When I click closed eye icon on Tokens page Then opened eye icon is displayed on Tokens page @@ -124,8 +124,7 @@ Feature: LW: Tokens tab - extended view # LW-7706 # | reopening | I reopen the page | - - @Testnet @Mainnet @LW-7125 + @LW-7125 @Testnet @Mainnet Scenario: Extended view - Hide my balance - keep state after switching to popup view When I click closed eye icon on Tokens page Then opened eye icon is displayed on Tokens page @@ -135,3 +134,46 @@ Feature: LW: Tokens tab - extended view Then opened eye icon is displayed on Tokens page And total wallet balance is masked with asterisks And balance and FIAT balance for each token are masked with asterisks + + @LW-6889 @Testnet @Mainnet + Scenario: Extended view - Token pricing - Price fetch expired error is displayed when coingecko request fails + Given ADA fiat price has been fetched + When I enable network interception to fail request: "https://coingecko.*" + And I shift back last fiat price fetch time in local storage by 500 seconds + Then "Price data expired" error is displayed + When I disable network interception + Then ADA fiat price has been fetched + And "Price data expired" error is not displayed + + @LW-10283 @Testnet @Mainnet @Pending + @issue=LW-10296 + Scenario: Extended view - Token pricing - Price fetch expired error is displayed when coingecko request returns 500 + Given ADA fiat price has been fetched + When I enable network interception to finish request: "https://coingecko.*" with error 500 + And I shift back last fiat price fetch time in local storage by 500 seconds + Then "Price data expired" error is displayed + When I disable network interception + Then ADA fiat price has been fetched + And "Price data expired" error is not displayed + + @LW-6890 @Testnet @Mainnet @Pending + @issue=LW-10296 + Scenario: Extended view - Token pricing - Fiat price unable to fetch error is displayed on failed request + Given ADA fiat price has been fetched + When I enable network interception to fail request: "https://coingecko.*" + And I delete fiat price timestamp from background storage + Then "Unable to fetch fiat values" error is displayed + When I disable network interception + Then ADA fiat price has been fetched + Then "Unable to fetch fiat values" error is not displayed + + @LW-6681 @Testnet @Mainnet @Pending + @issue=LW-10296 + Scenario: Extended view - Token pricing - Fiat price unable to fetch error is displayed when coingecko request returns 500 + Given ADA fiat price has been fetched + When I enable network interception to finish request: "https://coingecko.*" with error 500 + And I delete fiat price timestamp from background storage + Then "Unable to fetch fiat values" error is displayed + And I disable network interception + Then ADA fiat price has been fetched + Then "Unable to fetch fiat values" error is not displayed diff --git a/packages/e2e-tests/src/features/TokensPagePopup.feature b/packages/e2e-tests/src/features/TokensPagePopup.feature index fc0214081..329f6b514 100644 --- a/packages/e2e-tests/src/features/TokensPagePopup.feature +++ b/packages/e2e-tests/src/features/TokensPagePopup.feature @@ -78,7 +78,7 @@ Feature: LW: Tokens tab - popup view And I see total wallet balance in USD And balance and FIAT balance for each token are visible - @Testnet @Mainnet @LW-7122 @LW-7124 + @LW-7122 @LW-7124 @Testnet @Mainnet Scenario Outline: Popup View - Hide my balance - keep state after the page When I click closed eye icon on Tokens page Then opened eye icon is displayed on Tokens page @@ -94,7 +94,7 @@ Feature: LW: Tokens tab - popup view # LW-7706 # | reopening | I reopen the page | - @Testnet @Mainnet @LW-7126 + @LW-7126 @Testnet @Mainnet Scenario: Popup View - Hide my balance - keep state after switching to extended view When I click closed eye icon on Tokens page Then opened eye icon is displayed on Tokens page @@ -104,3 +104,46 @@ Feature: LW: Tokens tab - popup view Then opened eye icon is displayed on Tokens page And total wallet balance is masked with asterisks And balance and FIAT balance for each token are masked with asterisks + + @LW-6684 @Testnet @Mainnet + Scenario: Popup view - Token pricing - Price fetch expired error is displayed when coingecko request fails + Given ADA fiat price has been fetched + When I enable network interception to fail request: "https://coingecko.*" + And I shift back last fiat price fetch time in local storage by 500 seconds + Then "Price data expired" error is displayed + When I disable network interception + Then ADA fiat price has been fetched + And "Price data expired" error is not displayed + + @LW-10307 @Testnet @Mainnet @Pending + @issue=LW-10296 + Scenario: Popup view - Token pricing - Price fetch expired error is displayed when coingecko request returns 500 + Given ADA fiat price has been fetched + When I enable network interception to finish request: "https://coingecko.*" with error 500 + And I shift back last fiat price fetch time in local storage by 500 seconds + Then "Price data expired" error is displayed + When I disable network interception + Then ADA fiat price has been fetched + And "Price data expired" error is not displayed + + @LW-6682 @Testnet @Mainnet @Pending + @issue=LW-10296 + Scenario: Popup view - Token pricing - Fiat price unable to fetch error is displayed on failed request + Given ADA fiat price has been fetched + When I enable network interception to fail request: "https://coingecko.*" + And I delete fiat price timestamp from background storage + Then "Unable to fetch fiat values" error is displayed + When I disable network interception + Then ADA fiat price has been fetched + Then "Unable to fetch fiat values" error is not displayed + + @LW-6683 @Testnet @Mainnet @Pending + @issue=LW-10296 + Scenario: Popup view - Token pricing - Fiat price unable to fetch error is displayed when coingecko request returns 500 + Given ADA fiat price has been fetched + When I enable network interception to finish request: "https://coingecko.*" with error 500 + And I delete fiat price timestamp from background storage + Then "Unable to fetch fiat values" error is displayed + And I disable network interception + Then ADA fiat price has been fetched + Then "Unable to fetch fiat values" error is not displayed diff --git a/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature b/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature index 8b3c3bb02..8500b56a4 100644 --- a/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature +++ b/packages/e2e-tests/src/features/e2e/StakingSwitchingPoolsExtendedE2E.feature @@ -53,7 +53,7 @@ Feature: Staking Page - Switching pools - Extended Browser View - E2E @LW-4558 @Testnet Scenario: Extended View - Staking - Staking error screen displayed on transaction submit error - Given I enable network interception to fail request: "*/tx-submit/submit" with error 400 + Given I enable network interception to finish request: "*/tx-submit/submit" with error 400 When I navigate to Staking extended page Then I see currently staking stake pool in extended mode and choose new pool as "OtherStakePool" When I input "OtherStakePool" to the search bar diff --git a/packages/e2e-tests/src/steps/commonSteps.ts b/packages/e2e-tests/src/steps/commonSteps.ts index c800155d6..993f50c14 100755 --- a/packages/e2e-tests/src/steps/commonSteps.ts +++ b/packages/e2e-tests/src/steps/commonSteps.ts @@ -12,7 +12,11 @@ import localStorageManager from '../utils/localStorageManager'; import networkManager from '../utils/networkManager'; import { Logger } from '../support/logger'; import clipboard from 'clipboardy'; -import { cleanBrowserStorage } from '../utils/browserStorage'; +import { + shiftBackFiatPriceFetchedTimeInBrowserStorage, + cleanBrowserStorage, + deleteFiatPriceTimestampFromBackgroundStorage +} from '../utils/browserStorage'; import BackgroundStorageAssert from '../assert/backgroundStorageAssert'; import topNavigationAssert from '../assert/topNavigationAssert'; import testContext from '../utils/testContext'; @@ -201,13 +205,6 @@ When(/^I am in the offline network mode: (true|false)$/, async (offline: 'true' await networkManager.changeNetworkCapabilitiesOfBrowser(offline === 'true'); }); -When( - /^I enable network interception to fail request: "([^"]*)" with error (\d*)$/, - async (urlPattern: string, errorCode: number) => { - await networkManager.failResponse(urlPattern, errorCode); - } -); - When(/^I click outside the drawer$/, async () => { await new CommonDrawerElements().areaOutsideDrawer.click(); }); @@ -364,3 +361,26 @@ When(/^I scroll (down|up) (\d*) pixels$/, async (direction: 'down' | 'up', pixel Given(/^I confirm multi-address discovery modal$/, async () => { await settingsExtendedPageObject.multiAddressModalConfirm(); }); + +When(/^I enable network interception to fail request: "([^"]*)"$/, async (urlPattern: string) => { + await networkManager.failRequest(urlPattern); +}); + +When( + /^I enable network interception to finish request: "([^"]*)" with error (\d*)$/, + async (urlPattern: string, errorCode: number) => { + await networkManager.finishWithResponseCode(urlPattern, errorCode); + } +); + +Given(/^I shift back last fiat price fetch time in local storage by (\d+) seconds$/, async (seconds: number) => { + await shiftBackFiatPriceFetchedTimeInBrowserStorage(seconds); +}); + +Then(/^I disable network interception$/, async () => { + await networkManager.closeOpenedCdpSessions(); +}); + +Given(/^I delete fiat price timestamp from background storage$/, async () => { + await deleteFiatPriceTimestampFromBackgroundStorage(); +}); diff --git a/packages/e2e-tests/src/steps/tokensPageSteps.ts b/packages/e2e-tests/src/steps/tokensPageSteps.ts index a6ec0b68d..a23ffc713 100644 --- a/packages/e2e-tests/src/steps/tokensPageSteps.ts +++ b/packages/e2e-tests/src/steps/tokensPageSteps.ts @@ -1,4 +1,4 @@ -import { When, Then } from '@cucumber/cucumber'; +import { Then, When } from '@cucumber/cucumber'; import tokensPageAssert from '../assert/tokensPageAssert'; import tokensPageObject from '../pageobject/tokensPageObject'; import tokenDetailsAssert from '../assert/tokenDetailsAssert'; @@ -9,6 +9,7 @@ import { switchToLastWindow } from '../utils/window'; import extensionUtils from '../utils/utils'; import TokensPage from '../elements/tokensPage'; import type { NetworkType } from '../types/network'; +import { Given } from '@wdio/cucumber-framework'; When(/^I see Tokens counter with total number of tokens displayed$/, async () => { await tokensPageAssert.assertSeeTitleWithCounter(); @@ -217,3 +218,16 @@ Then(/^I see total wallet balance in ADA is "([^"]*)"$/, async (balanceInAda: nu Then(/^I see tMin token with the ADA balance of "([^"]*)"$/, async (balanceInAda: number) => { await tokensPageAssert.assertTMinBalance(balanceInAda); }); + +Then( + /^"(Price data expired|Unable to fetch fiat values)" error (is|is not) displayed$/, + async (errorType: 'Price data expired' | 'Unable to fetch fiat values', shouldBeDisplayed: 'is' | 'is not') => { + errorType === 'Price data expired' + ? await tokensPageAssert.seePriceFetchExpiredErrorMessage(shouldBeDisplayed === 'is') + : await tokensPageAssert.seePriceFetchFailedErrorMessage(shouldBeDisplayed === 'is'); + } +); + +Given(/^ADA fiat price has been fetched$/, async () => { + await TokensPage.waitForPricesToBeFetched(); +}); diff --git a/packages/e2e-tests/src/utils/browserStorage.ts b/packages/e2e-tests/src/utils/browserStorage.ts index 78e01cced..429ce781b 100644 --- a/packages/e2e-tests/src/utils/browserStorage.ts +++ b/packages/e2e-tests/src/utils/browserStorage.ts @@ -71,3 +71,29 @@ export const clearBackgroundStorageKey: any = async (): Promise => { Logger.warn(`Clearing background storage key failed: ${error}`); } }; + +export const shiftBackFiatPriceFetchedTimeInBrowserStorage = async (seconds: number): Promise => { + const backgroundStorage = await getBackgroundStorage(); + backgroundStorage.fiatPrices.timestamp -= seconds * 1000; + try { + await browser.execute( + `await chrome.storage.local.set({ BACKGROUND_STORAGE: ${JSON.stringify(backgroundStorage)}})`, + [] + ); + } catch (error) { + throw new Error(`Setting browser storage failed: ${error}`); + } +}; + +export const deleteFiatPriceTimestampFromBackgroundStorage = async (): Promise => { + const backgroundStorage = await getBackgroundStorage(); + delete backgroundStorage.fiatPrices.timestamp; + try { + await browser.execute( + `await chrome.storage.local.set({ BACKGROUND_STORAGE: ${JSON.stringify(backgroundStorage)}})`, + [] + ); + } catch (error) { + throw new Error(`Setting browser storage failed: ${error}`); + } +}; diff --git a/packages/e2e-tests/src/utils/networkManager.ts b/packages/e2e-tests/src/utils/networkManager.ts index 18c96a2f3..42d2869be 100644 --- a/packages/e2e-tests/src/utils/networkManager.ts +++ b/packages/e2e-tests/src/utils/networkManager.ts @@ -62,14 +62,12 @@ export class NetworkManager { await browser.pause(2000); }; - failResponse = async (urlPattern: string, responseCode: number): Promise => { + finishWithResponseCode = async (urlPattern: string, responseCode: number): Promise => { await browser.call(async () => { const puppeteer = await browser.getPuppeteer(); const targets = puppeteer .targets() - .filter( - (target) => target.type() === 'page' || target.type() === 'service_worker' || target.type() === 'other' - ); + .filter((target) => ['page', 'service_worker', 'other'].includes(target.type())); targets.map(async (target) => { const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession; NetworkManager.cdpSessions.push(client); @@ -88,6 +86,29 @@ export class NetworkManager { }); }; + failRequest = async (urlPattern: string): Promise => { + await browser.call(async () => { + const puppeteer = await browser.getPuppeteer(); + const targets = puppeteer + .targets() + .filter((target) => ['page', 'service_worker', 'other'].includes(target.type())); + targets.map(async (target) => { + const client: CDPSession = (await target.createCDPSession()) as unknown as CDPSession; + NetworkManager.cdpSessions.push(client); + await client.send('Fetch.enable', { + patterns: [{ urlPattern }] + }); + client.on('Fetch.requestPaused', async ({ requestId, request }) => { + Logger.log(`found request: ${request.url}, failing request`); + await client.send('Fetch.failRequest', { + requestId, + errorReason: 'Failed' + }); + }); + }); + }); + }; + logFailedRequests = async (): Promise => { await browser.call(async () => { const puppeteer = await browser.getPuppeteer(); From 44c51cb1741939a0c5fefaf9c6790a75653e68e5 Mon Sep 17 00:00:00 2001 From: Piotr Czeglik Date: Tue, 23 Apr 2024 08:51:41 +0200 Subject: [PATCH 69/74] chore: unify cleanup command (#999) --- packages/cardano/package.json | 2 +- packages/common/package.json | 2 +- packages/core/package.json | 2 +- packages/e2e-tests/package.json | 2 +- packages/icons/package.json | 1 + packages/ui/package.json | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/cardano/package.json b/packages/cardano/package.json index d3dffaf30..4d9f04903 100644 --- a/packages/cardano/package.json +++ b/packages/cardano/package.json @@ -27,7 +27,7 @@ ], "scripts": { "build": "run -T rollup -c rollup.config.js", - "cleanup": "yarn exec rm -rf dist node_modules .rollup.cache src/dist", + "cleanup": "yarn exec rm -rf dist node_modules", "lint": "cd ../.. && yarn cardano:lint", "prepack": "yarn build", "prepare": "ts-patch install -s", diff --git a/packages/common/package.json b/packages/common/package.json index 8f7a9b4d5..80879fc98 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -27,7 +27,7 @@ ], "scripts": { "build": "run -T rollup -c rollup.config.js", - "cleanup": "yarn exec rm -rf dist node_modules .rollup.cache src/dist", + "cleanup": "yarn exec rm -rf dist node_modules", "lint": "cd ../.. && yarn common:lint", "prepack": "yarn build", "prepare": "ts-patch install -s", diff --git a/packages/core/package.json b/packages/core/package.json index 7e7ab54c8..5ba6dc705 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,7 +28,7 @@ "scripts": { "build": "run -T rollup -c rollup.config.js", "build-storybook": "NODE_OPTIONS=--openssl-legacy-provider; build-storybook", - "cleanup": "yarn exec rm -rf dist node_modules .rollup.cache src/dist", + "cleanup": "yarn exec rm -rf dist node_modules", "lint": "cd ../.. && yarn core:lint", "prepack": "yarn build", "prestart": "yarn build", diff --git a/packages/e2e-tests/package.json b/packages/e2e-tests/package.json index bd6542460..d1fb7a814 100755 --- a/packages/e2e-tests/package.json +++ b/packages/e2e-tests/package.json @@ -8,7 +8,7 @@ "test": "yarn exec echo 'User test:local'", "test:local:chrome": "../../node_modules/.bin/wdio run wdio.conf.chrome.ts", "test:local:edge": "../../node_modules/.bin/wdio run wdio.conf.edge.ts", - "cleanup": "rm -rf node_modules logs reports screenshots", + "cleanup": "rm -rf logs node_modules reports screenshots", "lint": "run -T eslint -c .eslintrc.js .", "lint:fix": "run -T eslint -c .eslintrc.js . --fix", "pack-chrome-extension": "node ./src/utils/packExtension.ts", diff --git a/packages/icons/package.json b/packages/icons/package.json index 9275e7bd2..9ba11716d 100644 --- a/packages/icons/package.json +++ b/packages/icons/package.json @@ -21,6 +21,7 @@ "build:cleanup": "rm -rf ./tmp", "build:svgr": "svgr ./raw --out-dir ./tmp --typescript", "build:tsc": "tsc --project tsconfig.json", + "cleanup": "yarn exec rm -rf dist node_modules", "test": "echo \"@lace/icons: no test specified\"" }, "devDependencies": { diff --git a/packages/ui/package.json b/packages/ui/package.json index 986326574..762a42f14 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -18,7 +18,7 @@ "scripts": { "build": "run -T rollup -c rollup.config.js", "build-storybook": "NODE_OPTIONS=--openssl-legacy-provider; build-storybook", - "cleanup": "yarn exec rm -rf node_modules", + "cleanup": "yarn exec rm -rf dist node_modules", "format": "yarn prettier --write '**/*.tsx'", "lint": "eslint .", "storybook": "NODE_OPTIONS=--openssl-legacy-provider; start-storybook -p 6006", From ccdec2ae21146068f100bbdbb27a44917457300d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szymon=20Mas=C5=82owski?= Date: Tue, 23 Apr 2024 11:19:40 +0200 Subject: [PATCH 70/74] fix(staking): LW-10324 fix incorrect label in stake confirmation footer (#1088) --- .../Drawer/confirmation/StakePoolConfirmationFooter.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx b/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx index 90ac80e91..af7ddf04f 100644 --- a/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx +++ b/packages/staking/src/features/Drawer/confirmation/StakePoolConfirmationFooter.tsx @@ -112,7 +112,7 @@ export const StakePoolConfirmationFooter = ({ popupView }: StakePoolConfirmation }, [analytics, currentPortfolio, draftPortfolio, handleSubmission]); const confirmLabel = useMemo(() => { - if (!isHardwareWallet) { + if (isHardwareWallet) { const staleLabels = popupView ? t('drawer.confirmation.button.continueInAdvancedView') : t('drawer.confirmation.button.confirmWithDevice', { hardwareWallet: walletType }); From 57a8a769e801f85f2951dc5f6cd5756982918090 Mon Sep 17 00:00:00 2001 From: Janusz Janus Date: Tue, 23 Apr 2024 11:58:48 +0200 Subject: [PATCH 71/74] test(extension): add missing checking tokens data in extended mode (#1084) --- .../e2e-tests/src/assert/tokensPageAssert.ts | 67 ++++++++----------- packages/e2e-tests/src/elements/tokensPage.ts | 10 +++ .../e2e-tests/src/steps/tokensPageSteps.ts | 8 +-- packages/e2e-tests/src/support/patterns.ts | 8 ++- 4 files changed, 48 insertions(+), 45 deletions(-) diff --git a/packages/e2e-tests/src/assert/tokensPageAssert.ts b/packages/e2e-tests/src/assert/tokensPageAssert.ts index d1dcf7108..a43796bcb 100644 --- a/packages/e2e-tests/src/assert/tokensPageAssert.ts +++ b/packages/e2e-tests/src/assert/tokensPageAssert.ts @@ -67,59 +67,45 @@ class TokensPageAssert { await TokensPage.tokenName(i).waitForDisplayed(); await TokensPage.tokenTicker(i).waitForDisplayed(); if (mode === 'extended') { - // TODO: verify price cells in extended mode + await TokensPage.tokenPriceAda(i).waitForDisplayed(); + await TokensPage.tokenPriceChange(i).waitForDisplayed(); } await TokensPage.tokenBalance(i).waitForDisplayed(); await TokensPage.tokenFiatBalance(i).waitForDisplayed(); } }; - assertSeeCardanoItem = async (mode: 'extended' | 'popup') => { - expect(await TokensPage.tokenName(0).getText()).to.equal(Asset.CARDANO.name); - expect(await TokensPage.tokenTicker(0).getText()).to.equal(Asset.CARDANO.ticker); - - if (mode === 'extended') { - // TODO: verify price cells in extended mode - } - - const tokenBalance = await TokensPage.getTokenBalanceAsFloatByIndex(0); - expect(tokenBalance).to.be.greaterThan(0); - - const tokenFiatBalance = (await TokensPage.tokenFiatBalance(0).getText()).replace(',', ''); - const tokenValueFiatFloat = Number.parseFloat(tokenFiatBalance.split(' ')[0]); - expect(tokenValueFiatFloat).to.be.greaterThan(0); + assertSeeNativeToken = async (tokenName: Asset, mode: 'extended' | 'popup') => { + await this.assertSeeTokenItemBasicData(tokenName); + await this.assertSeeTokenData(tokenName, true, mode); }; - assertSeeLaceCoinItem = async (mode: 'extended' | 'popup') => { - expect(await TokensPage.getTokenNames()).to.contain(Asset.LACE_COIN.name); - expect(await TokensPage.getTokenTickers()).to.contain(Asset.LACE_COIN.ticker); - - const tokensTableIndex = await TokensPage.getTokenRowIndex(Asset.LACE_COIN.name); - - if (mode === 'extended') { - // TODO: verify price cells in extended mode - } - - const tokenBalance = await TokensPage.getTokenBalanceAsFloatByIndex(tokensTableIndex); - expect(tokenBalance).to.be.greaterThan(0); + assertSeeNotNativeToken = async (tokenName: Asset, mode: 'extended' | 'popup') => { + await this.assertSeeTokenItemBasicData(tokenName); + await this.assertSeeTokenData(tokenName, false, mode); + }; - const tokenFiatBalance = await TokensPage.tokenFiatBalance(tokensTableIndex).getText(); - expect(tokenFiatBalance).to.equal('-'); + assertSeeTokenItemBasicData = async (tokenName: Asset) => { + const tokensTableIndex = await TokensPage.getTokenRowIndex(tokenName.name); + expect(await TokensPage.tokenName(tokensTableIndex).getText()).to.contain(tokenName.name); + expect(await TokensPage.tokenTicker(tokensTableIndex).getText()).to.contain(tokenName.ticker); + expect(await TokensPage.getTokenBalanceAsFloatByIndex(tokensTableIndex)).to.be.greaterThan(0); }; - assertSeeHoskyItem = async (mode: 'extended' | 'popup') => { - expect(await TokensPage.getTokenNames()).to.contain(Asset.HOSKY_TOKEN.name); - expect(await TokensPage.getTokenTickers()).to.contain(Asset.HOSKY_TOKEN.ticker); + private assertTokenValueMatchesPattern = async (tokenValue: string, pattern: RegExp, isNativeToken: boolean) => { + isNativeToken ? expect(tokenValue).to.match(pattern) : expect(tokenValue).to.equal('-'); + }; - const tokensTableIndex = await TokensPage.getTokenRowIndex(Asset.HOSKY_TOKEN.name); + assertSeeTokenData = async (tokenName: Asset, nativeToken: boolean, mode: 'extended' | 'popup') => { + const tokensTableIndex = await TokensPage.getTokenRowIndex(tokenName.name); + const tokenValueFiat = await TokensPage.tokenFiatBalance(tokensTableIndex).getText(); + await this.assertTokenValueMatchesPattern(tokenValueFiat, TestnetPatterns.TOKEN_VALUE_FIAT_REGEX, nativeToken); if (mode === 'extended') { - // TODO: verify price cells in extended mode + const tokenValuePriceAda = await TokensPage.tokenPriceAda(tokensTableIndex).getText(); + await this.assertTokenValueMatchesPattern(tokenValuePriceAda, TestnetPatterns.TOKEN_VALUE_ADA_REGEX, nativeToken); + const tokenValuePriceChange = await TokensPage.tokenPriceChange(tokensTableIndex).getText(); + await this.assertTokenValueMatchesPattern(tokenValuePriceChange, TestnetPatterns.TOKEN_PRICE_CHANGE, nativeToken); } - const tokenBalance = await TokensPage.getTokenBalanceAsFloatByIndex(tokensTableIndex); - expect(tokenBalance).to.be.greaterThan(0); - - const tokenFiatBalance = await TokensPage.tokenFiatBalance(tokensTableIndex).getText(); - expect(tokenFiatBalance).to.equal('-'); }; async assertSeeToken(shouldSee: boolean, tokenDetails: ExpectedTokenDetails, mode: 'extended' | 'popup') { @@ -131,7 +117,8 @@ class TokensPageAssert { const tokenBalance = await TokensPage.getTokenBalanceAsFloatByIndex(tokensTableIndex); expect(tokenBalance).to.equal(tokenDetails.value); if (mode === 'extended') { - // TODO: verify price cells in extended mode + await TokensPage.tokenPriceAda(tokensTableIndex).waitForDisplayed(); + await TokensPage.tokenPriceChange(tokensTableIndex).waitForDisplayed(); } } else { expect(await TokensPage.getTokenNames()).to.not.contain(tokenDetails.name); diff --git a/packages/e2e-tests/src/elements/tokensPage.ts b/packages/e2e-tests/src/elements/tokensPage.ts index 4282d8759..fe9e702c1 100644 --- a/packages/e2e-tests/src/elements/tokensPage.ts +++ b/packages/e2e-tests/src/elements/tokensPage.ts @@ -14,6 +14,8 @@ class TokensPage { private TOKEN_TICKER = '[data-testid="token-table-cell-ticker"]'; private TOKEN_BALANCE = '[data-testid="token-table-cell-balance"]'; private TOKEN_FIAT_BALANCE = '[data-testid="token-table-cell-fiat-balance"]'; + private TOKEN_PRICE = '[data-testid="token-table-cell-price"]'; + private TOKEN_VARIATION = '[data-testid="token-table-cell-price-variation"]'; private COINGECKO_CREDITS = '[data-testid="coingecko-credits"]'; private COINGECKO_LINK = '[data-testid="coingecko-link"]'; private RECEIVE_BUTTON_POPUP_MODE = 'main [data-testid="receive-button"]'; @@ -80,6 +82,14 @@ class TokensPage { return $$(this.TOKENS_TABLE_ROW)[index].$(this.TOKEN_FIAT_BALANCE); } + tokenPriceAda(index: number): ChainablePromiseElement { + return $$(this.TOKENS_TABLE_ROW)[index].$(this.TOKEN_PRICE); + } + + tokenPriceChange(index: number): ChainablePromiseElement { + return $$(this.TOKENS_TABLE_ROW)[index].$(this.TOKEN_VARIATION); + } + tokensTableItemWithName(tokenName: string): ChainablePromiseElement { const selector = `${this.TOKENS_TABLE_ROW}[descendant::*[text()='${tokenName}']]`; return $(selector); diff --git a/packages/e2e-tests/src/steps/tokensPageSteps.ts b/packages/e2e-tests/src/steps/tokensPageSteps.ts index a23ffc713..59e48bb0a 100644 --- a/packages/e2e-tests/src/steps/tokensPageSteps.ts +++ b/packages/e2e-tests/src/steps/tokensPageSteps.ts @@ -35,8 +35,8 @@ Then( /^I see Cardano & LaceCoin tokens on the list with all the details in (extended|popup) mode$/, async (mode: 'extended' | 'popup') => { await tokensPageAssert.assertSeeTableItems(mode); - await tokensPageAssert.assertSeeCardanoItem(mode); - await tokensPageAssert.assertSeeLaceCoinItem(mode); + await tokensPageAssert.assertSeeNativeToken(Asset.CARDANO, mode); + await tokensPageAssert.assertSeeNotNativeToken(Asset.LACE_COIN, mode); } ); @@ -44,8 +44,8 @@ Then( /^I see Cardano & Hosky tokens on the list with all the details in (extended|popup) mode$/, async (mode: 'extended' | 'popup') => { await tokensPageAssert.assertSeeTableItems(mode); - await tokensPageAssert.assertSeeCardanoItem(mode); - await tokensPageAssert.assertSeeHoskyItem(mode); + await tokensPageAssert.assertSeeNativeToken(Asset.CARDANO, mode); + await tokensPageAssert.assertSeeNativeToken(Asset.HOSKY_TOKEN, mode); } ); diff --git a/packages/e2e-tests/src/support/patterns.ts b/packages/e2e-tests/src/support/patterns.ts index 37797397c..b4dc198a6 100644 --- a/packages/e2e-tests/src/support/patterns.ts +++ b/packages/e2e-tests/src/support/patterns.ts @@ -16,6 +16,9 @@ const STAKE_POOL_LIST_COST_REGEX = /(\d*\.)?\d+\s?%(\s\+\s\d*ADA)?/; const TIMESTAMP_REGEX = /\d{2}:\d{2}:\d{2}/; const PLEDGE_REGEX = /^\d{1,3}(\.\d{0,2})?[BKM]?$/; const BLOCKS_REGEX = /^(-|\d*)$/; +const TOKEN_VALUE_FIAT_REGEX = /^([\d+,.])+\.\d+\s\D{2,3}$/; +const TOKEN_VALUE_ADA_REGEX = /^\d+\.\d+$/; +const TOKEN_PRICE_CHANGE = /^([+-])\d+\.\d{2}$/; export const TestnetPatterns = { TESTNET_ADDR_REGEX, @@ -35,5 +38,8 @@ export const TestnetPatterns = { STAKE_POOL_LIST_COST_REGEX, TIMESTAMP_REGEX, PLEDGE_REGEX, - BLOCKS_REGEX + BLOCKS_REGEX, + TOKEN_VALUE_FIAT_REGEX, + TOKEN_VALUE_ADA_REGEX, + TOKEN_PRICE_CHANGE }; From dc5f2229130f4afece0308779262573d92610cad Mon Sep 17 00:00:00 2001 From: Lucas Date: Tue, 23 Apr 2024 11:10:58 -0300 Subject: [PATCH 72/74] [LW 10170] Store own addresses from all wallets and accounts (#1033) * feat: cache wallets addresses in metadata --------- Co-authored-by: Dominik Guzei --- .../DappTransactionContainer.tsx | 6 +- .../__tests__/cache-wallets-address.test.ts | 107 ++++++++++++++++++ .../background/cache-wallets-address.ts | 31 +++++ .../src/lib/scripts/background/wallet.ts | 3 + .../get-all-wallets-addresses.test.ts | 36 ++++++ .../src/utils/get-all-wallets-addresses.ts | 9 ++ .../components/TransactionDetailsProxy.tsx | 5 +- .../cardano/src/wallet/lib/cardano-wallet.ts | 1 + .../TransactionDetails.stories.tsx | 3 +- 9 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/__tests__/cache-wallets-address.test.ts create mode 100644 apps/browser-extension-wallet/src/lib/scripts/background/cache-wallets-address.ts create mode 100644 apps/browser-extension-wallet/src/utils/__tests__/get-all-wallets-addresses.test.ts create mode 100644 apps/browser-extension-wallet/src/utils/get-all-wallets-addresses.ts diff --git a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx index d5de946fa..8f7c5a175 100644 --- a/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx +++ b/apps/browser-extension-wallet/src/features/dapp/components/confirm-transaction/DappTransactionContainer.tsx @@ -20,10 +20,11 @@ import { createWalletAssetProvider } from '@cardano-sdk/wallet'; import { Skeleton } from 'antd'; import { useCurrencyStore, useAppSettingsContext } from '@providers'; -import { logger } from '@lib/wallet-api-ui'; +import { logger, walletRepository } from '@lib/wallet-api-ui'; import { useComputeTxCollateral } from '@hooks/useComputeTxCollateral'; import { utxoAndBackendChainHistoryResolver } from '@src/utils/utxo-chain-history-resolver'; import { AddressBookSchema, useDbStateValue } from '@lib/storage'; +import { getAllWalletsAddresses } from '@src/utils/get-all-wallets-addresses'; interface DappTransactionContainerProps { errorMessage?: string; @@ -82,6 +83,7 @@ export const DappTransactionContainer = withAddressBookContext( const userRewardAccounts = useObservable(inMemoryWallet.delegation.rewardAccounts$); const rewardAccountsAddresses = useMemo(() => userRewardAccounts?.map((key) => key.address), [userRewardAccounts]); const protocolParameters = useObservable(inMemoryWallet?.protocolParameters$); + const allWalletsAddresses = getAllWalletsAddresses(useObservable(walletRepository.wallets$)); useEffect(() => { if (!req || !protocolParameters) { @@ -151,7 +153,7 @@ export const DappTransactionContainer = withAddressBookContext( errorMessage={errorMessage} toAddress={toAddressTokens} collateral={txCollateral} - ownAddresses={ownAddresses} + ownAddresses={allWalletsAddresses.length > 0 ? allWalletsAddresses : ownAddresses} addressToNameMap={addressToNameMap} /> ) : ( diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/__tests__/cache-wallets-address.test.ts b/apps/browser-extension-wallet/src/lib/scripts/background/__tests__/cache-wallets-address.test.ts new file mode 100644 index 000000000..8a88ce90b --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/background/__tests__/cache-wallets-address.test.ts @@ -0,0 +1,107 @@ +/* eslint-disable no-magic-numbers */ +/* eslint-disable unicorn/no-useless-undefined */ +import { WalletManager, WalletRepository } from '@cardano-sdk/web-extension'; +import { cacheActivatedWalletAddressSubscription } from '../cache-wallets-address'; +import { Wallet } from '@lace/cardano'; +import { BehaviorSubject, of } from 'rxjs'; + +describe('cacheActivatedWalletAddressSubscription', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should not trigger subscription for no active wallet', () => { + const mockWalletManager = { + activeWallet$: of(undefined), + activeWalletId$: of(undefined) + } as unknown as WalletManager; + + const mockWalletRepository = { + wallets$: of([]), + updateWalletMetadata: jest.fn() + } as unknown as WalletRepository; + + cacheActivatedWalletAddressSubscription(mockWalletManager, mockWalletRepository); + + expect(mockWalletRepository.updateWalletMetadata).not.toHaveBeenCalled(); + }); + + it('should subscribe and update metadata', () => { + const mockWalletManager = { + activeWallet$: of({ addresses$: of([{ address: 'address1' }]) }), + activeWalletId$: of({ walletId: 'walletId' }) + } as unknown as WalletManager; + + const mockWalletRepository = { + wallets$: of([ + { + walletId: 'walletId', + metadata: {} + } + ]), + updateWalletMetadata: jest.fn() + } as unknown as WalletRepository; + + cacheActivatedWalletAddressSubscription(mockWalletManager, mockWalletRepository); + + expect(mockWalletRepository.updateWalletMetadata).toHaveBeenCalledWith({ + walletId: 'walletId', + metadata: { + walletAddresses: ['address1'] + } + }); + }); + + it('should subscribe and update metadata when a new wallet is added and activated', () => { + const activeWallet$ = new BehaviorSubject({ + addresses$: of([{ address: 'address1' }]) + }); + const activeWalletId$ = new BehaviorSubject({ + walletId: 'walletId1' + }); + const wallets$ = new BehaviorSubject<{ walletId: string; metadata: { walletAddresses?: string[] } }[]>([ + { + walletId: 'walletId1', + metadata: { walletAddresses: ['address1'] } + } + ]); + const mockWalletManager = { + activeWallet$, + activeWalletId$ + } as unknown as WalletManager; + + const mockWalletRepository = { + wallets$, + updateWalletMetadata: jest.fn() + } as unknown as WalletRepository; + + cacheActivatedWalletAddressSubscription(mockWalletManager, mockWalletRepository); + + wallets$.next([ + { + walletId: 'walletId1', + metadata: { walletAddresses: ['address1'] } + }, + { + walletId: 'walletId2', + metadata: {} + } + ]); + activeWalletId$.next({ walletId: 'walletId2' }); + activeWallet$.next({ addresses$: of([{ address: 'address2' }, { address: 'address3' }]) }); + + expect(mockWalletRepository.updateWalletMetadata).toHaveBeenNthCalledWith(1, { + walletId: 'walletId1', + metadata: { + walletAddresses: ['address1'] + } + }); + + expect(mockWalletRepository.updateWalletMetadata).toHaveBeenNthCalledWith(2, { + walletId: 'walletId2', + metadata: { + walletAddresses: ['address2', 'address3'] + } + }); + }); +}); diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/cache-wallets-address.ts b/apps/browser-extension-wallet/src/lib/scripts/background/cache-wallets-address.ts new file mode 100644 index 000000000..54f9857d1 --- /dev/null +++ b/apps/browser-extension-wallet/src/lib/scripts/background/cache-wallets-address.ts @@ -0,0 +1,31 @@ +import { WalletManager, WalletRepository } from '@cardano-sdk/web-extension'; +import { Wallet } from '@lace/cardano'; +import { filter, switchMap, withLatestFrom, zip } from 'rxjs'; + +export const cacheActivatedWalletAddressSubscription = ( + walletManager: WalletManager, + walletRepository: WalletRepository +): void => { + zip([ + walletManager.activeWalletId$.pipe(filter((activeWalletId) => Boolean(activeWalletId))), + walletManager.activeWallet$.pipe( + filter((wallet) => Boolean(wallet)), + switchMap((wallet) => wallet.addresses$) + ) + ]) + .pipe(withLatestFrom(walletRepository.wallets$)) + .subscribe(([[activeWallet, walletAddresses], wallets]) => { + const wallet = wallets.find(({ walletId }) => walletId === activeWallet.walletId); + const uniqueAddresses = [ + ...new Set([...(wallet.metadata.walletAddresses || []), ...walletAddresses.map(({ address }) => address)]) + ]; + + walletRepository.updateWalletMetadata({ + walletId: activeWallet.walletId, + metadata: { + ...wallet.metadata, + walletAddresses: uniqueAddresses + } + }); + }); +}; diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts index 2c64fa9d1..0f58377d9 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/wallet.ts @@ -23,6 +23,7 @@ import { import { Wallet } from '@lace/cardano'; import { ADA_HANDLE_POLICY_ID, HANDLE_SERVER_URLS } from '@src/features/ada-handle/config'; import { Cardano, NotImplementedError } from '@cardano-sdk/core'; +import { cacheActivatedWalletAddressSubscription } from './cache-wallets-address'; const logger = console; @@ -198,4 +199,6 @@ walletManager logger.error('Failed to initialize wallet manager', error); }); +cacheActivatedWalletAddressSubscription(walletManager, walletRepository); + export const wallet$ = walletManager.activeWallet$; diff --git a/apps/browser-extension-wallet/src/utils/__tests__/get-all-wallets-addresses.test.ts b/apps/browser-extension-wallet/src/utils/__tests__/get-all-wallets-addresses.test.ts new file mode 100644 index 000000000..c2ade5698 --- /dev/null +++ b/apps/browser-extension-wallet/src/utils/__tests__/get-all-wallets-addresses.test.ts @@ -0,0 +1,36 @@ +import { AnyWallet } from '@cardano-sdk/web-extension'; +import { getAllWalletsAddresses } from '../get-all-wallets-addresses'; +import { Wallet } from '@lace/cardano'; + +describe('getAllWalletsAddresses', () => { + it('should return an empty array if undefined is provided', () => { + const addresses = getAllWalletsAddresses(); + + expect(addresses).toEqual([]); + }); + + it('should return an empty array if no wallets are provided', () => { + const addresses = getAllWalletsAddresses([]); + + expect(addresses).toEqual([]); + }); + + it('should return an array of payment addresses', () => { + const mockWallets = [ + { + metadata: { + walletAddresses: ['addr1', 'addr2'] + } + }, + { + metadata: { + walletAddresses: ['addr2', 'addr3'] + } + } + ] as AnyWallet[]; + + const addresses = getAllWalletsAddresses(mockWallets); + + expect(addresses).toEqual(['addr1', 'addr2', 'addr3']); + }); +}); diff --git a/apps/browser-extension-wallet/src/utils/get-all-wallets-addresses.ts b/apps/browser-extension-wallet/src/utils/get-all-wallets-addresses.ts new file mode 100644 index 000000000..690c1c32b --- /dev/null +++ b/apps/browser-extension-wallet/src/utils/get-all-wallets-addresses.ts @@ -0,0 +1,9 @@ +import { AnyWallet } from '@cardano-sdk/web-extension'; +import { Wallet } from '@lace/cardano'; +import flatMap from 'lodash/flatMap'; + +export const getAllWalletsAddresses = ( + wallets: AnyWallet[] = [] +): Wallet.Cardano.PaymentAddress[] => [ + ...new Set(flatMap(wallets.map(({ metadata: { walletAddresses = [] } }) => walletAddresses))) +]; diff --git a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx index e0223dbe9..e5bab6689 100644 --- a/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx +++ b/apps/browser-extension-wallet/src/views/browser-view/features/activity/components/TransactionDetailsProxy.tsx @@ -10,6 +10,8 @@ import { APP_MODE_POPUP } from '@src/utils/constants'; import { config } from '@src/config'; import { PostHogAction } from '@providers/AnalyticsProvider/analyticsTracker'; import { useObservable } from '@lace/common'; +import { getAllWalletsAddresses } from '@src/utils/get-all-wallets-addresses'; +import { walletRepository } from '@lib/wallet-api-ui'; type TransactionDetailsProxyProps = { name: string; @@ -31,6 +33,7 @@ export const TransactionDetailsProxy = withAddressBookContext( const openExternalLink = useExternalLinkOpener(); // Prepare own addresses of active account + const allWalletsAddresses = getAllWalletsAddresses(useObservable(walletRepository.wallets$)); const walletAddresses = useObservable(inMemoryWallet.addresses$)?.map((a) => a.address); // Prepare address book data as Map @@ -101,7 +104,7 @@ export const TransactionDetailsProxy = withAddressBookContext( amountTransformer={amountTransformer} headerDescription={getHeaderDescription() || cardanoCoin.symbol} txSummary={txSummary} - ownAddresses={walletAddresses} + ownAddresses={allWalletsAddresses.length > 0 ? allWalletsAddresses : walletAddresses} addressToNameMap={addressToNameMap} coinSymbol={cardanoCoin.symbol} isPopupView={isPopupView} diff --git a/packages/cardano/src/wallet/lib/cardano-wallet.ts b/packages/cardano/src/wallet/lib/cardano-wallet.ts index e17f23713..90aadb267 100644 --- a/packages/cardano/src/wallet/lib/cardano-wallet.ts +++ b/packages/cardano/src/wallet/lib/cardano-wallet.ts @@ -27,6 +27,7 @@ export interface WalletMetadata { name: string; lockValue?: HexBlob; lastActiveAccountIndex?: number; + walletAddresses?: Cardano.PaymentAddress[]; } export interface AccountMetadata { diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx index 414ddf07b..97acb4e7b 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.stories.tsx @@ -56,7 +56,8 @@ const data: ComponentProps = { openExternalLink: (url) => window.open(url, '_blank', 'noopener,noreferrer'), handleOpenExternalHashLink: () => { console.log('handle on hash click', '639a43144dc2c0ead16f2fb753360f4b4f536502dbdb8aa5e424b00abb7534ff'); - } + }, + ownAddresses: [] }; const stakeVoteDelegationCertificate = [ From 5a829d63d6745fafef4ca3188ee74111f3fc1f6d Mon Sep 17 00:00:00 2001 From: Lukasz Jagiela <12641433+ljagiela@users.noreply.github.com> Date: Tue, 23 Apr 2024 16:33:24 +0200 Subject: [PATCH 73/74] test(extension): test maintenance 23 April (#1093) --- .../ActivityDetail/TransactionDetails.tsx | 17 ++-- .../src/assert/transactionDetailsAssert.ts | 99 ++++++++++--------- .../src/elements/transactionDetails.ts | 4 +- .../SendTransactionBundlesExtended.feature | 4 +- ...dTransactionBundlesExtendedMainnet.feature | 2 + ...endTransactionSimpleExtended.part1.feature | 3 +- ...endTransactionSimpleExtended.part2.feature | 2 +- .../SendTransactionSimplePopup.part1.feature | 3 +- .../SendTransactionSimplePopup.part2.feature | 2 +- .../src/features/TokensPageExtended.feature | 3 +- .../src/features/TokensPagePopup.feature | 3 +- .../src/steps/sendTransactionSimpleSteps.ts | 10 +- 12 files changed, 84 insertions(+), 68 deletions(-) diff --git a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx index 2406736fb..c76fcd1f0 100644 --- a/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx +++ b/packages/core/src/ui/components/ActivityDetail/TransactionDetails.tsx @@ -359,16 +359,19 @@ export const TransactionDetails = ({ )} {(summary.addr as string[]).map((addr) => { const address = isPopupView ? ( - + ) : ( - {addr} + + {addr} + ); return ( -
+
{address} {renderAddressTag(addr, getAddressTagTranslations(t), ownAddresses, addressToNameMap)}
diff --git a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts index 098b1993c..a5eadfc94 100644 --- a/packages/e2e-tests/src/assert/transactionDetailsAssert.ts +++ b/packages/e2e-tests/src/assert/transactionDetailsAssert.ts @@ -77,7 +77,7 @@ class TransactionsDetailsAssert { if (expectedActivityDetails.transactionData[i].addressTag) { const actualAddressTag = await TransactionDetailsPage.transactionDetailsToAddressTag(i).getText(); - expect(expectedActivityDetails.transactionData[i].addressTag).to(actualAddressTag); + expect(expectedActivityDetails.transactionData[i].addressTag).to.equal(actualAddressTag); } } } @@ -143,19 +143,20 @@ class TransactionsDetailsAssert { for (let i = 0; i <= rowsNumber && i < 10; i++) { await TransactionsPage.clickOnTransactionRow(i); - await TransactionDetailsPage.transactionDetailsInputsDropdown.click(); - await TransactionDetailsPage.transactionDetailsOutputsDropdown.click(); - await TransactionDetailsPage.transactionDetailsInputAddress.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsInputAdaAmount.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsInputFiatAmount.waitForDisplayed(); - // TODO refactor steps below - // some transactions (ADA only) don't have this field - // await TransactionDetailsPage.transactionDetailsInputTokens.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsOutputAddress.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsOutputAdaAmount.waitForDisplayed(); - await TransactionDetailsPage.transactionDetailsOutputFiatAmount.waitForDisplayed(); - // await TransactionDetailsPage.transactionDetailsOutputTokens.waitForDisplayed(); - + if ((await TransactionDetailsPage.transactionDetailsDescription.getText()) !== 'Rewards') { + await TransactionDetailsPage.transactionDetailsInputsDropdown.click(); + await TransactionDetailsPage.transactionDetailsOutputsDropdown.click(); + await TransactionDetailsPage.transactionDetailsInputAddress.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsInputAdaAmount.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsInputFiatAmount.waitForDisplayed(); + // TODO refactor steps below + // some transactions (ADA only) don't have this field + // await TransactionDetailsPage.transactionDetailsInputTokens.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsOutputAddress.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsOutputAdaAmount.waitForDisplayed(); + await TransactionDetailsPage.transactionDetailsOutputFiatAmount.waitForDisplayed(); + // await TransactionDetailsPage.transactionDetailsOutputTokens.waitForDisplayed(); + } await TransactionDetailsPage.closeActivityDetails(mode); } } @@ -166,39 +167,41 @@ class TransactionsDetailsAssert { for (let i = 0; i <= rowsNumber && i < 10; i++) { await TransactionsPage.clickOnTransactionRow(i); - await TransactionDetailsPage.transactionDetailsInputsDropdown.click(); - await TransactionDetailsPage.transactionDetailsInputsDropdown.waitForStable(); - await TransactionDetailsPage.transactionDetailsOutputsDropdown.click(); - await TransactionDetailsPage.transactionDetailsOutputsDropdown.waitForStable(); - - const txDetailsInputADAValueString = await TransactionDetailsPage.transactionDetailsInputAdaAmount.getText(); - const txDetailsInputADAValue = Number(txDetailsInputADAValueString.split(' ', 1)); - const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); - - const txDetailsInputFiatValueString = await TransactionDetailsPage.transactionDetailsInputFiatAmount.getText(); - const txDetailsInputFiatValue = Number(txDetailsInputFiatValueString.slice(1).split(' ', 1)); - - const txDetailsOutputADAValueString = await TransactionDetailsPage.transactionDetailsOutputAdaAmount.getText(); - const txDetailsOutputADAValue = Number(txDetailsOutputADAValueString.split(' ', 1)); - - const txDetailsOutputFiatValueString = await TransactionDetailsPage.transactionDetailsOutputFiatAmount.getText(); - const txDetailsOutputFiatValue = Number(txDetailsOutputFiatValueString.slice(1).split(' ', 1)); - - if (!txType.includes(stakeKeyRegistration)) { - const txDetailsFeeADAValueString = await TransactionDetailsPage.transactionDetailsFeeADA.getText(); - const txDetailsFeeADAValue = Number(txDetailsFeeADAValueString.split(' ', 1)); - expect(txDetailsFeeADAValue).to.be.greaterThan(0); + if ((await TransactionDetailsPage.transactionDetailsDescription.getText()) !== 'Rewards') { + await TransactionDetailsPage.transactionDetailsInputsDropdown.click(); + await TransactionDetailsPage.transactionDetailsInputsDropdown.waitForStable(); + await TransactionDetailsPage.transactionDetailsOutputsDropdown.click(); + await TransactionDetailsPage.transactionDetailsOutputsDropdown.waitForStable(); + + const txDetailsInputADAValueString = await TransactionDetailsPage.transactionDetailsInputAdaAmount.getText(); + const txDetailsInputADAValue = Number(txDetailsInputADAValueString.split(' ', 1)); + const txType = await TransactionDetailsPage.transactionDetailsDescription.getText(); + + const txDetailsInputFiatValueString = await TransactionDetailsPage.transactionDetailsInputFiatAmount.getText(); + const txDetailsInputFiatValue = Number(txDetailsInputFiatValueString.slice(1).split(' ', 1)); + + const txDetailsOutputADAValueString = await TransactionDetailsPage.transactionDetailsOutputAdaAmount.getText(); + const txDetailsOutputADAValue = Number(txDetailsOutputADAValueString.split(' ', 1)); + + const txDetailsOutputFiatValueString = + await TransactionDetailsPage.transactionDetailsOutputFiatAmount.getText(); + const txDetailsOutputFiatValue = Number(txDetailsOutputFiatValueString.slice(1).split(' ', 1)); + + if (!txType.includes(stakeKeyRegistration)) { + const txDetailsFeeADAValueString = await TransactionDetailsPage.transactionDetailsFeeADA.getText(); + const txDetailsFeeADAValue = Number(txDetailsFeeADAValueString.split(' ', 1)); + expect(txDetailsFeeADAValue).to.be.greaterThan(0); + + const txDetailsFeeFiatValueString = await TransactionDetailsPage.transactionDetailsFeeFiat.getText(); + const txDetailsFeeFiatValue = Number(txDetailsFeeFiatValueString.slice(1).split(' ', 1)); + expect(txDetailsFeeFiatValue).to.be.greaterThan(0); + } - const txDetailsFeeFiatValueString = await TransactionDetailsPage.transactionDetailsFeeFiat.getText(); - const txDetailsFeeFiatValue = Number(txDetailsFeeFiatValueString.slice(1).split(' ', 1)); - expect(txDetailsFeeFiatValue).to.be.greaterThan(0); + expect(txDetailsInputADAValue).to.be.greaterThan(0); + expect(txDetailsInputFiatValue).to.be.greaterThan(0); + expect(txDetailsOutputADAValue).to.be.greaterThan(0); + expect(txDetailsOutputFiatValue).to.be.greaterThan(0); } - - expect(txDetailsInputADAValue).to.be.greaterThan(0); - expect(txDetailsInputFiatValue).to.be.greaterThan(0); - expect(txDetailsOutputADAValue).to.be.greaterThan(0); - expect(txDetailsOutputFiatValue).to.be.greaterThan(0); - await TransactionDetailsPage.closeActivityDetails(mode); } } @@ -212,7 +215,7 @@ class TransactionsDetailsAssert { await TransactionsPage.clickOnTransactionRow(i); await TransactionDetailsPage.transactionDetailsDescription.waitForClickable({ timeout: 15_000 }); if ( - !['Delegation', 'Stake Key De-Registration', 'Stake Key Registration', 'Self Transaction'].includes( + !['Delegation', 'Stake Key De-Registration', 'Stake Key Registration', 'Self Transaction', 'Rewards'].includes( transactionType ) ) { @@ -229,8 +232,8 @@ class TransactionsDetailsAssert { const rowsNumber = (await TransactionsPage.rows).length; for (let i = 0; i <= rowsNumber && i < 10; i++) { - // TODO Cover self transaction details with automation - if ((await TransactionsPage.transactionsTableItemType(i).getText()) !== 'Self Transaction') { + const skippedTransaction = ['Self Transaction', 'Rewards']; // should be covered in separate tests + if (!skippedTransaction.includes(await TransactionsPage.transactionsTableItemType(i).getText())) { await TransactionsPage.clickOnTransactionRow(i); await TransactionDetailsPage.transactionDetailsDescription.waitForClickable({ timeout: 15_000 }); const txType = (await TransactionDetailsPage.transactionDetailsDescription.getText()).split('\n')[0]; diff --git a/packages/e2e-tests/src/elements/transactionDetails.ts b/packages/e2e-tests/src/elements/transactionDetails.ts index f6644090c..719b304d7 100644 --- a/packages/e2e-tests/src/elements/transactionDetails.ts +++ b/packages/e2e-tests/src/elements/transactionDetails.ts @@ -15,8 +15,8 @@ class ActivityDetailsPage extends CommonDrawerElements { private TRANSACTION_DETAILS_SENT_TOKEN = '[data-testid="tx-sent-detail-token"]'; private TRANSACTION_DETAILS_SENT_ADA = '[data-testid="tx-sent-detail-ada"]'; private TRANSACTION_DETAILS_SENT_FIAT = '[data-testid="tx-sent-detail-fiat"]'; - private TRANSACTION_DETAILS_TO_ADDRESS = '[data-testid="tx-to-detail"] span'; - private TRANSACTION_DETAILS_TO_ADDRESS_TAG = '[data-testid="tx-to-detail"] [data-testid="address-tag"]'; + private TRANSACTION_DETAILS_TO_ADDRESS = '[data-testid="tx-to-address"]'; + private TRANSACTION_DETAILS_TO_ADDRESS_TAG = '[data-testid="address-tag"]'; private TRANSACTION_DETAILS_STATUS = '[data-testid="tx-status"]'; private TRANSACTION_DETAILS_TIMESTAMP = '[data-testid="tx-timestamp"]'; private TRANSACTION_DETAILS_FEE_ADA = '[data-testid="tx-amount-fee-amount"]'; diff --git a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature index b6f787e3f..88ff1b545 100644 --- a/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature +++ b/packages/e2e-tests/src/features/SendTransactionBundlesExtended.feature @@ -167,15 +167,15 @@ Feature: Send - Extended Browser View (Advanced Tx) And I click "Send" button on page header And I enter a valid "shelley" address in the bundle 1 recipient's address And I enter a 51% of total "tADA" asset in bundle 1 + And I open cancel modal to trigger button validation Then I do not see insufficient balance error in bundle 1 for "tADA" asset And "Review transaction" button is enabled on "Send" page When I click "Add bundle" button on "Send" page And I enter a valid "shelley" address in the bundle 2 recipient's address And I enter a 51% of total "tADA" asset in bundle 2 + And I open cancel modal to trigger button validation Then I see insufficient balance error in bundle 2 for "tADA" asset And I do not see insufficient balance error in bundle 1 for "tADA" asset - # step below is a workaround to set focus on something else than input - And I click "Review transaction" button on "Send" page And "Review transaction" button is disabled on "Send" page @LW-1762 diff --git a/packages/e2e-tests/src/features/SendTransactionBundlesExtendedMainnet.feature b/packages/e2e-tests/src/features/SendTransactionBundlesExtendedMainnet.feature index f99ad9fd4..6af08506f 100644 --- a/packages/e2e-tests/src/features/SendTransactionBundlesExtendedMainnet.feature +++ b/packages/e2e-tests/src/features/SendTransactionBundlesExtendedMainnet.feature @@ -147,11 +147,13 @@ Feature: Send - Extended Browser View (Advanced Tx) And I click "Send" button on page header And I enter a valid "shelley" address in the bundle 1 recipient's address And I enter a 51% of total "ADA" asset in bundle 1 + And I open cancel modal to trigger button validation Then I do not see insufficient balance error in bundle 1 for "ADA" asset And "Review transaction" button is enabled on "Send" page When I click "Add bundle" button on "Send" page And I enter a valid "shelley" address in the bundle 2 recipient's address And I enter a 51% of total "ADA" asset in bundle 2 + And I open cancel modal to trigger button validation Then I see insufficient balance error in bundle 2 for "ADA" asset And I see insufficient balance error in bundle 1 for "ADA" asset And "Review transaction" button is disabled on "Send" page diff --git a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature index 6ca3d9e44..50132b38f 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part1.feature @@ -176,10 +176,11 @@ Feature: LW-484: Send & Receive - Extended Browser View (Simple Tx) And I click "Send" button on page header And I enter a valid "shelley" address in the bundle 1 recipient's address When I enter a value of: 99999999 to the "tADA" asset - And I click on transaction drawer background to lose focus + And I open cancel modal to trigger button validation Then "Insufficient balance" error is displayed on "Send" page And "Review transaction" button is disabled on "Send" page When I enter a value of: 2 to the "tADA" asset + And I open cancel modal to trigger button validation Then "Insufficient balance" error is not displayed on "Send" page And "Review transaction" button is enabled on "Send" page diff --git a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature index 78f261130..eec03d9e0 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimpleExtended.part2.feature @@ -128,7 +128,7 @@ Feature: LW-484: Send & Receive - Extended Browser View (Simple Tx) When I click "Send" button on page header When I enter a valid "shelley" address in the bundle 1 recipient's address Then I enter a value of: to the "tADA" asset in bundle 1 - And I click on transaction drawer background to lose focus + And I open cancel modal to trigger button validation Then "Insufficient balance" error displayed on "Send" page Then "Review transaction" button is on "Send" page Examples: diff --git a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature index 15bbe09cf..2b40f6cd7 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part1.feature @@ -169,10 +169,11 @@ Feature: LW-484: Send & Receive - Popup View (Simple Tx) And I click "Send" button on Tokens page in popup mode And I enter a valid "shelley" address in the bundle 1 recipient's address When I enter a value of: 99999999 to the "tADA" asset in bundle 1 - And I click on transaction drawer background to lose focus + And I open cancel modal to trigger button validation Then "Insufficient balance" error is displayed on "Send" page And "Review transaction" button is disabled on "Send" page When I enter a value of: 2 to the "tADA" asset in bundle 1 + And I open cancel modal to trigger button validation Then "Insufficient balance" error is not displayed on "Send" page Then "Review transaction" button is enabled on "Send" page diff --git a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature index a5a6cf33f..a3f00b3b1 100644 --- a/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature +++ b/packages/e2e-tests/src/features/SendTransactionSimplePopup.part2.feature @@ -138,7 +138,7 @@ Feature: LW-484: Send & Receive - Popup View (Simple Tx) When I click "Send" button on Tokens page in popup mode When I enter a valid "shelley" address in the bundle 1 recipient's address Then I enter a value of: to the "tADA" asset in bundle 1 - And I click on transaction drawer background to lose focus + And I open cancel modal to trigger button validation Then "Insufficient balance" error displayed on "Send" page And "Review transaction" button is on "Send" page Examples: diff --git a/packages/e2e-tests/src/features/TokensPageExtended.feature b/packages/e2e-tests/src/features/TokensPageExtended.feature index 2cfceeb12..3b9be53e7 100644 --- a/packages/e2e-tests/src/features/TokensPageExtended.feature +++ b/packages/e2e-tests/src/features/TokensPageExtended.feature @@ -135,7 +135,8 @@ Feature: LW: Tokens tab - extended view And total wallet balance is masked with asterisks And balance and FIAT balance for each token are masked with asterisks - @LW-6889 @Testnet @Mainnet + @LW-6889 @Testnet @Mainnet @Pending + @issue=LW-10296 Scenario: Extended view - Token pricing - Price fetch expired error is displayed when coingecko request fails Given ADA fiat price has been fetched When I enable network interception to fail request: "https://coingecko.*" diff --git a/packages/e2e-tests/src/features/TokensPagePopup.feature b/packages/e2e-tests/src/features/TokensPagePopup.feature index 329f6b514..6849bdd43 100644 --- a/packages/e2e-tests/src/features/TokensPagePopup.feature +++ b/packages/e2e-tests/src/features/TokensPagePopup.feature @@ -105,7 +105,8 @@ Feature: LW: Tokens tab - popup view And total wallet balance is masked with asterisks And balance and FIAT balance for each token are masked with asterisks - @LW-6684 @Testnet @Mainnet + @LW-6684 @Testnet @Mainnet @Pending + @issue=LW-10296 Scenario: Popup view - Token pricing - Price fetch expired error is displayed when coingecko request fails Given ADA fiat price has been fetched When I enable network interception to fail request: "https://coingecko.*" diff --git a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts index 42e3f7ca2..92b73d90d 100644 --- a/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts +++ b/packages/e2e-tests/src/steps/sendTransactionSimpleSteps.ts @@ -38,6 +38,7 @@ import { AddressInput } from '../elements/AddressInput'; import { AssetInput } from '../elements/newTransaction/assetInput'; import TokenSelectionPage from '../elements/newTransaction/tokenSelectionPage'; import TransactionPasswordPage from '../elements/newTransaction/transactionPasswordPage'; +import { Key } from 'webdriverio'; Given(/I have several contacts whose start with the same characters/, async () => { await indexedDB.clearAddressBook(); @@ -269,12 +270,15 @@ Then( async (valueToEnter: string, assetName: string, bundleIndex: number) => { assetName = assetName === 'tADA' && extensionUtils.isMainnet() ? 'ADA' : assetName; await TransactionNewPage.coinConfigure(bundleIndex, assetName).fillTokenValue(Number.parseFloat(valueToEnter)); - // workaround for test automation only to fire all events after finished typing - await TransactionNewPage.clickDrawerBackground(); - await TransactionNewPage.coinConfigure(bundleIndex, assetName).balanceFiatValueElement.click(); } ); +Then(/^I open cancel modal to trigger button validation$/, async () => { + // workaround for test automation only to fire all events after finished typing + await browser.keys(Key.Escape); + await Modal.cancelButton.click(); +}); + Then(/^I click on transaction drawer background to lose focus$/, async () => { await TransactionNewPage.clickDrawerBackground(); }); From 0601346f870397cddd11ac26122bd398e7fe29ba Mon Sep 17 00:00:00 2001 From: Renan Valentin Date: Tue, 23 Apr 2024 11:39:05 -0300 Subject: [PATCH 74/74] fix(core): apply negative balance only for pending tx (#1068) --- .../ui/components/Activity/AssetActivityItem.module.scss | 6 +++++- .../core/src/ui/components/Activity/AssetActivityItem.tsx | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/core/src/ui/components/Activity/AssetActivityItem.module.scss b/packages/core/src/ui/components/Activity/AssetActivityItem.module.scss index d483d98a6..9fb039540 100644 --- a/packages/core/src/ui/components/Activity/AssetActivityItem.module.scss +++ b/packages/core/src/ui/components/Activity/AssetActivityItem.module.scss @@ -124,10 +124,14 @@ } } -.negativeBalance { +.pendingNegativeBalance { color: var(--data-orange); } .positiveBalance { color: var(--data-green); } + +.negativeBalance { + color: var(--text-color-primary); +} diff --git a/packages/core/src/ui/components/Activity/AssetActivityItem.tsx b/packages/core/src/ui/components/Activity/AssetActivityItem.tsx index 3cfc6cd2b..ba2e441fe 100644 --- a/packages/core/src/ui/components/Activity/AssetActivityItem.tsx +++ b/packages/core/src/ui/components/Activity/AssetActivityItem.tsx @@ -202,8 +202,9 @@ export const AssetActivityItem = ({