From 47e87567028422e770255c848ac2ddf4eba0c403 Mon Sep 17 00:00:00 2001 From: Chloe <44501120+chloezxyy@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:29:52 +0800 Subject: [PATCH] feat(e2e): address book transfer domain (#4057) * feat(e2e): address book transfer domain * fix(ui-ux): display updated wallet label on portfolio screen * feat(e2e): setup to send dfi to evm domain --------- Co-authored-by: Pierre Gee --- .../screens/Portfolio/PortfolioScreen.tsx | 6 +- .../components/AddressSelectionButtonV2.tsx | 36 ++- .../components/BottomSheetAddressDetailV2.tsx | 3 +- .../Portfolio/screens/AddressBookScreen.tsx | 5 +- .../transferDomain/addresses.spec.ts | 246 ++++++++++++++++++ 5 files changed, 288 insertions(+), 8 deletions(-) create mode 100644 mobile-app/cypress/e2e/functional/transferDomain/addresses.spec.ts diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioScreen.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioScreen.tsx index 3a685ab4ca..3f072b6e4b 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioScreen.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/PortfolioScreen.tsx @@ -13,18 +13,18 @@ import { import { useDisplayBalancesContext } from "@contexts/DisplayBalancesContext"; import { useWalletContext } from "@shared-contexts/WalletContext"; import { - useWalletPersistenceContext, useThemeContext, + useWalletPersistenceContext, useWhaleApiClient, useWhaleRpcClient, } from "@waveshq/walletkit-ui"; import { useBottomTabBarHeight } from "@react-navigation/bottom-tabs"; import { StackScreenProps } from "@react-navigation/stack"; import { - ocean, dexPricesSelectorByDenomination, fetchDexPrice, fetchTokens, + ocean, tokensSelector, WalletToken, } from "@waveshq/walletkit-ui/dist/store"; @@ -72,7 +72,7 @@ import { BottomSheetHeader } from "@components/BottomSheetHeader"; import * as SplashScreen from "expo-splash-screen"; import { useLogger } from "@shared-contexts/NativeLoggingProvider"; import { bottomTabDefaultRoutes } from "@screens/AppNavigator/constants/DefaultRoutes"; -import { useDomainContext, DomainType } from "@contexts/DomainContext"; +import { DomainType, useDomainContext } from "@contexts/DomainContext"; import { AddressSelectionButtonV2 } from "./components/AddressSelectionButtonV2"; import { ActionButtons } from "./components/ActionButtons"; import { diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/AddressSelectionButtonV2.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/AddressSelectionButtonV2.tsx index 0a1a7cdba9..d81b9d506f 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/AddressSelectionButtonV2.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/AddressSelectionButtonV2.tsx @@ -7,6 +7,11 @@ import { tailwind } from "@tailwind"; import { useAddressLabel } from "@hooks/useAddressLabel"; import { DomainType, useDomainContext } from "@contexts/DomainContext"; import { useWalletContext } from "@shared-contexts/WalletContext"; +import { useSelector } from "react-redux"; +import { RootState } from "@store"; +import { useEffect, useState } from "react"; +import { useWalletAddress, WalletAddressI } from "@hooks/useWalletAddress"; +import { useLogger } from "@shared-contexts/NativeLoggingProvider"; import { RandomAvatar } from "./RandomAvatar"; interface AddressSelectionButtonProps { @@ -20,7 +25,34 @@ export function AddressSelectionButtonV2( const { domain } = useDomainContext(); const { address, evmAddress } = useWalletContext(); const displayAddress = domain === DomainType.EVM ? evmAddress : address; - const addressLabel = useAddressLabel(displayAddress); + const activeLabel = useAddressLabel(displayAddress); + const { fetchWalletAddresses } = useWalletAddress(); + + const userPreferences = useSelector( + (state: RootState) => state.userPreferences, + ); + const labeledAddresses = userPreferences.addresses; + const logger = useLogger(); + + const [availableAddresses, setAvailableAddresses] = useState< + WalletAddressI[] + >([]); + + // Getting addresses + const fetchAddresses = async (): Promise => { + const addresses = await fetchWalletAddresses(); + setAvailableAddresses(addresses); + }; + + const activeAddress = availableAddresses.find(({ dvm }) => dvm === address); + const displayAddressLabel = + activeLabel === null + ? activeAddress?.generatedLabel + : labeledAddresses?.[address]?.label; + + useEffect(() => { + fetchAddresses().catch(logger.error); + }, [address]); return ( - {addressLabel != null ? addressLabel : displayAddress} + {displayAddressLabel} ); diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/BottomSheetAddressDetailV2.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/BottomSheetAddressDetailV2.tsx index 135b8d5b21..3f9e33f474 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/BottomSheetAddressDetailV2.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/components/BottomSheetAddressDetailV2.tsx @@ -231,7 +231,7 @@ export const BottomSheetAddressDetailV2 = ( {displayAddressLabel} @@ -325,6 +325,7 @@ export const BottomSheetAddressDetailV2 = ( name="edit-2" light={tailwind("text-mono-light-v2-700")} dark={tailwind("text-mono-dark-v2-700")} + testID={`address_edit_icon_address_row_${index}`} /> diff --git a/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/AddressBookScreen.tsx b/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/AddressBookScreen.tsx index a466304281..e9849f76ff 100644 --- a/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/AddressBookScreen.tsx +++ b/mobile-app/app/screens/AppNavigator/screens/Portfolio/screens/AddressBookScreen.tsx @@ -402,6 +402,7 @@ export function AddressBookScreen({ route, navigation }: Props): JSX.Element { activeOpacity={0.7} onPress={onDFIAddressClick} style={tailwind("flex flex-row items-center")} + testID={`address_row_${index}_${testIDSuffix}_caret`} > {address} @@ -855,6 +855,7 @@ function YourAddressLink({ {enableAddressSelect && ( { + const labels = ["DVMAddress", "EVMAddress"]; + const addresses = [ + "bcrt1q8rfsfny80jx78cmk4rsa069e2ckp6rn83u6ut9", + "0x2DeC425BF3c289C9B7452aD54E2F9877F21e0316", + ]; + + function validateMatchAddress(address: string, label: string): void { + cy.getByTestID("address_input").contains(address); + if (label === labels[0]) { + cy.getByTestID("address_input_footer").contains(label); + } else { + cy.getByTestID("address_input_footer_evm").contains(label); + } + } + + // Whitelisted addresses + function populateAddressBook(hasExistingAddress?: boolean): void { + cy.getByTestID("action_button_group").should("exist"); + cy.getByTestID("send_balance_button").click().wait(3000); + cy.getByTestID("select_DFI").click().wait(3000); + cy.getByTestID("address_book_button").click(); + cy.wrap(labels).each((_v, index: number) => { + if (index === 0) { + cy.getByTestID("button_add_address").click(); + } else { + cy.getByTestID("add_new_address").click(); + } + + if (hasExistingAddress) { + // Reselect DVM address type + cy.getByTestID("address_book_address_type_DVM").click(); + } + + // Select EVM address type + if (index === 1) { + cy.getByTestID("address_book_address_type_EVM").click(); + } + cy.getByTestID("address_book_label_input").type(labels[index]); + cy.getByTestID("address_book_label_input_error").should("not.exist"); + cy.getByTestID("address_book_address_input") + .clear() + .type(addresses[index]) + .blur(); + cy.getByTestID("address_book_address_input_error").should("not.exist"); + cy.getByTestID("save_address_label").click().wait(1000); + cy.getByTestID("pin_authorize").type("000000").wait(2000); + validateMatchAddress(addresses[index], labels[index]); + cy.wait(1000); + cy.getByTestID("address_input_clear_button").click(); + cy.getByTestID("address_book_button").click(); + cy.getByTestID(`address_row_label_${index}_WHITELISTED`).contains( + labels[index], + ); + cy.getByTestID(`address_row_text_${index}_WHITELISTED`).contains( + addresses[index], + ); + // cy.getByTestID('address_book_address_input').clear().type(addresses[index]).blur() + }); + } + function verifyWhitelistedAddressRowItems(index: number) { + cy.getByTestID(`address_row_label_${index}_WHITELISTED`).should( + "have.text", + labels[index], + ); + cy.getByTestID(`address_row_text_${index}_WHITELISTED`).should( + "have.text", + addresses[index], + ); + + cy.getByTestID(`address_row_${index}_WHITELISTED_caret`).should("exist"); + + // dvm address + if (index === 0) { + cy.getByTestID(`address_row_label_${index}_WHITELISTED_EVM_tag`).should( + "not.exist", + ); + // evm address + } else { + cy.getByTestID(`address_row_label_${index}_WHITELISTED_EVM_tag`).should( + "exist", + ); + } + } + function verifyYourAddressRowItems(index: number) { + // Generated wallet label + cy.getByTestID(`address_row_label_${index}_YOUR_ADDRESS`).should( + "have.text", + `Address ${index + 1}`, + ); + // dvm address + cy.getByTestID(`address_row_text_${index}_YOUR_ADDRESS`).should("exist"); + + // evm address + cy.getByTestID(`address_row_text_${index}_YOUR_ADDRESS_EVM`).should( + "exist", + ); + + // caret + cy.getByTestID(`address_row_${index}_YOUR_ADDRESS_caret`).should("exist"); + } + + // Send DFI tokens dvm -> evm + function topUpDfiInEvmDomain() { + cy.getByTestID("bottom_tab_portfolio").click(); + cy.getByTestID("send_balance_button").click(); + cy.getByTestID("select_DFI").click(); + cy.getByTestID("25%_amount_button").click(); + cy.getByTestID("address_book_button").click(); + cy.getByTestID("address_button_group_YOUR_ADDRESS").click(); + cy.getByTestID("address_row_text_0_YOUR_ADDRESS_EVM").click(); + cy.getByTestID("button_confirm_send_continue").click(); + + // Send confirmation screen + cy.getByTestID("button_confirm_send").click(); + cy.getByTestID("pin_authorize").type("000000").wait(4000); + } + + describe("Whitelisted and Your Addresses tab", () => { + before(() => { + cy.createEmptyWallet(true); + cy.sendDFItoWallet().sendDFITokentoWallet().wait(4000); + cy.getByTestID("bottom_tab_portfolio").click(); + + topUpDfiInEvmDomain(); + cy.getByTestID("oceanInterface_close").click(); // Close ocean interface popup + + populateYourAddresses(); // Generate new wallet Address 2 + }); + it("(dvm) Whitelisted - should not display evm tag for dvm addresses", () => { + populateAddressBook(); // Add whitelist addresses + verifyWhitelistedAddressRowItems(0); + }); + it("(dvm) Whitelisted - should display evm tag for evm addresses", () => { + verifyWhitelistedAddressRowItems(1); + }); + it("(dvm) Your Addresses - should display not evm tag for dvm addresses", () => { + cy.getByTestID("address_button_group_YOUR_ADDRESS").click(); + verifyYourAddressRowItems(0); + }); + it("(dvm) Your Addresses - should display evm tag for evm addresses", () => { + verifyYourAddressRowItems(1); + cy.getByTestID("bottom_tab_portfolio").click(); + }); + + // Switch to evm domain + it("(evm) Whitelisted - should disable evm addresses in evm domain", () => { + // Go back to portfolio page to switch domain + cy.getByTestID("domain_switch").click(); + + goToAddressBook(); + + cy.getByTestID("address_row_0_WHITELISTED").should( + "have.attr", + "aria-disabled", + "true", + ); + cy.getByTestID("address_row_1_WHITELISTED").should("not.be.disabled"); + }); + // Switch to your address tab + it("(evm) Your Address - should disable evm addresses in evm domain", () => { + cy.getByTestID("address_button_group_YOUR_ADDRESS").click(); + verifyYourAddressItemEvm(); + }); + }); +}); + +// Addresses that shows up under the 'Your address' tab in address book +function populateYourAddresses(): void { + // Create new wallet address - only available if there is DFI UTXO + cy.getByTestID("bottom_tab_portfolio").click(); + cy.getByTestID("wallet_address").click(); + cy.getByTestID("create_new_address").should("exist").click(); // Generate Address 2 wallet address + + // Go back to previous Address 1 + cy.getByTestID("wallet_address").click(); + cy.getByTestID("address_row_0").click(); + cy.getByTestID("wallet_address").should("have.text", "Address 1"); +} +function goToAddressBook() { + cy.getByTestID("action_button_group").should("exist"); + cy.getByTestID("send_balance_button").click().wait(3000); + cy.getByTestID("select_EvmDFI").click().wait(3000); + cy.getByTestID("address_book_button").click(); +} + +// Check if evm address is disabled in evm domain for generated Address 1 and 2 cards +function verifyYourAddressItemEvm() { + cy.wrap([0, 1]).each((index: number) => { + // dvm address + cy.getByTestID(`address_row_text_${index}_YOUR_ADDRESS`).should( + "not.be.disabled", + ); + // evm address + cy.getByTestID(`address_row_text_${index}_YOUR_ADDRESS_EVM`).should( + "have.attr", + "aria-disabled", + "true", + ); + }); +} + +context("Portfolio", () => { + before(() => { + cy.createEmptyWallet(true); + cy.sendDFItoWallet().sendTokenToWallet(["BTC", "BTC-DFI"]).wait(4000); + cy.getByTestID("bottom_tab_portfolio").click(); + }); + + describe("Wallet label (& address book bottom sheet)", () => { + it('should display generated address label "Address 1" as first wallet label', () => { + cy.getByTestID("wallet_address").should("have.text", "Address 1"); + }); + it("should display new wallet label after modifying wallet label", () => { + cy.getByTestID("wallet_address").click(); + + // Go to edit address book bottom sheet + cy.getByTestID("address_edit_icon_address_row_0").click(); + cy.getByTestID("address_book_label_input") + .click() + .type("New Wallet Label") + .blur() + .wait(1000); + cy.getByTestID("button_confirm_save_address_label").click().wait(1000); + + // Go back to address book bottom sheet + cy.getByTestID("list_header_address_label").should("exist"); + cy.getByTestID("close_bottom_sheet_button").click(); + + // Go back to portfolio page + cy.getByTestID("wallet_address").should("have.text", "New Wallet Label"); + }); + + // Generate new wallet address + it("should display generated Address 2 label as second wallet label", () => { + cy.getByTestID("bottom_tab_portfolio").click(); + cy.getByTestID("wallet_address").click(); + + cy.getByTestID("create_new_address").should("exist").click(); // Generate Address 2 wallet address + cy.getByTestID("wallet_address").should("have.text", "Address 2"); + }); + }); +}); + +// Ensure that the blockchain container in Docker returns 'Block minted' to know that it's connected to Local env, else restart container +// If values are taking too long to load or for flaky tests, restart Cypress