From acf18dc9163b052ad376f1b47ce083fe05706b9f Mon Sep 17 00:00:00 2001 From: Thibaut Sardan Date: Wed, 29 Jan 2025 12:47:06 +0100 Subject: [PATCH] add tests --- packages/ui/cypress/fixtures/landingData.ts | 4 + .../ui/cypress/fixtures/westendAccounts.ts | 24 +++ packages/ui/cypress/support/commands.ts | 5 + .../support/page-objects/settingsPage.ts | 10 +- .../ui/cypress/tests/hidden-accounts.cy.ts | 168 ++++++++++++++++++ .../cypress/tests/name-edition-display.cy.ts | 6 +- .../ui/cypress/tests/network-switch.cy.ts | 4 +- .../ui/cypress/tests/watched-accounts.cy.ts | 41 ++--- .../ui/src/contexts/HiddenAccountsContext.tsx | 14 +- .../ui/src/pages/Settings/HiddenAccounts.tsx | 2 +- .../ui/src/pages/Settings/WatchedAccounts.tsx | 2 +- 11 files changed, 250 insertions(+), 30 deletions(-) create mode 100644 packages/ui/cypress/fixtures/westendAccounts.ts create mode 100644 packages/ui/cypress/tests/hidden-accounts.cy.ts diff --git a/packages/ui/cypress/fixtures/landingData.ts b/packages/ui/cypress/fixtures/landingData.ts index da1a8631..13b70bae 100644 --- a/packages/ui/cypress/fixtures/landingData.ts +++ b/packages/ui/cypress/fixtures/landingData.ts @@ -1,12 +1,16 @@ export const baseUrl = 'http://localhost:3333' export const defaultNetwork = 'paseo' const WATCH_ACCOUNT_ANCHOR = 'watched-accounts' +const HIDDEN_ACCOUNTS_ANCHOR = 'hidden-accounts' + export const landingPageNetwork = (networkName: string) => `${baseUrl}?network=${networkName}` export const landingPageUrl = landingPageNetwork(defaultNetwork) export const getSettingsPageUrl = (network = defaultNetwork) => `${baseUrl}/settings?network=${network}` export const getSettingsPageWatchAccountUrl = (network = defaultNetwork) => `${getSettingsPageUrl(network)}#${WATCH_ACCOUNT_ANCHOR}` +export const getSettingsPageHiddenAccountUrl = (network = defaultNetwork) => + `${getSettingsPageUrl(network)}#${HIDDEN_ACCOUNTS_ANCHOR}` export const landingPageNetworkAddress = ({ network, address diff --git a/packages/ui/cypress/fixtures/westendAccounts.ts b/packages/ui/cypress/fixtures/westendAccounts.ts new file mode 100644 index 00000000..3ac4fe43 --- /dev/null +++ b/packages/ui/cypress/fixtures/westendAccounts.ts @@ -0,0 +1,24 @@ +import { InjectedAccountWitMnemonic } from './testAccounts' + +export const westendMemberAccount = { + // this is the member of a multisig and a multisig with Pure + // use in hidden-accounts + hidden: { + account: { + address: '5CPG8FMciJBBE47YwSQHte23tTz91egixJw514g6BCt5nHPz', + publicKey: '0x0e270b0984354e0f6033dee93293dbeaea366220ca59e584ba7614d8bf393040', + name: 'hidden', + type: 'sr25519', + mnemonic: '' + } as InjectedAccountWitMnemonic, + expectedSingleMultisig: { + westEndAddress: '5CvCLBVHufgqTDUVJL3xY6Pd7TVaYtaTGzvYRfGeaAPJLdDS', + paseoAddress: '1rVUWkMmSxJtkV1Fy6xgFDmy5VEFC8bMVf2axG18FQpX7hE', + pubKey: '0x25bee0c82d1a5ea1ef4f75b4cb517286a78ed51ab934b1636ac4d8b018811b1b' + }, + expectedPure: { + address: '5DqS9vsnXotmczKu87xb5KMUARCVF5JUUVveZz9R8UvXKExK', + pubKey: '0x4e596aec4922957174ba3f86860cca88fa4664006b511f11260cc34ca303d0dd' + } + } +} diff --git a/packages/ui/cypress/support/commands.ts b/packages/ui/cypress/support/commands.ts index 8ac7d147..bb8be265 100644 --- a/packages/ui/cypress/support/commands.ts +++ b/packages/ui/cypress/support/commands.ts @@ -7,6 +7,7 @@ import '@chainsafe/cypress-polkadot-wallet' const LOCALSTORAGE_ACCOUNT_NAMES_KEY = 'multix.accountNames' const LOCALSTORAGE_WATCHED_ACCOUNTS_KEY = 'multix.watchedAccount' +const LOCALSTORAGE_HIDDEN_ACCOUNTS_KEY = 'multix.hiddenAccounts' const LOCALSTORAGE_EXTENSION_CONNECTION_KEY = '@reactive-dot/wallet/injected/polkadot-js/connected' const LOCALSTORAGE_ALLOWED_CONNECTION_KEY = 'multix.canConnectToExtension' export const MULTIX_DAPP_NAME = 'Multix' @@ -28,6 +29,7 @@ Cypress.Commands.add('connectAccounts', (accountAddresses: string[]) => { interface IsetupAndVisit { url: string watchedAccounts?: string[] + hiddenAccounts?: Array<{ network: string; pubKey: string }> accountNames?: Record extensionConnectionAllowed?: boolean injectExtensionWithAccounts?: InjectedAccountWitMnemonic[] @@ -38,6 +40,7 @@ Cypress.Commands.add( ({ url, watchedAccounts, + hiddenAccounts, accountNames, extensionConnectionAllowed, injectExtensionWithAccounts @@ -49,6 +52,8 @@ Cypress.Commands.add( LOCALSTORAGE_WATCHED_ACCOUNTS_KEY, JSON.stringify(watchedAccounts) ) + !!hiddenAccounts?.length && + win.localStorage.setItem(LOCALSTORAGE_HIDDEN_ACCOUNTS_KEY, JSON.stringify(hiddenAccounts)) !!accountNames && win.localStorage.setItem(LOCALSTORAGE_ACCOUNT_NAMES_KEY, JSON.stringify(accountNames)) diff --git a/packages/ui/cypress/support/page-objects/settingsPage.ts b/packages/ui/cypress/support/page-objects/settingsPage.ts index 8e28b699..24beaab0 100644 --- a/packages/ui/cypress/support/page-objects/settingsPage.ts +++ b/packages/ui/cypress/support/page-objects/settingsPage.ts @@ -1,11 +1,17 @@ export const settingsPage = { // watch account section watchedAccountsAccordion: () => cy.get('[data-cy=accordion-title-watched-accounts]'), + watchedAccountsInputsWrapper: () => cy.get('[data-cy=wrapper-watched-accounts-inputs]'), + hiddenAccountsAccordion: () => cy.get('[data-cy=accordion-title-hidden-accounts]'), + hiddenAccountsInputsWrapper: () => cy.get('[data-cy=wrapper-hidden-accounts-inputs]'), accountAddressInput: () => cy.get('[data-cy=input-account-address]'), accountNameInput: () => cy.get('[data-cy=input-account-name]'), addButton: () => cy.get('[data-cy=button-add-account]'), - accountContainer: () => cy.get('[data-cy=container-account-details]', { timeout: 20000 }), - accountDeleteButton: () => cy.get('[data-cy=button-delete-watched-account]'), + watchedAccountsContainer: () => cy.get('[data-cy=container-account-details]', { timeout: 20000 }), + hiddenAccountsContainer: () => + cy.get('[data-cy=container-hidden-account-details]', { timeout: 20000 }), + watchedAccountDeleteButton: () => cy.get('[data-cy=button-delete-watched-account]'), + hiddenAccountDeleteButton: () => cy.get('[data-cy=button-delete-hidden-account]'), errorLabel: () => cy.get('[data-cy=label-add-account-error]'), // wallet connect section wallectConnectAccordion: () => cy.get('[data-cy=accordion-title-wallet-connect]'), diff --git a/packages/ui/cypress/tests/hidden-accounts.cy.ts b/packages/ui/cypress/tests/hidden-accounts.cy.ts new file mode 100644 index 00000000..0ebe84bd --- /dev/null +++ b/packages/ui/cypress/tests/hidden-accounts.cy.ts @@ -0,0 +1,168 @@ +import { accountDisplay } from '../support/page-objects/components/accountDisplay' +import { + getSettingsPageHiddenAccountUrl, + landingPageNetwork, + landingPageNetworkAddress +} from '../fixtures/landingData' +import { settingsPage } from '../support/page-objects/settingsPage' +import { westendMemberAccount } from '../fixtures/westendAccounts' +import { topMenuItems } from '../support/page-objects/topMenuItems' +import { multisigPage } from '../support/page-objects/multisigPage' +import { landingPage } from '../support/page-objects/landingPage' + +const randomAddress = 'HeVswqunza8rP2hEWDCThfiB5v2Jxng91yX2oGAZnCKtsgS' + +const addHiddenAccount = (address: string) => { + settingsPage.hiddenAccountsInputsWrapper().within(() => { + settingsPage.accountAddressInput().type(`${address}{enter}`, { delay: 20, timeout: 8000 }) + + settingsPage.addButton().should('be.enabled') + settingsPage.addButton().click() + }) +} + +const goToHiddenAccountSettings = () => { + topMenuItems.settingsButton().click() + settingsPage.hiddenAccountsAccordion().click() +} + +describe('Hidden Accounts', () => { + it('adds an account with a name to the hidden list', () => { + cy.setupAndVisit({ + url: landingPageNetwork('westend'), + extensionConnectionAllowed: true, + injectExtensionWithAccounts: [westendMemberAccount.hidden.account] + }) + goToHiddenAccountSettings() + topMenuItems.multiproxySelectorDesktop().should('be.visible').click() + topMenuItems.multiproxySelectorOptionDesktop().should('have.length', 2) + + //hide random account + addHiddenAccount(randomAddress) + topMenuItems.multiproxySelectorDesktop().should('be.visible').click() + topMenuItems.multiproxySelectorOptionDesktop().should('have.length', 2) + settingsPage.hiddenAccountsContainer().should('be.visible') + settingsPage.hiddenAccountsContainer().within(() => { + accountDisplay.identicon().should('be.visible') + accountDisplay.addressLabel().should('be.visible') + settingsPage.hiddenAccountDeleteButton().should('be.visible') + }) + + //hide the multisig account + addHiddenAccount(westendMemberAccount.hidden.expectedSingleMultisig.westEndAddress) + settingsPage.hiddenAccountsContainer().should('have.length', 2) + topMenuItems.multiproxySelectorDesktop().should('be.visible').click() + // the multisig should be hidden + topMenuItems.multiproxySelectorOptionDesktop().should('have.length', 1) + topMenuItems + .multiproxySelectorOptionDesktop() + .should('contain', westendMemberAccount.hidden.expectedPure.address.slice(0, 6)) + }) + + it('hides all accounts sequentially and we switches to the available accounts if any', () => { + cy.setupAndVisit({ + url: landingPageNetworkAddress({ + network: 'westend', + address: westendMemberAccount.hidden.expectedPure.address + }), + extensionConnectionAllowed: true, + injectExtensionWithAccounts: [westendMemberAccount.hidden.account] + }) + //land on the pure + multisigPage.accountHeader().within(() => { + accountDisplay + .addressLabel() + .should('contain.text', westendMemberAccount.hidden.expectedPure.address.slice(0, 6)) + }) + cy.url().should('include', westendMemberAccount.hidden.expectedPure.address) + goToHiddenAccountSettings() + addHiddenAccount(westendMemberAccount.hidden.expectedPure.address) + // we should now have only the single multisig and have it selected + cy.url().should('include', westendMemberAccount.hidden.expectedSingleMultisig.westEndAddress) + topMenuItems.homeButton().click() + multisigPage.accountHeader().within(() => { + accountDisplay + .addressLabel() + .should( + 'contain.text', + westendMemberAccount.hidden.expectedSingleMultisig.westEndAddress.slice(0, 6) + ) + }) + + topMenuItems.multiproxySelectorDesktop().should('be.visible').click() + topMenuItems.multiproxySelectorOptionDesktop().should('have.length', 1) + goToHiddenAccountSettings() + settingsPage + .hiddenAccountsContainer() + .should('have.length', 1) + .within(() => { + accountDisplay.identicon().should('be.visible') + accountDisplay + .addressLabel() + .should('contain.text', westendMemberAccount.hidden.expectedPure.address.slice(0, 6)) + settingsPage.hiddenAccountDeleteButton().should('be.visible') + }) + + // hide all accounts and expect an error + addHiddenAccount(westendMemberAccount.hidden.expectedSingleMultisig.westEndAddress) + topMenuItems.multiproxySelectorDesktop().should('not.exist') + topMenuItems.homeButton().click() + landingPage + .noMultisigFoundError() + .should('contain.text', 'No multisig found for your accounts or watched accounts on westend.') + }) + + it('hides accounts per network only', () => { + cy.setupAndVisit({ + url: landingPageNetworkAddress({ + network: 'westend', + address: westendMemberAccount.hidden.expectedPure.address + }), + extensionConnectionAllowed: true, + injectExtensionWithAccounts: [westendMemberAccount.hidden.account], + hiddenAccounts: [ + { network: 'westend', pubKey: westendMemberAccount.hidden.expectedPure.pubKey }, + { network: 'westend', pubKey: westendMemberAccount.hidden.expectedSingleMultisig.pubKey } + ] + }) + + landingPage.linkedAddressNotFound().should('be.visible') + // change network paseo should have 1 multisig + topMenuItems.desktopMenu().within(() => topMenuItems.networkSelector().click()) + topMenuItems.networkSelectorOption('paseo').click() + + topMenuItems.multiproxySelectorDesktop().should('be.visible').click() + topMenuItems + .multiproxySelectorOptionDesktop() + .should('have.length', 1) + .should( + 'contain', + westendMemberAccount.hidden.expectedSingleMultisig.paseoAddress.slice(0, 6) + ) + + // there should be no account in the list + // since it's per network + goToHiddenAccountSettings() + settingsPage.hiddenAccountsContainer().should('not.exist') + }) + + it('can see error when attempting to add same address more than once', () => { + // add an account first + cy.visit(getSettingsPageHiddenAccountUrl()) + addHiddenAccount(randomAddress) + settingsPage.hiddenAccountsContainer().should('have.length', 1) + // attempt to add the same account again + addHiddenAccount(randomAddress) + settingsPage.errorLabel().should('be.visible').should('have.text', 'Account already added') + settingsPage.hiddenAccountsContainer().should('have.length', 1) + settingsPage.addButton().should('be.disabled') + }) + + it('can see error when attempting to add an invalid address', () => { + cy.visit(getSettingsPageHiddenAccountUrl()) + addHiddenAccount('123') + settingsPage.errorLabel().should('be.visible').should('have.text', 'Invalid address') + settingsPage.hiddenAccountsContainer().should('have.length', 0) + settingsPage.addButton().should('be.disabled') + }) +}) diff --git a/packages/ui/cypress/tests/name-edition-display.cy.ts b/packages/ui/cypress/tests/name-edition-display.cy.ts index ae3be2d9..a91de4c7 100644 --- a/packages/ui/cypress/tests/name-edition-display.cy.ts +++ b/packages/ui/cypress/tests/name-edition-display.cy.ts @@ -15,7 +15,7 @@ describe('Name Edition and Display', () => { watchedAccounts: [purePublicKey] }) - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.identicon().should('be.visible') accountDisplay.addressLabel().should('be.visible') accountDisplay.noNameLabel().should('have.text', 'No Name') @@ -32,7 +32,7 @@ describe('Name Edition and Display', () => { watchedAccounts: [purePublicKey] }) - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.nameEditButton().click() accountDisplay.nameEditionInput().should('be.focused') // editing with enter @@ -62,7 +62,7 @@ describe('Name Edition and Display', () => { accountNames: { [purePublicKey]: originalName } }) - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { //edit and cancel with Escape accountDisplay.nameEditButton().click() accountDisplay.nameEditionInput().should('have.value', originalName) diff --git a/packages/ui/cypress/tests/network-switch.cy.ts b/packages/ui/cypress/tests/network-switch.cy.ts index 709383f0..be4091fb 100644 --- a/packages/ui/cypress/tests/network-switch.cy.ts +++ b/packages/ui/cypress/tests/network-switch.cy.ts @@ -26,7 +26,7 @@ describe('Network can be switched', () => { cy.url().should('contain', 'network=paseo') cy.url().should('contain', `address=${multisigPureAddress}`) - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.identicon().should('be.visible') accountDisplay.nameLabel().should('contain', multisigName) accountDisplay.addressLabel().contains(multisigAddress.slice(0, 5)) @@ -45,7 +45,7 @@ describe('Network can be switched', () => { .desktopMenu() .within(() => topMenuItems.multiproxySelectorDesktop().should('not.exist')) - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.identicon().should('be.visible') accountDisplay.nameLabel().should('contain', multisigName) accountDisplay.addressLabel().contains(kusamaAddress.slice(0, 5)) diff --git a/packages/ui/cypress/tests/watched-accounts.cy.ts b/packages/ui/cypress/tests/watched-accounts.cy.ts index 3f376562..cba1f831 100644 --- a/packages/ui/cypress/tests/watched-accounts.cy.ts +++ b/packages/ui/cypress/tests/watched-accounts.cy.ts @@ -14,14 +14,15 @@ import { knownMultisigs } from '../fixtures/knownMultisigs' import { getShortAddress } from '../utils/getShortAddress' const addWatchAccount = (address: string, name?: string) => { - settingsPage.accountAddressInput().type(`${address}{enter}`, { delay: 20, timeout: 8000 }) - - if (name) { - settingsPage.accountNameInput().type(name) - } + settingsPage.watchedAccountsInputsWrapper().within(() => { + settingsPage.accountAddressInput().type(`${address}{enter}`, { delay: 20, timeout: 8000 }) + if (name) { + settingsPage.accountNameInput().type(name) + } - settingsPage.addButton().should('be.enabled') - settingsPage.addButton().click() + settingsPage.addButton().should('be.enabled') + settingsPage.addButton().click() + }) } const { name: testAccountName, address: testAccountAddress } = @@ -32,12 +33,12 @@ describe('Watched Accounts', () => { cy.visit(landingPageUrl) landingPage.watchAccountButton().click() addWatchAccount(testAccountAddress, testAccountName) - settingsPage.accountContainer().should('be.visible') - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().should('be.visible') + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.identicon().should('be.visible') accountDisplay.addressLabel().should('be.visible') accountDisplay.nameLabel().should('be.visible') - settingsPage.accountDeleteButton().should('be.visible') + settingsPage.watchedAccountDeleteButton().should('be.visible') }) }) @@ -45,26 +46,26 @@ describe('Watched Accounts', () => { // add an account first cy.visit(getSettingsPageWatchAccountUrl()) addWatchAccount(testAccountAddress) - settingsPage.accountContainer().should('be.visible') + settingsPage.watchedAccountsContainer().should('be.visible') // now remove it - settingsPage.accountContainer().within(() => { - settingsPage.accountDeleteButton().click() + settingsPage.watchedAccountsContainer().within(() => { + settingsPage.watchedAccountDeleteButton().click() accountDisplay.identicon().should('not.exist') accountDisplay.addressLabel().should('not.exist') }) - settingsPage.accountContainer().should('have.length', 0) + settingsPage.watchedAccountsContainer().should('have.length', 0) }) it('can see error when attempting to add same address more than once', () => { // add an account first cy.visit(getSettingsPageWatchAccountUrl()) addWatchAccount(testAccountAddress) - settingsPage.accountContainer().should('have.length', 1) + settingsPage.watchedAccountsContainer().should('have.length', 1) // attempt to add the same account again addWatchAccount(testAccountAddress) settingsPage.errorLabel().should('be.visible').should('have.text', 'Account already added') - settingsPage.accountContainer().should('have.length', 1) + settingsPage.watchedAccountsContainer().should('have.length', 1) settingsPage.addButton().should('be.disabled') }) @@ -72,7 +73,7 @@ describe('Watched Accounts', () => { cy.visit(getSettingsPageWatchAccountUrl()) addWatchAccount('123') settingsPage.errorLabel().should('be.visible').should('have.text', 'Invalid address') - settingsPage.accountContainer().should('have.length', 0) + settingsPage.watchedAccountsContainer().should('have.length', 0) settingsPage.addButton().should('be.disabled') }) @@ -88,7 +89,7 @@ describe('Watched Accounts', () => { watchedAccounts: [multisigPublicKey] }) // ensure the multisig name is displayed in the settings account container - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.identicon().should('be.visible') accountDisplay.nameLabel().should('be.visible').should('have.text', multisigName) }) @@ -123,7 +124,7 @@ describe('Watched Accounts', () => { watchedAccounts: [purePublicKey] }) // ensure the multisig name is displayed in the settings account container - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.identicon().should('be.visible') accountDisplay.nameLabel().should('be.visible').should('have.text', pureName) }) @@ -172,7 +173,7 @@ describe('Watched Accounts', () => { // navigate to settings and ensure the edited name is displayed topMenuItems.settingsButton().click() settingsPage.watchedAccountsAccordion().click() - settingsPage.accountContainer().within(() => { + settingsPage.watchedAccountsContainer().within(() => { accountDisplay.nameLabel().should('have.text', 'Edited Name Test') }) }) diff --git a/packages/ui/src/contexts/HiddenAccountsContext.tsx b/packages/ui/src/contexts/HiddenAccountsContext.tsx index 6d25f8fe..7fa9f50d 100644 --- a/packages/ui/src/contexts/HiddenAccountsContext.tsx +++ b/packages/ui/src/contexts/HiddenAccountsContext.tsx @@ -12,6 +12,7 @@ import { getPubKeyFromAddress } from '../utils/getPubKeyFromAddress' import { useNetwork } from './NetworkContext' import { HexString } from 'polkadot-api' import { useGetEncodedAddress } from '../hooks/useGetEncodedAddress' +import { useSearchParams } from 'react-router' const LOCALSTORAGE_HIDDEN_ACCOUNTS_KEY = 'multix.hiddenAccounts' @@ -40,6 +41,7 @@ const HiddenAccountsContextProvider = ({ children }: HiddenAccountsProps) => { const { chainInfo } = useApi() const { selectedNetwork } = useNetwork() const getEncodedAddress = useGetEncodedAddress() + const [searchParams, setSearchParams] = useSearchParams({ address: '' }) const networkHiddenAccounts = useMemo(() => { if (!selectedNetwork) return [] @@ -55,6 +57,16 @@ const HiddenAccountsContextProvider = ({ children }: HiddenAccountsProps) => { const addHiddenAccount = useCallback( (address: string) => { const pubKey = getPubKeyFromAddress(address) + const searchParamsAddress = searchParams.get('address') + const urlAddressPubKey = searchParamsAddress && getPubKeyFromAddress(searchParamsAddress) + + // if the currently selected account is being hidden + if (urlAddressPubKey === pubKey) { + setSearchParams((prev) => { + prev.delete('address') + return prev + }) + } selectedNetwork && pubKey && setHiddenAccounts((prev) => [ @@ -62,7 +74,7 @@ const HiddenAccountsContextProvider = ({ children }: HiddenAccountsProps) => { { pubKey, network: selectedNetwork } as HiddenAccount ]) }, - [selectedNetwork] + [searchParams, selectedNetwork, setSearchParams] ) const removeHiddenAccount = useCallback( diff --git a/packages/ui/src/pages/Settings/HiddenAccounts.tsx b/packages/ui/src/pages/Settings/HiddenAccounts.tsx index c8057c0d..b7987900 100644 --- a/packages/ui/src/pages/Settings/HiddenAccounts.tsx +++ b/packages/ui/src/pages/Settings/HiddenAccounts.tsx @@ -55,7 +55,7 @@ const HiddenAccounts = () => { )} - + { )} - +