From cbfeda7bf216a73a4f412b0180c9301e1e63d061 Mon Sep 17 00:00:00 2001 From: Vlad <95026747+NovakVlad2424@users.noreply.github.com> Date: Fri, 3 Feb 2023 13:09:22 +0200 Subject: [PATCH] TW-459: [e2e] Import account by Mnemonic (#852) * [e2e] Import account by Mnemonic * env fix * env fix * env fix * env fix * env fix * env fix * TW-459: [e2e] Import account by Mnemonic. Refactor * TW-459: [e2e] Import account by Mnemonic. Refactor * TW-459: [e2e] Import account by Mnemonic. Refactor + CopyButton selector fix --------- Co-authored-by: alex-seleznov --- .github/workflows/code-quality.yml | 2 ++ .github/workflows/secrets-setup/action.yml | 6 ++++ e2e/.env.dist | 2 ++ e2e/src/classes/browser-context.class.ts | 6 ++++ e2e/src/features/create-new-wallet.feature | 3 +- .../import-account-by-mnemonic.feature | 18 ++++++++++ .../features/import-existing-wallet.feature | 4 +-- e2e/src/features/reveal-private-key.feature | 3 +- e2e/src/features/reveal-seed-phrase.feature | 3 +- e2e/src/page-objects/index.ts | 12 ++++++- e2e/src/page-objects/pages/home.page.ts | 27 ++++++++++++++ .../import-account-mnemonic-tab.page.ts | 27 ++++++++++++++ .../import-account-private-key-tab.page.ts | 13 +++++++ .../import-account-tab-switcher.page.ts | 22 ++++++++++++ .../import-account-watch-only-tab.page.ts | 13 +++++++ .../pages/importing-existing-wallet.page.ts | 13 +++++-- .../pages/verify-mnemonic.page.ts | 36 +++++++++++++++---- e2e/src/step-definitions/common.steps.ts | 9 ++--- .../create-new-wallet.steps.ts | 28 +-------------- .../step-definitions/import-account.steps.ts | 29 +++++++++++++++ .../import-existing-wallet.steps.ts | 8 +++++ e2e/src/utils/search.utils.ts | 34 +++++++++++------- e2e/src/utils/shared-steps.utils.ts | 14 -------- src/app/PageRouter.tsx | 2 +- src/app/atoms/CopyButton.tsx | 3 +- src/app/atoms/TabSwitcher.tsx | 3 ++ src/app/pages/Explore.selectors.ts | 3 +- src/app/pages/Explore/AddressChip.tsx | 9 ++--- src/app/pages/Explore/MainBanner.tsx | 3 +- .../ImportAccount/ImportAccount.test-ids.ts | 13 +++++++ .../{ => ImportAccount}/ImportAccount.tsx | 20 +++++++++-- 31 files changed, 301 insertions(+), 87 deletions(-) create mode 100644 e2e/src/features/import-account-by-mnemonic.feature create mode 100644 e2e/src/page-objects/pages/home.page.ts create mode 100644 e2e/src/page-objects/pages/import-account-tabs/import-account-mnemonic-tab.page.ts create mode 100644 e2e/src/page-objects/pages/import-account-tabs/import-account-private-key-tab.page.ts create mode 100644 e2e/src/page-objects/pages/import-account-tabs/import-account-tab-switcher.page.ts create mode 100644 e2e/src/page-objects/pages/import-account-tabs/import-account-watch-only-tab.page.ts create mode 100644 e2e/src/step-definitions/import-account.steps.ts create mode 100644 e2e/src/step-definitions/import-existing-wallet.steps.ts delete mode 100644 e2e/src/utils/shared-steps.utils.ts create mode 100644 src/app/pages/ImportAccount/ImportAccount.test-ids.ts rename src/app/pages/{ => ImportAccount}/ImportAccount.tsx (97%) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index df483f6f8..210df1769 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -46,6 +46,8 @@ jobs: DEFAULT_SEED_PHRASE: ${{ secrets.DEFAULT_SEED_PHRASE }} DEFAULT_PASSWORD: ${{ secrets.DEFAULT_PASSWORD }} DEFAULT_HD_ACCOUNT_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_PRIVATE_KEY }} + SECOND_SEED_PHRASE: ${{ secrets.SECOND_SEED_PHRASE }} + SECOND_SEED_PHRASE_PRIVATE_KEY: ${{ secrets.SECOND_SEED_PHRASE_PRIVATE_KEY }} - name: Install dependencies and code quality check uses: ./.github/workflows/code-quality-check diff --git a/.github/workflows/secrets-setup/action.yml b/.github/workflows/secrets-setup/action.yml index c089fe33c..e0f79601d 100644 --- a/.github/workflows/secrets-setup/action.yml +++ b/.github/workflows/secrets-setup/action.yml @@ -25,6 +25,10 @@ inputs: required: false DEFAULT_HD_ACCOUNT_PRIVATE_KEY: required: false + SECOND_SEED_PHRASE: + required: false + SECOND_SEED_PHRASE_PRIVATE_KEY: + required: false runs: using: 'composite' @@ -52,4 +56,6 @@ runs: DEFAULT_SEED_PHRASE=${{ inputs.DEFAULT_SEED_PHRASE }} DEFAULT_PASSWORD=${{ inputs.DEFAULT_PASSWORD }} DEFAULT_HD_ACCOUNT_PRIVATE_KEY=${{ inputs.DEFAULT_HD_ACCOUNT_PRIVATE_KEY }} + SECOND_SEED_PHRASE=${{ inputs.SECOND_SEED_PHRASE }} + SECOND_SEED_PHRASE_PRIVATE_KEY=${{ inputs.SECOND_SEED_PHRASE_PRIVATE_KEY }} EOF diff --git a/e2e/.env.dist b/e2e/.env.dist index 1f2cafbb5..5f39c618f 100644 --- a/e2e/.env.dist +++ b/e2e/.env.dist @@ -1,3 +1,5 @@ DEFAULT_SEED_PHRASE= DEFAULT_PASSWORD= DEFAULT_HD_ACCOUNT_PRIVATE_KEY= +SECOND_SEED_PHRASE= +SECOND_SEED_PHRASE_PRIVATE_KEY= diff --git a/e2e/src/classes/browser-context.class.ts b/e2e/src/classes/browser-context.class.ts index e85056863..0ed218b87 100644 --- a/e2e/src/classes/browser-context.class.ts +++ b/e2e/src/classes/browser-context.class.ts @@ -5,10 +5,14 @@ import { getEnv } from '../utils/env.utils'; const defaultSeedPhrase = getEnv('DEFAULT_SEED_PHRASE'); const defaultPassword = getEnv('DEFAULT_PASSWORD'); const defaultPrivateKey = getEnv('DEFAULT_HD_ACCOUNT_PRIVATE_KEY'); +const secondSeedPhrase = getEnv('SECOND_SEED_PHRASE'); +const privateKeyOfSecondSeedPhrase = getEnv('SECOND_SEED_PHRASE_PRIVATE_KEY'); if (!defaultSeedPhrase) throw new Error('process.env.DEFAULT_SEED_PHRASE not found.'); if (!defaultPassword) throw new Error('process.env.DEFAULT_PASSWORD not found.'); if (!defaultPrivateKey) throw new Error('process.env.DEFAULT_PASSWORD not found.'); +if (!secondSeedPhrase) throw new Error('process.env.SECOND_SEED_PHRASE not found.'); +if (!privateKeyOfSecondSeedPhrase) throw new Error('process.env.SECOND_SEED_PHRASE_PRIVATE_KEY not found.'); export class BrowserContext { public static EXTENSION_ID: string; @@ -17,6 +21,8 @@ export class BrowserContext { public static seedPhrase = defaultSeedPhrase; public static password = defaultPassword; public static privateKey = defaultPrivateKey; + public static secondSeedPhrase = secondSeedPhrase; + public static privateKeyOfSecondSeedPhrase = privateKeyOfSecondSeedPhrase; public static resetPrivates = () => { BrowserContext.seedPhrase = defaultSeedPhrase; BrowserContext.password = defaultPassword; diff --git a/e2e/src/features/create-new-wallet.feature b/e2e/src/features/create-new-wallet.feature index 52f1ce799..c3b4a54e7 100644 --- a/e2e/src/features/create-new-wallet.feature +++ b/e2e/src/features/create-new-wallet.feature @@ -1,5 +1,4 @@ Feature: Create a new wallet - @dev Scenario: As a user, i'd like to create a new wallet Given I am on the Welcome page And I press Create New Wallet button on the Welcome page @@ -21,5 +20,5 @@ Feature: Create a new wallet And I press Accept Terms Checkbox on the Register Form page And I press Create Button on the Register Form page - Then I am on the Header page + Then I am on the Home page diff --git a/e2e/src/features/import-account-by-mnemonic.feature b/e2e/src/features/import-account-by-mnemonic.feature new file mode 100644 index 000000000..bfad3d846 --- /dev/null +++ b/e2e/src/features/import-account-by-mnemonic.feature @@ -0,0 +1,18 @@ +Feature: Import Account by Mnemonic +@dev + Scenario: As a user, I'd like to import account by mnemonic + Given I have imported an existing account + + And I press AccountIcon on the Header page + And I am on the AccountsDropdown page + + And I press ImportAccountButton on the AccountsDropdown page + And I am on the ImportAccountTab page + + And I select Mnemonic tab + And I am on the ImportAccountMnemonic page + + And I enter second mnemonic + And I press Mnemonic Import Button on the Import Account(Mnemonic) page + + Then I reveal a private key and compare with private key of second seed phrase diff --git a/e2e/src/features/import-existing-wallet.feature b/e2e/src/features/import-existing-wallet.feature index 061ec1734..00db5bb47 100644 --- a/e2e/src/features/import-existing-wallet.feature +++ b/e2e/src/features/import-existing-wallet.feature @@ -4,7 +4,7 @@ Feature: Import existing wallet And I press Import Existing Wallet button on the Welcome page And I am on the ImportExistingWallet page - And I enter my mnemonic + And I enter default mnemonic And I press Next button on the Import Existing Seed Phrase page And I am on the SetWallet page @@ -14,5 +14,5 @@ Feature: Import existing wallet And I press Accept Terms Checkbox on the Register Form page And I press Import Button on the Register Form page - Then I am on the Header page + Then I am on the Home page diff --git a/e2e/src/features/reveal-private-key.feature b/e2e/src/features/reveal-private-key.feature index 40e7b0708..e9bc7fb60 100644 --- a/e2e/src/features/reveal-private-key.feature +++ b/e2e/src/features/reveal-private-key.feature @@ -13,5 +13,6 @@ Feature: Reveal private key And I am on the RevealSecrets page And I enter password into Reveal Password Field on the RevealSecrets page And I press Reveal Button on the RevealSecrets page - And I compare my Private Key to Revealed value + + Then I compare my Private Key to Revealed value diff --git a/e2e/src/features/reveal-seed-phrase.feature b/e2e/src/features/reveal-seed-phrase.feature index cf575ec73..2ab276c2d 100644 --- a/e2e/src/features/reveal-seed-phrase.feature +++ b/e2e/src/features/reveal-seed-phrase.feature @@ -13,5 +13,6 @@ Feature: Reveal seed phrase And I am on the RevealSecrets page And I enter password into Reveal Password Field on the RevealSecrets page And I press Reveal Button on the RevealSecrets page - And I compare my Seed Phrase to Revealed value + + Then I compare my Seed Phrase to Revealed value diff --git a/e2e/src/page-objects/index.ts b/e2e/src/page-objects/index.ts index ce0b60f46..a7fb774db 100644 --- a/e2e/src/page-objects/index.ts +++ b/e2e/src/page-objects/index.ts @@ -1,5 +1,10 @@ import { AccountsDropdown } from './pages/drop-down-lists/accounts.drop-down'; import { HeaderPage } from './pages/header.page'; +import { HomePage } from './pages/home.page'; +import { ImportAccountMnemonicTab } from './pages/import-account-tabs/import-account-mnemonic-tab.page'; +import { ImportAccountPrivateKeyTab } from './pages/import-account-tabs/import-account-private-key-tab.page'; +import { ImportAccountTab } from './pages/import-account-tabs/import-account-tab-switcher.page'; +import { ImportAccountWatchOnlyTab } from './pages/import-account-tabs/import-account-watch-only-tab.page'; import { ImportExistingWalletPage } from './pages/importing-existing-wallet.page'; import { NewSeedBackupPage } from './pages/new-seed-backup.page'; import { RevealSecretsPage } from './pages/reveal-secrets.page'; @@ -17,5 +22,10 @@ export const Pages = { Settings: new SettingsPage(), RevealSecrets: new RevealSecretsPage(), NewSeedBackup: new NewSeedBackupPage(), - VerifyMnemonic: new VerifyMnemonicPage() + VerifyMnemonic: new VerifyMnemonicPage(), + ImportAccountTab: new ImportAccountTab(), + ImportAccountPrivateKey: new ImportAccountPrivateKeyTab(), + ImportAccountMnemonic: new ImportAccountMnemonicTab(), + ImportAccountWatchOnly: new ImportAccountWatchOnlyTab(), + Home: new HomePage() }; diff --git a/e2e/src/page-objects/pages/home.page.ts b/e2e/src/page-objects/pages/home.page.ts new file mode 100644 index 000000000..0bf7476b0 --- /dev/null +++ b/e2e/src/page-objects/pages/home.page.ts @@ -0,0 +1,27 @@ +import { ExploreSelectors } from '../../../../src/app/pages/Explore.selectors'; +import { Page } from '../../classes/page.class'; +import { createPageElement } from '../../utils/search.utils'; + +export class HomePage extends Page { + ReceiveButton = createPageElement(ExploreSelectors.ReceiveButton); + BuyButton = createPageElement(ExploreSelectors.BuyButton); + SendButton = createPageElement(ExploreSelectors.SendButton); + WithdrawButton = createPageElement(ExploreSelectors.WithdrawButton); + SwapButton = createPageElement(ExploreSelectors.SwapButton); + AssetsTab = createPageElement(ExploreSelectors.AssetsTab); + ActivityTab = createPageElement(ExploreSelectors.ActivityTab); + CollectiblesTab = createPageElement(ExploreSelectors.CollectiblesTab); + PublicAddressButton = createPageElement(ExploreSelectors.PublicAddressButton); + + async isVisible() { + await this.ReceiveButton.waitForDisplayed(); + await this.BuyButton.waitForDisplayed(); + await this.SendButton.waitForDisplayed(); + await this.WithdrawButton.waitForDisplayed(); + await this.SwapButton.waitForDisplayed(); + await this.AssetsTab.waitForDisplayed(); + await this.ActivityTab.waitForDisplayed(); + await this.CollectiblesTab.waitForDisplayed(); + await this.PublicAddressButton.waitForDisplayed(); + } +} diff --git a/e2e/src/page-objects/pages/import-account-tabs/import-account-mnemonic-tab.page.ts b/e2e/src/page-objects/pages/import-account-tabs/import-account-mnemonic-tab.page.ts new file mode 100644 index 000000000..48eb65a6d --- /dev/null +++ b/e2e/src/page-objects/pages/import-account-tabs/import-account-mnemonic-tab.page.ts @@ -0,0 +1,27 @@ +import { ImportAccountTestIds } from '../../../../../src/app/pages/ImportAccount/ImportAccount.test-ids'; +import { Page } from '../../../classes/page.class'; +import { createPageElement, findElements } from '../../../utils/search.utils'; + +export class ImportAccountMnemonicTab extends Page { + mnemonicWordInput = createPageElement(ImportAccountTestIds.mnemonicWordInput); + mnemonicPasswordField = createPageElement(ImportAccountTestIds.mnemonicPasswordField); + mnemonicImportButton = createPageElement(ImportAccountTestIds.mnemonicImportButton); + + async isVisible() { + await this.mnemonicWordInput.waitForDisplayed(); + await this.mnemonicPasswordField.waitForDisplayed(); + await this.mnemonicImportButton.waitForDisplayed(); + } + + async enterSeedPhrase(seedPhrase: string) { + const wordsArray = seedPhrase.split(' '); + const wordsInputs = await findElements(ImportAccountTestIds.mnemonicWordInput); + + for (let i = 0; i < wordsArray.length; i++) { + const word = wordsArray[i]; + const input = wordsInputs[i]; + + await input.type(word); + } + } +} diff --git a/e2e/src/page-objects/pages/import-account-tabs/import-account-private-key-tab.page.ts b/e2e/src/page-objects/pages/import-account-tabs/import-account-private-key-tab.page.ts new file mode 100644 index 000000000..cc1f33b02 --- /dev/null +++ b/e2e/src/page-objects/pages/import-account-tabs/import-account-private-key-tab.page.ts @@ -0,0 +1,13 @@ +import { ImportAccountTestIds } from '../../../../../src/app/pages/ImportAccount/ImportAccount.test-ids'; +import { Page } from '../../../classes/page.class'; +import { createPageElement } from '../../../utils/search.utils'; + +export class ImportAccountPrivateKeyTab extends Page { + privateKeyInput = createPageElement(ImportAccountTestIds.privateKeyInput); + privateKeyImportButton = createPageElement(ImportAccountTestIds.privateKeyImportButton); + + async isVisible() { + await this.privateKeyInput.waitForDisplayed(); + await this.privateKeyImportButton.waitForDisplayed(); + } +} diff --git a/e2e/src/page-objects/pages/import-account-tabs/import-account-tab-switcher.page.ts b/e2e/src/page-objects/pages/import-account-tabs/import-account-tab-switcher.page.ts new file mode 100644 index 000000000..c9b295ea0 --- /dev/null +++ b/e2e/src/page-objects/pages/import-account-tabs/import-account-tab-switcher.page.ts @@ -0,0 +1,22 @@ +import { ImportAccountTestIds } from '../../../../../src/app/pages/ImportAccount/ImportAccount.test-ids'; +import { Page } from '../../../classes/page.class'; +import { createPageElement, findElements, getElementText } from '../../../utils/search.utils'; + +export class ImportAccountTab extends Page { + tabSwitcher = createPageElement(ImportAccountTestIds.tabSwitcher); + + async isVisible() { + await this.tabSwitcher.waitForDisplayed(); + } + async selectTab(tabName: string) { + const tabElements = await findElements(ImportAccountTestIds.tabSwitcher); + + for (const tabElement of tabElements) { + const getTabValue = await getElementText(tabElement); + + if (getTabValue === tabName) { + await tabElement.click(); + } + } + } +} diff --git a/e2e/src/page-objects/pages/import-account-tabs/import-account-watch-only-tab.page.ts b/e2e/src/page-objects/pages/import-account-tabs/import-account-watch-only-tab.page.ts new file mode 100644 index 000000000..151cbdd83 --- /dev/null +++ b/e2e/src/page-objects/pages/import-account-tabs/import-account-watch-only-tab.page.ts @@ -0,0 +1,13 @@ +import { ImportAccountTestIds } from '../../../../../src/app/pages/ImportAccount/ImportAccount.test-ids'; +import { Page } from '../../../classes/page.class'; +import { createPageElement } from '../../../utils/search.utils'; + +export class ImportAccountWatchOnlyTab extends Page { + watchOnlyInput = createPageElement(ImportAccountTestIds.watchOnlyInput); + watchOnlyImportButton = createPageElement(ImportAccountTestIds.watchOnlyImportButton); + + async isVisible() { + await this.watchOnlyInput.waitForDisplayed(); + await this.watchOnlyImportButton.waitForDisplayed(); + } +} diff --git a/e2e/src/page-objects/pages/importing-existing-wallet.page.ts b/e2e/src/page-objects/pages/importing-existing-wallet.page.ts index bd03180a8..63764a79a 100644 --- a/e2e/src/page-objects/pages/importing-existing-wallet.page.ts +++ b/e2e/src/page-objects/pages/importing-existing-wallet.page.ts @@ -10,7 +10,16 @@ export class ImportExistingWalletPage extends Page { await this.nextButton.waitForDisplayed(); await this.wordInput.waitForDisplayed(); } - async getWordsInputs() { - return findElements(ImportFromSeedPhraseTestIds.wordInput); + + async enterSeedPhrase(seedPhrase: string) { + const wordsArray = seedPhrase.split(' '); + const wordsInputs = await findElements(ImportFromSeedPhraseTestIds.wordInput); + + for (let i = 0; i < wordsArray.length; i++) { + const word = wordsArray[i]; + const input = wordsInputs[i]; + + await input.type(word); + } } } diff --git a/e2e/src/page-objects/pages/verify-mnemonic.page.ts b/e2e/src/page-objects/pages/verify-mnemonic.page.ts index 3c95351bf..284f2c37a 100644 --- a/e2e/src/page-objects/pages/verify-mnemonic.page.ts +++ b/e2e/src/page-objects/pages/verify-mnemonic.page.ts @@ -1,6 +1,7 @@ import { NewSeedVerifyTestIds } from '../../../../src/app/pages/NewWallet/create/NewSeedVerify/NewSeedVerify.test-ids'; +import { BrowserContext } from '../../classes/browser-context.class'; import { Page } from '../../classes/page.class'; -import { createPageElement, findElements } from '../../utils/search.utils'; +import { createPageElement, findElements, getElementText } from '../../utils/search.utils'; export class VerifyMnemonicPage extends Page { nextButton = createPageElement(NewSeedVerifyTestIds.nextButton); @@ -10,10 +11,33 @@ export class VerifyMnemonicPage extends Page { await this.nextButton.waitForDisplayed(); await this.firstMnemonicInput.waitForDisplayed(); } - async getWordNumberSpans() { - return findElements(NewSeedVerifyTestIds.mnemonicWordNumber); - } - async getWordsInputs() { - return findElements(NewSeedVerifyTestIds.firstMnemonicInput); + + async enterSeedPhraseVerification() { + const wordNumberSpans = await findElements(NewSeedVerifyTestIds.mnemonicWordNumber); + const wordNumberTexts = await Promise.all(wordNumberSpans.map(item => getElementText(item))); + + const wordNumbers = wordNumberTexts.map(fullText => { + const numberText = fullText.split(' ')[1]; + + return Number(numberText); + }); + + const wordInputs = await findElements(NewSeedVerifyTestIds.firstMnemonicInput); + const wordInputTexts = await Promise.all(wordInputs.map(item => getElementText(item))); + const emptyWordInputIndexes = wordInputTexts + .map((text, index) => { + if (text) return undefined; + return index; + }) + .filter(index => index !== undefined) as number[]; + + for (const index of emptyWordInputIndexes) { + const input = wordInputs[index]; + + const wordIndex = wordNumbers[index] - 1; + const word = BrowserContext.seedPhrase.split(' ')[wordIndex]; + + await input.type(word); + } } } diff --git a/e2e/src/step-definitions/common.steps.ts b/e2e/src/step-definitions/common.steps.ts index 9897f6ec1..650d58ddd 100644 --- a/e2e/src/step-definitions/common.steps.ts +++ b/e2e/src/step-definitions/common.steps.ts @@ -4,7 +4,6 @@ import { BrowserContext } from '../classes/browser-context.class'; import { Pages } from '../page-objects'; import { getInputText } from '../utils/input.utils'; import { createPageElement } from '../utils/search.utils'; -import { enterMyMnemonicStep } from '../utils/shared-steps.utils'; import { LONG_TIMEOUT } from '../utils/timing.utils'; Given(/^I am on the (\w+) page$/, async (page: keyof typeof Pages) => { @@ -15,10 +14,6 @@ Given(/I press (.*) on the (.*) page/, async (elementName: string, pageName: str await createPageElement(`${pageName}/${elementName}`).click(); }); -Given(/I enter my mnemonic/, async () => { - await enterMyMnemonicStep(); -}); - Given( /I enter (seed|password) into (.*) on the (.*) page/, async (inputType: string, elementName: string, pageName: string) => { @@ -33,7 +28,7 @@ Given(/I have imported an existing account/, { timeout: LONG_TIMEOUT }, async () await Pages.Welcome.importExistingWalletButton.click(); await Pages.ImportExistingWallet.isVisible(); - await enterMyMnemonicStep(); + await Pages.ImportExistingWallet.enterSeedPhrase(BrowserContext.seedPhrase); await Pages.ImportExistingWallet.nextButton.click(); await Pages.SetWallet.isVisible(); @@ -43,5 +38,5 @@ Given(/I have imported an existing account/, { timeout: LONG_TIMEOUT }, async () await Pages.SetWallet.acceptTerms.click(); await Pages.SetWallet.importButton.click(); - await Pages.Header.isVisible(); + await Pages.Home.isVisible(); }); diff --git a/e2e/src/step-definitions/create-new-wallet.steps.ts b/e2e/src/step-definitions/create-new-wallet.steps.ts index c2f9f61f9..54ca28ca0 100644 --- a/e2e/src/step-definitions/create-new-wallet.steps.ts +++ b/e2e/src/step-definitions/create-new-wallet.steps.ts @@ -3,7 +3,6 @@ import { expect } from 'chai'; import { BrowserContext } from '../classes/browser-context.class'; import { Pages } from '../page-objects'; -import { getElementText } from '../utils/search.utils'; Given(/I save my mnemonic/, async () => { const value = await Pages.NewSeedBackup.seedPhraseValue.getText(); @@ -12,30 +11,5 @@ Given(/I save my mnemonic/, async () => { }); Given(/I verify my mnemonic/, async () => { - const wordNumberSpans = await Pages.VerifyMnemonic.getWordNumberSpans(); - const wordNumberTexts = await Promise.all(wordNumberSpans.map(item => getElementText(item))); - - const wordNumbers = wordNumberTexts.map(fullText => { - const numberText = fullText.split(' ')[1]; - - return Number(numberText); - }); - - const wordInputs = await Pages.VerifyMnemonic.getWordsInputs(); - const wordInputTexts = await Promise.all(wordInputs.map(item => getElementText(item))); - const emptyWordInputIndexes = wordInputTexts - .map((text, index) => { - if (text) return undefined; - return index; - }) - .filter(index => index !== undefined) as number[]; - - for (const index of emptyWordInputIndexes) { - const input = wordInputs[index]; - - const wordIndex = wordNumbers[index] - 1; - const word = BrowserContext.seedPhrase.split(' ')[wordIndex]; - - await input.type(word); - } + await Pages.VerifyMnemonic.enterSeedPhraseVerification(); }); diff --git a/e2e/src/step-definitions/import-account.steps.ts b/e2e/src/step-definitions/import-account.steps.ts new file mode 100644 index 000000000..d8f0ab6b6 --- /dev/null +++ b/e2e/src/step-definitions/import-account.steps.ts @@ -0,0 +1,29 @@ +import { Given } from '@cucumber/cucumber'; +import { expect } from 'chai'; + +import { BrowserContext } from '../classes/browser-context.class'; +import { Pages } from '../page-objects'; + +Given(/I enter second mnemonic/, async () => { + await Pages.ImportAccountMnemonic.enterSeedPhrase(BrowserContext.secondSeedPhrase); +}); + +Given(/I select (.*) tab/, async (tabName: string) => { + await Pages.ImportAccountTab.selectTab(tabName); +}); + +Given(/I reveal a private key and compare with private key of second seed phrase/, async () => { + await Pages.Home.isVisible(); + await Pages.Header.accountIconButton.click(); + await Pages.AccountsDropdown.isVisible(); + await Pages.AccountsDropdown.settingsButton.click(); + await Pages.Settings.isVisible(); + await Pages.Settings.revealPrivateKeyButton.click(); + await Pages.RevealSecrets.isVisible(); + await Pages.RevealSecrets.revealPasswordField.type(BrowserContext.password); + await Pages.RevealSecrets.revealButton.click(); + await Pages.RevealSecrets.revealSecretsValue.getText(); + const revealedSecretsValue = await Pages.RevealSecrets.revealSecretsValue.getText(); + + expect(revealedSecretsValue).eql(BrowserContext.privateKeyOfSecondSeedPhrase); +}); diff --git a/e2e/src/step-definitions/import-existing-wallet.steps.ts b/e2e/src/step-definitions/import-existing-wallet.steps.ts new file mode 100644 index 000000000..51da3a6f2 --- /dev/null +++ b/e2e/src/step-definitions/import-existing-wallet.steps.ts @@ -0,0 +1,8 @@ +import { Given } from '@cucumber/cucumber'; + +import { BrowserContext } from '../classes/browser-context.class'; +import { Pages } from '../page-objects'; + +Given(/I enter default mnemonic/, async () => { + await Pages.ImportExistingWallet.enterSeedPhrase(BrowserContext.seedPhrase); +}); diff --git a/e2e/src/utils/search.utils.ts b/e2e/src/utils/search.utils.ts index 5730d3d3f..46e1dd324 100644 --- a/e2e/src/utils/search.utils.ts +++ b/e2e/src/utils/search.utils.ts @@ -5,7 +5,7 @@ import { BrowserContext } from '../classes/browser-context.class'; const getSelector = (testID: string) => `[data-testid="${testID}"]`; -export const findElement = async (testID: string) => { +const findElement = async (testID: string) => { const selector = getSelector(testID); const element = await BrowserContext.page.waitForSelector(selector, { visible: true, timeout: 5000 }); @@ -29,22 +29,30 @@ export const findElements = async (testID: string) => { throw new Error(`None of "${testID}" elements where found found`); }; -export const createPageElement = (testID: string) => ({ - waitForDisplayed: async () => findElement(testID), - click: async () => { - const element = await findElement(testID); +class PageElement { + constructor(public testID: string) {} + + findElement() { + return findElement(this.testID); + } + waitForDisplayed() { + return this.findElement(); + } + async click() { + const element = await this.findElement(); await element.click(); - }, - type: async (text: string) => { - const element = await findElement(testID); + } + async type(text: string) { + const element = await this.findElement(); await element.type(text); - }, - getText: async () => { - const element = await findElement(testID); - + } + async getText() { + const element = await this.findElement(); return getElementText(element); } -}); +} + +export const createPageElement = (testID: string) => new PageElement(testID); export const getElementText = (element: ElementHandle) => element.evaluate(innerElement => { diff --git a/e2e/src/utils/shared-steps.utils.ts b/e2e/src/utils/shared-steps.utils.ts deleted file mode 100644 index 75679570f..000000000 --- a/e2e/src/utils/shared-steps.utils.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { BrowserContext } from '../classes/browser-context.class'; -import { Pages } from '../page-objects'; - -export const enterMyMnemonicStep = async () => { - const wordsArray = BrowserContext.seedPhrase.split(' '); - const wordsInputs = await Pages.ImportExistingWallet.getWordsInputs(); - - for (let i = 0; i < wordsArray.length; i++) { - const word = wordsArray[i]; - const input = wordsInputs[i]; - - await input.type(word); - } -}; diff --git a/src/app/PageRouter.tsx b/src/app/PageRouter.tsx index ca1c15895..d6aad5a6e 100644 --- a/src/app/PageRouter.tsx +++ b/src/app/PageRouter.tsx @@ -9,7 +9,7 @@ import CreateAccount from 'app/pages/CreateAccount'; import DApps from 'app/pages/DApps'; import Delegate from 'app/pages/Delegate'; import Explore from 'app/pages/Explore'; -import ImportAccount from 'app/pages/ImportAccount'; +import ImportAccount from 'app/pages/ImportAccount/ImportAccount'; import ManageAssets from 'app/pages/ManageAssets'; import { CreateWallet } from 'app/pages/NewWallet/CreateWallet'; import { ImportWallet } from 'app/pages/NewWallet/ImportWallet'; diff --git a/src/app/atoms/CopyButton.tsx b/src/app/atoms/CopyButton.tsx index 588911fc2..0caa40f78 100644 --- a/src/app/atoms/CopyButton.tsx +++ b/src/app/atoms/CopyButton.tsx @@ -2,7 +2,7 @@ import React, { FC, HTMLAttributes, useMemo } from 'react'; import classNames from 'clsx'; -import { AnalyticsEventCategory, TestIDProps, useAnalytics } from 'lib/analytics'; +import { AnalyticsEventCategory, setTestID, TestIDProps, useAnalytics } from 'lib/analytics'; import { t } from 'lib/i18n'; import useCopyToClipboard from 'lib/ui/useCopyToClipboard'; import useTippy from 'lib/ui/useTippy'; @@ -96,6 +96,7 @@ const CopyButton: FC = ({ } {...rest} onClick={handleCopyPress} + {...setTestID(testID)} > {children} diff --git a/src/app/atoms/TabSwitcher.tsx b/src/app/atoms/TabSwitcher.tsx index db1ca1c8f..fb47543d9 100644 --- a/src/app/atoms/TabSwitcher.tsx +++ b/src/app/atoms/TabSwitcher.tsx @@ -5,6 +5,8 @@ import classNames from 'clsx'; import { TID, T } from 'lib/i18n'; import { Link } from 'lib/woozie'; +import { ImportAccountTestIds } from '../pages/ImportAccount/ImportAccount.test-ids'; + type TabDescriptor = { slug: string; i18nKey: TID; @@ -33,6 +35,7 @@ export const TabSwitcher: React.FC = ({ className, tabs, activ active ? 'text-primary-orange bg-primary-orange bg-opacity-10' : 'hover:bg-gray-100 focus:bg-gray-100', 'transition ease-in-out duration-200' )} + testID={ImportAccountTestIds.tabSwitcher} > diff --git a/src/app/pages/Explore.selectors.ts b/src/app/pages/Explore.selectors.ts index 8115ac264..fa3400500 100644 --- a/src/app/pages/Explore.selectors.ts +++ b/src/app/pages/Explore.selectors.ts @@ -8,5 +8,6 @@ export enum ExploreSelectors { DelegationTab = 'Explore/DelegationTab', ActivityTab = 'Explore/ActivityTab', AboutTab = 'Explore/AboutTab', - CollectiblesTab = 'Explore/CollectiblesTab' + CollectiblesTab = 'Explore/CollectiblesTab', + PublicAddressButton = 'Explore/Public Address Button' } diff --git a/src/app/pages/Explore/AddressChip.tsx b/src/app/pages/Explore/AddressChip.tsx index 657af4cac..eb9c0ad45 100644 --- a/src/app/pages/Explore/AddressChip.tsx +++ b/src/app/pages/Explore/AddressChip.tsx @@ -6,15 +6,16 @@ import useSWR from 'swr'; import { ReactComponent as HashIcon } from 'app/icons/hash.svg'; import { ReactComponent as LanguageIcon } from 'app/icons/language.svg'; import HashChip from 'app/templates/HashChip'; +import { TestIDProps } from 'lib/analytics'; import { useTezos, useTezosDomainsClient, fetchFromStorage, putToStorage } from 'lib/temple/front'; -type AddressChipProps = { +type AddressChipProps = TestIDProps & { pkh: string; className?: string; small?: boolean; }; -const AddressChip: FC = ({ pkh, className, small }) => { +const AddressChip: FC = ({ pkh, className, small, ...rest }) => { const tezos = useTezos(); const { resolver: domainsResolver } = useTezosDomainsClient(); @@ -53,9 +54,9 @@ const AddressChip: FC = ({ pkh, className, small }) => { return (
{reverseName && domainDisplayed ? ( - + ) : ( - + )} {reverseName && ( diff --git a/src/app/pages/Explore/MainBanner.tsx b/src/app/pages/Explore/MainBanner.tsx index f62661891..c3d661a38 100644 --- a/src/app/pages/Explore/MainBanner.tsx +++ b/src/app/pages/Explore/MainBanner.tsx @@ -20,6 +20,7 @@ import { useTotalBalance } from 'lib/temple/front/use-total-balance.hook'; import { getAssetName, getAssetSymbol } from 'lib/temple/metadata'; import useTippy from 'lib/ui/useTippy'; +import { ExploreSelectors } from '../Explore.selectors'; import AddressChip from './AddressChip'; interface Props { @@ -44,7 +45,7 @@ interface TotalVolumeBannerProps { const TotalVolumeBanner: FC = ({ accountPkh }) => (
- +
); diff --git a/src/app/pages/ImportAccount/ImportAccount.test-ids.ts b/src/app/pages/ImportAccount/ImportAccount.test-ids.ts new file mode 100644 index 000000000..833adc5d8 --- /dev/null +++ b/src/app/pages/ImportAccount/ImportAccount.test-ids.ts @@ -0,0 +1,13 @@ +export enum ImportAccountTestIds { + tabSwitcher = 'Import Account/Tabs', + + privateKeyInput = 'Import Account(Private Key)/Private Key Input', + privateKeyImportButton = 'Import Account(Private Key)/Private Key Import Button', + + mnemonicWordInput = 'Import Account(Mnemonic)/Mnemonic Word Input', + mnemonicPasswordField = 'Import Account(Mnemonic)/Mnemonic Password Field', + mnemonicImportButton = 'Import Account(Mnemonic)/Mnemonic Import Button', + + watchOnlyInput = 'Import Account(Watch-Only)/Watch Only Input', + watchOnlyImportButton = 'Import Account(Watch-Only)/Watch Only Import Button' +} diff --git a/src/app/pages/ImportAccount.tsx b/src/app/pages/ImportAccount/ImportAccount.tsx similarity index 97% rename from src/app/pages/ImportAccount.tsx rename to src/app/pages/ImportAccount/ImportAccount.tsx index 3de0c5ba2..188ef1c97 100644 --- a/src/app/pages/ImportAccount.tsx +++ b/src/app/pages/ImportAccount/ImportAccount.tsx @@ -41,6 +41,8 @@ import { useSafeState } from 'lib/ui/hooks'; import { clearClipboard } from 'lib/ui/util'; import { navigate } from 'lib/woozie'; +import { ImportAccountTestIds } from './ImportAccount.test-ids'; + type ImportAccountProps = { tabSlug: string | null; }; @@ -195,6 +197,7 @@ const ByPrivateKeyForm: FC = () => { className="resize-none" containerClassName="mb-6" onPaste={() => clearClipboard()} + testID={ImportAccountTestIds.privateKeyInput} /> {encrypted && ( @@ -218,7 +221,9 @@ const ByPrivateKeyForm: FC = () => { /> )} - {t('importAccount')} + + {t('importAccount')} + ); }; @@ -309,6 +314,7 @@ const ByMnemonicForm: FC = () => { setSeedError={setSeedError} onChange={setSeedPhrase} reset={reset} + testID={ImportAccountTestIds.mnemonicWordInput} />
@@ -408,9 +414,14 @@ const ByMnemonicForm: FC = () => { placeholder="*********" errorCaption={errors.password?.message} containerClassName="mb-6" + testID={ImportAccountTestIds.mnemonicPasswordField} /> - + @@ -832,6 +843,7 @@ const WatchOnlyForm: FC = () => { onClean={cleanAddressField} id="watch-address" label={t('address')} + testID={ImportAccountTestIds.watchOnlyInput} labelDescription={ } @@ -850,7 +862,9 @@ const WatchOnlyForm: FC = () => { )} - {t('importAccount')} + + {t('importAccount')} + ); };