From aebb4022f3bb254a7eb474d0079446ca6d05d673 Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Mon, 26 Jun 2023 16:36:43 +0300 Subject: [PATCH 01/14] remove account --- e2e/.env.dist | 2 + e2e/package.json | 1 + .../import-account-by-public-key.feature | 2 +- e2e/src/features/remove-account.feature | 97 +++++++++++++++++++ e2e/src/page-objects/index.ts | 4 +- .../page-objects/pages/remove-account.page.ts | 14 +++ e2e/src/step-definitions/shared.steps.ts | 18 ++++ e2e/src/utils/env.utils.ts | 2 + 8 files changed, 138 insertions(+), 2 deletions(-) create mode 100644 e2e/src/features/remove-account.feature create mode 100644 e2e/src/page-objects/pages/remove-account.page.ts diff --git a/e2e/.env.dist b/e2e/.env.dist index 847306a7e..de49cf5ec 100644 --- a/e2e/.env.dist +++ b/e2e/.env.dist @@ -1,11 +1,13 @@ DEFAULT_HD_ACCOUNT_SEED_PHRASE= DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY= +DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM= DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY= DEFAULT_PASSWORD= IMPORTED_HD_ACCOUNT_SEED_PHRASE= IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY= +IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM= WATCH_ONLY_PUBLIC_KEY_HASH= WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM= diff --git a/e2e/package.json b/e2e/package.json index 78f1caf4e..5a3591572 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -6,6 +6,7 @@ "scripts": { "start": "./node_modules/.bin/cucumber-js --fail-fast", "start:dev": "yarn start --tags @dev", + "start:remove_account": "yarn start --tags @remove_account", "test": "yarn start --exit --tags 'not @dev'", "ts": "tsc --pretty" }, diff --git a/e2e/src/features/import-account-by-public-key.feature b/e2e/src/features/import-account-by-public-key.feature index 6e1125053..3520d69ed 100644 --- a/e2e/src/features/import-account-by-public-key.feature +++ b/e2e/src/features/import-account-by-public-key.feature @@ -15,4 +15,4 @@ Feature: Import an account by public key (Watch-only) And I enter watchOnlyPublicKey into Watch Only Input on the Import Account(Watch-Only) page And I press Watch Only Import Button on the Import Account(Watch-Only) page - Then I compare my Watch-only Public hash with imported account + Then I check if watchOnlyAccountShortHash is corresponded to the selected account diff --git a/e2e/src/features/remove-account.feature b/e2e/src/features/remove-account.feature new file mode 100644 index 000000000..076858102 --- /dev/null +++ b/e2e/src/features/remove-account.feature @@ -0,0 +1,97 @@ +Feature: Remove an account + @remove_account + Scenario: As a user, I'd like to remove an imported account by mnemonic, private key and public key + Given I have imported an existing account +# Remove an imported account by mnemonic + + And I press Account Icon on the Header page + And I am on the AccountsDropdown page + + And I press Import Account Button on the Account Drop-down 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 + + And I am on the Home page + And I check if importedAccountShortHash is corresponded to the selected account + And I press Account Icon on the Header page + + And I am on the AccountsDropdown page + And I press Settings Button on the Account Drop-down page + + And I am on the Settings page + And I press Remove Account Button on the Settings page + + And I am on the RemoveAccount page + And I enter defaultPassword into Password Input on the Remove Account page + And I press Remove Button on the Remove Account page + + And I am on the Home page + And I check if defaultAccountShortHash is corresponded to the selected account + +# Remove an imported account private key + + And I press Account Icon on the Header page + And I am on the AccountsDropdown page + + And I press Import Account Button on the Account Drop-down page + And I am on the ImportAccountTab page + + And I select Private Key tab + And I am on the ImportAccountPrivateKey page + + And I enter importedFirstPrivateKey into Private Key Input on the Import Account(Private Key) page + And I press Private Key Import Button on the Import Account(Private Key) page + + And I am on the Home page + And I check if importedAccountShortHash is corresponded to the selected account + And I press Account Icon on the Header page + + And I am on the AccountsDropdown page + And I press Settings Button on the Account Drop-down page + + And I am on the Settings page + And I press Remove Account Button on the Settings page + + And I am on the RemoveAccount page + And I enter defaultPassword into Password Input on the Remove Account page + And I press Remove Button on the Remove Account page + + And I am on the Home page + And I check if defaultAccountShortHash is corresponded to the selected account + +# Remove an imported account public key + + And I press Account Icon on the Header page + And I am on the AccountsDropdown page + + And I press Import Account Button on the Account Drop-down page + And I am on the ImportAccountTab page + + And I select Watch-only tab + And I am on the ImportAccountWatchOnly page + + And I enter watchOnlyPublicKey into Watch Only Input on the Import Account(Watch-Only) page + And I press Watch Only Import Button on the Import Account(Watch-Only) page + + And I check if watchOnlyAccountShortHash is corresponded to the selected account + + And I press Account Icon on the Header page + + And I am on the AccountsDropdown page + And I press Settings Button on the Account Drop-down page + + And I am on the Settings page + And I press Remove Account Button on the Settings page + + And I am on the RemoveAccount page + And I enter defaultPassword into Password Input on the Remove Account page + And I press Remove Button on the Remove Account page + + And I am on the Home page + + Then I check if defaultAccountShortHash is corresponded to the selected account diff --git a/e2e/src/page-objects/index.ts b/e2e/src/page-objects/index.ts index cfbe43921..5dc82424c 100644 --- a/e2e/src/page-objects/index.ts +++ b/e2e/src/page-objects/index.ts @@ -1,4 +1,5 @@ import { NetworksDropDown } from 'e2e/src/page-objects/pages/drop-down-lists/networks.drop-down'; +import { RemoveAccountPage } from 'e2e/src/page-objects/pages/remove-account.page'; import { SwapPage } from 'e2e/src/page-objects/pages/swap.page'; import { OperationStatusAlert } from './pages/alerts/operation-status.alert'; @@ -47,5 +48,6 @@ export const Pages = { OperationStatusAlert: new OperationStatusAlert(), Send: new SendPage(), NetworksDropDown: new NetworksDropDown(), - Swap: new SwapPage() + Swap: new SwapPage(), + RemoveAccount: new RemoveAccountPage() }; diff --git a/e2e/src/page-objects/pages/remove-account.page.ts b/e2e/src/page-objects/pages/remove-account.page.ts new file mode 100644 index 000000000..70f187a02 --- /dev/null +++ b/e2e/src/page-objects/pages/remove-account.page.ts @@ -0,0 +1,14 @@ +import { RemoveAccountSelectors } from 'src/app/templates/RemoveAccount/RemoveAccount.selectors'; + +import { Page } from '../../classes/page.class'; +import { createPageElement } from '../../utils/search.utils'; + +export class RemoveAccountPage extends Page { + passwordInput = createPageElement(RemoveAccountSelectors.passwordInput); + removeButton = createPageElement(RemoveAccountSelectors.removeButton); + + async isVisible() { + await this.passwordInput.waitForDisplayed(); + await this.removeButton.waitForDisplayed(); + } +} diff --git a/e2e/src/step-definitions/shared.steps.ts b/e2e/src/step-definitions/shared.steps.ts index d67b511e8..a6613175b 100644 --- a/e2e/src/step-definitions/shared.steps.ts +++ b/e2e/src/step-definitions/shared.steps.ts @@ -4,6 +4,7 @@ import { OperationStatusSelectors } from 'src/app/templates/OperationStatus.sele import { BrowserContext } from '../classes/browser-context.class'; import { Pages } from '../page-objects'; +import { envVars } from '../utils/env.utils'; import { iComparePrivateKeys } from '../utils/input-data.utils'; import { LONG_TIMEOUT, MEDIUM_TIMEOUT } from '../utils/timing.utils'; @@ -33,3 +34,20 @@ Given(/I'm waiting for 'success ✓' operation status/, { timeout: LONG_TIMEOUT timeout: LONG_TIMEOUT }); }); + +const hashObject = { + defaultAccountShortHash: envVars.DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM, + importedAccountShortHash: envVars.IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM, + watchOnlyAccountShortHash: envVars.WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM +}; + +Given( + /I check if (.*) is corresponded to the selected account/, + { timeout: MEDIUM_TIMEOUT }, + async (hashType: keyof typeof hashObject) => { + const getPublicHash = await Pages.Home.PublicAddressButton.getText(); + const hashValue = hashObject[hashType]; + + expect(getPublicHash).eql(hashValue); + } +); diff --git a/e2e/src/utils/env.utils.ts b/e2e/src/utils/env.utils.ts index d8427ec43..b28f1b4dd 100644 --- a/e2e/src/utils/env.utils.ts +++ b/e2e/src/utils/env.utils.ts @@ -7,10 +7,12 @@ export const getEnv = (key: string) => process.env[key] ?? ''; export const envVars = { DEFAULT_HD_ACCOUNT_SEED_PHRASE: getEnv('DEFAULT_HD_ACCOUNT_SEED_PHRASE'), DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY: getEnv('DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY'), + DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: getEnv('DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM'), DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY: getEnv('DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY'), DEFAULT_PASSWORD: getEnv('DEFAULT_PASSWORD'), IMPORTED_HD_ACCOUNT_SEED_PHRASE: getEnv('IMPORTED_HD_ACCOUNT_SEED_PHRASE'), IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY: getEnv('IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY'), + IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: getEnv('IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM'), WATCH_ONLY_PUBLIC_KEY_HASH: getEnv('WATCH_ONLY_PUBLIC_KEY_HASH'), WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: getEnv('WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM') }; From 8811af7ab677cf23baaf52ba71ca87cd06914e17 Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Tue, 27 Jun 2023 12:07:22 +0300 Subject: [PATCH 02/14] remove account negative --- e2e/src/features/remove-account.feature | 11 ++++++++++- src/app/atoms/Alert.selectors.ts | 3 +++ src/app/atoms/Alert.tsx | 12 +++++++++++- src/app/atoms/ErrorCaption.selectors.ts | 3 +++ src/app/atoms/FormField.tsx | 5 ++++- 5 files changed, 31 insertions(+), 3 deletions(-) create mode 100644 src/app/atoms/Alert.selectors.ts create mode 100644 src/app/atoms/ErrorCaption.selectors.ts diff --git a/e2e/src/features/remove-account.feature b/e2e/src/features/remove-account.feature index 076858102..ae2879329 100644 --- a/e2e/src/features/remove-account.feature +++ b/e2e/src/features/remove-account.feature @@ -1,6 +1,6 @@ Feature: Remove an account @remove_account - Scenario: As a user, I'd like to remove an imported account by mnemonic, private key and public key + Scenario: [Positive] As a user, I'd like to remove an imported account by mnemonic, private key and public key Given I have imported an existing account # Remove an imported account by mnemonic @@ -95,3 +95,12 @@ Feature: Remove an account And I am on the Home page Then I check if defaultAccountShortHash is corresponded to the selected account + + + + @remove_account + Scenario: [Negative] As a user, I'd like to check validations and another errors on the "Remove Account" page + Given I have imported an existing account + + + diff --git a/src/app/atoms/Alert.selectors.ts b/src/app/atoms/Alert.selectors.ts new file mode 100644 index 000000000..c27bd1c50 --- /dev/null +++ b/src/app/atoms/Alert.selectors.ts @@ -0,0 +1,3 @@ +export enum AlertSelectors { + alertTitle = 'Alert/Alert title Text' +} diff --git a/src/app/atoms/Alert.tsx b/src/app/atoms/Alert.tsx index 11de900e0..9ac4a2cbd 100644 --- a/src/app/atoms/Alert.tsx +++ b/src/app/atoms/Alert.tsx @@ -3,8 +3,11 @@ import React, { FC, HTMLAttributes, ReactNode, useEffect, useRef } from 'react'; import classNames from 'clsx'; import { ReactComponent as CloseIcon } from 'app/icons/close.svg'; +import { setTestID } from 'lib/analytics'; import { t } from 'lib/i18n'; +import { AlertSelectors } from './Alert.selectors'; + type AlertProps = Omit, 'title'> & { type?: 'success' | 'warn' | 'error' | 'delegate'; title?: ReactNode; @@ -61,7 +64,14 @@ export const Alert: FC = ({ aria-label={t('alert')} {...rest} > - {title &&

{title}

} + {title && ( +

+ {title} +

+ )} {description && (
= ({ errorCaption }) => { const isPasswordStrengthIndicator = errorCaption === PASSWORD_ERROR_CAPTION; return errorCaption && !isPasswordStrengthIndicator ? ( -
{errorCaption}
+
+ {errorCaption} +
) : null; }; From 91ac0cd2a32ef7a7fdcca7ee69ebb5b8241d2060 Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Tue, 27 Jun 2023 16:32:11 +0300 Subject: [PATCH 03/14] github workflow. environment updated --- .github/workflows/code-quality.yml | 2 ++ .github/workflows/secrets-setup/action.yml | 8 +++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index d2ade783e..41f5ba531 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -46,10 +46,12 @@ jobs: TEMPLE_WALLET_DEXES_API_URL: ${{ vars.TEMPLE_WALLET_DEXES_API_URL }} DEFAULT_HD_ACCOUNT_SEED_PHRASE: ${{ secrets.DEFAULT_HD_ACCOUNT_SEED_PHRASE }} DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY }} + DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: ${{ secrets.DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY }} DEFAULT_PASSWORD: ${{ secrets.DEFAULT_PASSWORD }} IMPORTED_HD_ACCOUNT_SEED_PHRASE: ${{ secrets.IMPORTED_HD_ACCOUNT_SEED_PHRASE }} IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY: ${{ secrets.IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY }} + IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: ${{ secrets.IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }} TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }} WATCH_ONLY_PUBLIC_KEY_HASH: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH }} diff --git a/.github/workflows/secrets-setup/action.yml b/.github/workflows/secrets-setup/action.yml index 7503f7d1d..41560df1d 100644 --- a/.github/workflows/secrets-setup/action.yml +++ b/.github/workflows/secrets-setup/action.yml @@ -25,14 +25,18 @@ inputs: required: false DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY: required: false + DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: + required: false DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY: required: false DEFAULT_PASSWORD: - required: false + required: false IMPORTED_HD_ACCOUNT_SEED_PHRASE: required: false IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY: required: false + IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: + required: false TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: required: true TEMPLE_WALLET_MOONPAY_API_KEY: @@ -70,10 +74,12 @@ runs: cat << EOF > e2e/.env DEFAULT_HD_ACCOUNT_SEED_PHRASE=${{ inputs.DEFAULT_HD_ACCOUNT_SEED_PHRASE }} DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY=${{ inputs.DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY }} + DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM=${{ inputs.DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY=${{ inputs.DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY }} DEFAULT_PASSWORD=${{ inputs.DEFAULT_PASSWORD }} IMPORTED_HD_ACCOUNT_SEED_PHRASE=${{ inputs.IMPORTED_HD_ACCOUNT_SEED_PHRASE }} IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY=${{ inputs.IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY }} + IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM=${{ inputs.IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} WATCH_ONLY_PUBLIC_KEY_HASH=${{ inputs.WATCH_ONLY_PUBLIC_KEY_HASH }} WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM=${{ inputs.WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM }} EOF From fcca882de56674496711429f4f677b373f83f0e4 Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Thu, 29 Jun 2023 13:28:15 +0300 Subject: [PATCH 04/14] second stage --- e2e/src/features/remove-account.feature | 12 +++++++++++- e2e/src/page-objects/pages/remove-account.page.ts | 10 ++++++++-- e2e/src/step-definitions/shared.steps.ts | 13 +++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/e2e/src/features/remove-account.feature b/e2e/src/features/remove-account.feature index ae2879329..0816a58e0 100644 --- a/e2e/src/features/remove-account.feature +++ b/e2e/src/features/remove-account.feature @@ -1,5 +1,5 @@ Feature: Remove an account - @remove_account +# @remove_account Scenario: [Positive] As a user, I'd like to remove an imported account by mnemonic, private key and public key Given I have imported an existing account # Remove an imported account by mnemonic @@ -102,5 +102,15 @@ Feature: Remove an account Scenario: [Negative] As a user, I'd like to check validations and another errors on the "Remove Account" page Given I have imported an existing account + And I press Account Icon on the Header page + + And I am on the AccountsDropdown page + And I press Settings Button on the Account Drop-down page + + And I am on the Settings page + And I press Remove Account Button on the Settings page + + And I am on the RemoveAccount page + And I got the 'Cannot be removed' error with Alert title Text element on the Alert page diff --git a/e2e/src/page-objects/pages/remove-account.page.ts b/e2e/src/page-objects/pages/remove-account.page.ts index 70f187a02..b8031e195 100644 --- a/e2e/src/page-objects/pages/remove-account.page.ts +++ b/e2e/src/page-objects/pages/remove-account.page.ts @@ -1,3 +1,4 @@ +import { AlertSelectors } from 'src/app/atoms/Alert.selectors'; import { RemoveAccountSelectors } from 'src/app/templates/RemoveAccount/RemoveAccount.selectors'; import { Page } from '../../classes/page.class'; @@ -6,9 +7,14 @@ import { createPageElement } from '../../utils/search.utils'; export class RemoveAccountPage extends Page { passwordInput = createPageElement(RemoveAccountSelectors.passwordInput); removeButton = createPageElement(RemoveAccountSelectors.removeButton); + alertWarning = createPageElement(AlertSelectors.alertTitle); async isVisible() { - await this.passwordInput.waitForDisplayed(); - await this.removeButton.waitForDisplayed(); + try { + await this.alertWarning.waitForDisplayed(5000); + } catch (error) { + await this.removeButton.waitForDisplayed(); + await this.passwordInput.waitForDisplayed(); + } } } diff --git a/e2e/src/step-definitions/shared.steps.ts b/e2e/src/step-definitions/shared.steps.ts index a6613175b..7a25de5cc 100644 --- a/e2e/src/step-definitions/shared.steps.ts +++ b/e2e/src/step-definitions/shared.steps.ts @@ -2,6 +2,8 @@ import { Given } from '@cucumber/cucumber'; import { expect } from 'chai'; import { OperationStatusSelectors } from 'src/app/templates/OperationStatus.selectors'; +import { createPageElement } from 'e2e/src/utils/search.utils'; + import { BrowserContext } from '../classes/browser-context.class'; import { Pages } from '../page-objects'; import { envVars } from '../utils/env.utils'; @@ -51,3 +53,14 @@ Given( expect(getPublicHash).eql(hashValue); } ); + +Given( + /I got the '(.*)' error with (.*) element on the (.*) page/, + { timeout: MEDIUM_TIMEOUT }, + async (errorName: string, elementName: string, pageName: string) => { + await createPageElement(`${pageName}/${elementName}`).waitForDisplayed(); + const getErrorContent = await createPageElement(`${pageName}/${elementName}`).getText(); + + expect(getErrorContent).eql(errorName); + } +); From ad875e5380bbafbcef4f93f532c76201365e6c38 Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Thu, 29 Jun 2023 17:20:31 +0300 Subject: [PATCH 05/14] Negative scenario --- e2e/src/features/remove-account.feature | 33 +++++++++++++++++++++++-- e2e/src/utils/input-data.utils.ts | 8 ++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/e2e/src/features/remove-account.feature b/e2e/src/features/remove-account.feature index 0816a58e0..db1667de9 100644 --- a/e2e/src/features/remove-account.feature +++ b/e2e/src/features/remove-account.feature @@ -33,7 +33,7 @@ Feature: Remove an account And I am on the Home page And I check if defaultAccountShortHash is corresponded to the selected account -# Remove an imported account private key +# Remove an imported account by private key And I press Account Icon on the Header page And I am on the AccountsDropdown page @@ -64,7 +64,7 @@ Feature: Remove an account And I am on the Home page And I check if defaultAccountShortHash is corresponded to the selected account -# Remove an imported account public key +# Remove an imported account by public key And I press Account Icon on the Header page And I am on the AccountsDropdown page @@ -113,4 +113,33 @@ Feature: Remove an account And I am on the RemoveAccount page And I got the 'Cannot be removed' error with Alert title Text element on the Alert page + And I press Account Icon on the Header page + + And I am on the AccountsDropdown page + And I press Import Account Button on the Account Drop-down 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 + + And I am on the Home page + And I check if importedAccountShortHash is corresponded to the selected account + And I press Account Icon on the Header page + + And I am on the AccountsDropdown page + And I press Settings Button on the Account Drop-down page + + And I am on the Settings page + And I press Remove Account Button on the Settings page + + And I am on the RemoveAccount page + And I press Remove Button on the Remove Account page + And I got the 'Required' error with Input Error element on the Universal Component page + + And I enter randomContent into Password Input on the Remove Account page + And I press Remove Button on the Remove Account page + And I got the 'Invalid password' error with Input Error element on the Universal Component page diff --git a/e2e/src/utils/input-data.utils.ts b/e2e/src/utils/input-data.utils.ts index 1bf768a43..d114ce6b0 100644 --- a/e2e/src/utils/input-data.utils.ts +++ b/e2e/src/utils/input-data.utils.ts @@ -5,12 +5,20 @@ export const iComparePrivateKeys = { importedFirstPrivateKey: envVars.IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY }; +const generateRandomContent = () => { + const wordsArray = ['apple', 'banana', 'carrot', 'dog', 'elephant', 'fish', 'grape', 'hat', 'ice cream', 'jungle']; + const randomWord = Math.floor(Math.random() * wordsArray.length); + + return wordsArray[randomWord] + Math.floor(Math.random() * 10000).toString(); +}; + export const iEnterValues = { ...iComparePrivateKeys, defaultSeedPhrase: envVars.DEFAULT_HD_ACCOUNT_SEED_PHRASE, defaultPassword: envVars.DEFAULT_PASSWORD, watchOnlyPublicKey: envVars.WATCH_ONLY_PUBLIC_KEY_HASH, bakerAddress: '', + randomContent: generateRandomContent(), // For transactions amount_0_0001: '0.0001', From f0e64e5837474118f916fa8d0094df2a205ad897 Mon Sep 17 00:00:00 2001 From: Vlad <95026747+NovakVlad2424@users.noreply.github.com> Date: Mon, 3 Jul 2023 12:32:33 +0300 Subject: [PATCH 06/14] Update e2e/src/step-definitions/shared.steps.ts Co-authored-by: Alex --- e2e/src/step-definitions/shared.steps.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/e2e/src/step-definitions/shared.steps.ts b/e2e/src/step-definitions/shared.steps.ts index a6613175b..9275bcc7c 100644 --- a/e2e/src/step-definitions/shared.steps.ts +++ b/e2e/src/step-definitions/shared.steps.ts @@ -45,9 +45,9 @@ Given( /I check if (.*) is corresponded to the selected account/, { timeout: MEDIUM_TIMEOUT }, async (hashType: keyof typeof hashObject) => { - const getPublicHash = await Pages.Home.PublicAddressButton.getText(); - const hashValue = hashObject[hashType]; + const pkhFromUI = await Pages.Home.PublicAddressButton.getText(); + const targetPkh = hashObject[hashType]; - expect(getPublicHash).eql(hashValue); + expect(pkhFromUI).eql(targetPkh); } ); From 1ff49e36778cfc7dba4e1bbad79edd2f410c00ec Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Tue, 4 Jul 2023 13:38:01 +0300 Subject: [PATCH 07/14] create with edited name + negative --- e2e/package.json | 1 + .../create-or-restore-an-account.feature | 34 +++++++++++++++++-- e2e/src/features/remove-account.feature | 2 +- e2e/src/page-objects/pages/home.page.ts | 2 ++ .../create-or-restore-account.steps.ts | 17 ++++++++++ e2e/src/utils/input-data.utils.ts | 3 +- src/app/pages/Home/Home.selectors.ts | 3 +- .../Home/OtherComponents/EditableTitle.tsx | 7 +++- 8 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 e2e/src/step-definitions/create-or-restore-account.steps.ts diff --git a/e2e/package.json b/e2e/package.json index 5a3591572..9744b2d51 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -7,6 +7,7 @@ "start": "./node_modules/.bin/cucumber-js --fail-fast", "start:dev": "yarn start --tags @dev", "start:remove_account": "yarn start --tags @remove_account", + "start:@create_account": "yarn start --tags @create_account", "test": "yarn start --exit --tags 'not @dev'", "ts": "tsc --pretty" }, diff --git a/e2e/src/features/create-or-restore-an-account.feature b/e2e/src/features/create-or-restore-an-account.feature index 8b8769c1c..7f4131177 100644 --- a/e2e/src/features/create-or-restore-an-account.feature +++ b/e2e/src/features/create-or-restore-an-account.feature @@ -1,6 +1,6 @@ Feature: Create or restore an account - - Scenario: As a user, I'd like to create or restore an account +#@create_account + Scenario: [Positive] As a user, I'd like to create or restore an account Given I have imported an existing account And I press Account Icon on the Header page @@ -15,3 +15,33 @@ Feature: Create or restore an account Then I reveal a private key and compare with defaultSecondPrivateKey + + @create_account + Scenario: [Negative] As a user, I'd like to create or restore an account with edited name + validation errors + Given I have imported an existing account + + And I press Account Icon on the Header page + And I am on the AccountsDropdown page + + And I press Create Or Restore Account Button on the Account Drop-down page + And I am on the CreateOrRestoreAnAccount page + + And I clear Account Name Input value on the Create Account page + And I enter shortRandomContent into Account Name Input on the Create Account page + And I press Create Or Restore Button on the Create Account page + + And I am on the Home page + And I check if shortRandomContent is edited name for created account + + # check validation error + And I press Account Icon on the Header page + And I am on the AccountsDropdown page + + And I press Create Or Restore Account Button on the Account Drop-down page + And I am on the CreateOrRestoreAnAccount page + + And I clear Account Name Input value on the Create Account page + And I enter longRandomContent into Account Name Input on the Create Account page + And I press Create Or Restore Button on the Create Account page + + Then I got the 'Invalid name. It should be: 1-16 characters, without special' error with Input Error element on the Universal Component page diff --git a/e2e/src/features/remove-account.feature b/e2e/src/features/remove-account.feature index 7d9090087..1c5911edd 100644 --- a/e2e/src/features/remove-account.feature +++ b/e2e/src/features/remove-account.feature @@ -139,7 +139,7 @@ Feature: Remove an account And I press Remove Button on the Remove Account page And I got the 'Required' error with Input Error element on the Universal Component page - And I enter randomContent into Password Input on the Remove Account page + And I enter shortRandomContent into Password Input on the Remove Account page And I press Remove Button on the Remove Account page And I got the 'Invalid password' error with Input Error element on the Universal Component page diff --git a/e2e/src/page-objects/pages/home.page.ts b/e2e/src/page-objects/pages/home.page.ts index 1085f19e2..7331d16d1 100644 --- a/e2e/src/page-objects/pages/home.page.ts +++ b/e2e/src/page-objects/pages/home.page.ts @@ -13,6 +13,7 @@ export class HomePage extends Page { ActivityTab = createPageElement(HomeSelectors.activityTab); CollectiblesTab = createPageElement(HomeSelectors.collectiblesTab); PublicAddressButton = createPageElement(HomeSelectors.publicAddressButton); + accountNameText = createPageElement(HomeSelectors.accountNameText); async isVisible() { await this.ReceiveButton.waitForDisplayed(); @@ -24,5 +25,6 @@ export class HomePage extends Page { await this.ActivityTab.waitForDisplayed(); await this.CollectiblesTab.waitForDisplayed(); await this.PublicAddressButton.waitForDisplayed(); + await this.accountNameText.waitForDisplayed(); } } diff --git a/e2e/src/step-definitions/create-or-restore-account.steps.ts b/e2e/src/step-definitions/create-or-restore-account.steps.ts new file mode 100644 index 000000000..22ff0b60b --- /dev/null +++ b/e2e/src/step-definitions/create-or-restore-account.steps.ts @@ -0,0 +1,17 @@ +import { Given } from '@cucumber/cucumber'; +import { expect } from 'chai'; + +import { iEnterValues } from 'e2e/src/utils/input-data.utils'; +import { MEDIUM_TIMEOUT } from 'e2e/src/utils/timing.utils'; + +import { Pages } from '../page-objects'; + +Given( + /I check if (.*) is edited name for created account/, + { timeout: MEDIUM_TIMEOUT }, + async (editedName: keyof typeof iEnterValues) => { + const nameOfCreatedAccount = await Pages.Home.accountNameText.getText(); + + expect(nameOfCreatedAccount).eql(iEnterValues[editedName]); + } +); diff --git a/e2e/src/utils/input-data.utils.ts b/e2e/src/utils/input-data.utils.ts index d114ce6b0..6303e6713 100644 --- a/e2e/src/utils/input-data.utils.ts +++ b/e2e/src/utils/input-data.utils.ts @@ -18,7 +18,8 @@ export const iEnterValues = { defaultPassword: envVars.DEFAULT_PASSWORD, watchOnlyPublicKey: envVars.WATCH_ONLY_PUBLIC_KEY_HASH, bakerAddress: '', - randomContent: generateRandomContent(), + shortRandomContent: generateRandomContent(), + longRandomContent: 'long random content for test + ' + generateRandomContent(), // For transactions amount_0_0001: '0.0001', diff --git a/src/app/pages/Home/Home.selectors.ts b/src/app/pages/Home/Home.selectors.ts index 9f7c5aeed..d53dcbd95 100644 --- a/src/app/pages/Home/Home.selectors.ts +++ b/src/app/pages/Home/Home.selectors.ts @@ -13,5 +13,6 @@ export enum HomeSelectors { fiatTezSwitchButton = 'Home/Fiat-Tez Switch Button', notificationIconButton = 'Home/Notification Icon Button', delegateButton = 'Home/Delegate Button', - addressModeSwitchButton = 'Home/Address Mode Switch Button' + addressModeSwitchButton = 'Home/Address Mode Switch Button', + accountNameText = 'Home/Account Name Text' } diff --git a/src/app/pages/Home/OtherComponents/EditableTitle.tsx b/src/app/pages/Home/OtherComponents/EditableTitle.tsx index 2db5ccd35..641771930 100644 --- a/src/app/pages/Home/OtherComponents/EditableTitle.tsx +++ b/src/app/pages/Home/OtherComponents/EditableTitle.tsx @@ -11,6 +11,7 @@ import { T, t } from 'lib/i18n'; import { useTempleClient, useAccount } from 'lib/temple/front'; import { useAlert } from 'lib/ui/dialog'; +import { HomeSelectors } from '../Home.selectors'; import { EditableTitleSelectors } from './EditableTitle.selectors'; const buttonClassNames = [ @@ -138,7 +139,11 @@ const EditableTitle: FC = () => { ) : ( <> - + {account.name} From bb004026e42864fc81c2bf28c0218d562b9b594e Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Wed, 5 Jul 2023 16:01:48 +0300 Subject: [PATCH 08/14] turn on tag --- e2e/src/features/create-or-restore-an-account.feature | 3 ++- e2e/src/step-definitions/swap.steps.ts | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/e2e/src/features/create-or-restore-an-account.feature b/e2e/src/features/create-or-restore-an-account.feature index 7f4131177..caff39941 100644 --- a/e2e/src/features/create-or-restore-an-account.feature +++ b/e2e/src/features/create-or-restore-an-account.feature @@ -1,5 +1,5 @@ Feature: Create or restore an account -#@create_account + @create_account Scenario: [Positive] As a user, I'd like to create or restore an account Given I have imported an existing account @@ -20,6 +20,7 @@ Feature: Create or restore an account Scenario: [Negative] As a user, I'd like to create or restore an account with edited name + validation errors Given I have imported an existing account + # create an account with edited name And I press Account Icon on the Header page And I am on the AccountsDropdown page diff --git a/e2e/src/step-definitions/swap.steps.ts b/e2e/src/step-definitions/swap.steps.ts index 7f3bae077..2cd753b9f 100644 --- a/e2e/src/step-definitions/swap.steps.ts +++ b/e2e/src/step-definitions/swap.steps.ts @@ -8,8 +8,6 @@ import { iSelectTokenSlugs } from 'e2e/src/utils/input-data.utils'; import { createPageElement } from 'e2e/src/utils/search.utils'; import { MEDIUM_TIMEOUT, VERY_LONG_TIMEOUT } from 'e2e/src/utils/timing.utils'; -import { BrowserContext } from '../classes/browser-context.class'; - Given( /I select (.*) token in the token drop-down list on the Swap page/, { timeout: VERY_LONG_TIMEOUT }, From 6bcc2836f89a89e05c93ec2d0db830267f01a7ce Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Fri, 7 Jul 2023 13:16:43 +0300 Subject: [PATCH 09/14] 1) add contact: positive. 2) Analytics disabled --- .github/workflows/code-quality.yml | 2 ++ .github/workflows/secrets-setup/action.yml | 6 ++++ e2e/.env.dist | 3 ++ e2e/package.json | 3 +- e2e/src/features/address-book.feature | 18 ++++++++++ e2e/src/features/create-new-wallet.feature | 1 + .../features/import-existing-wallet.feature | 1 + e2e/src/page-objects/index.ts | 4 ++- .../page-objects/pages/address-book.page.ts | 33 +++++++++++++++++++ e2e/src/page-objects/pages/send.page.ts | 2 ++ .../pages/setWalletPassword.page.ts | 2 ++ .../step-definitions/address-book.steps.ts | 22 +++++++++++++ e2e/src/step-definitions/common.steps.ts | 1 + e2e/src/utils/env.utils.ts | 4 ++- e2e/src/utils/input-data.utils.ts | 1 + .../SetWalletPassword.selectors.ts | 1 + .../setWalletPassword/SetWalletPassword.tsx | 1 + .../AddressBook/AddressBook.selectors.ts | 5 ++- src/app/templates/AddressBook/AddressBook.tsx | 9 ++++- .../SendForm/ContactsDropdownItem.tsx | 6 +++- src/app/templates/SendForm/selectors.ts | 1 + 21 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 e2e/src/features/address-book.feature create mode 100644 e2e/src/page-objects/pages/address-book.page.ts create mode 100644 e2e/src/step-definitions/address-book.steps.ts diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 5ab3bee03..36dfc5241 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -57,6 +57,8 @@ jobs: TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }} WATCH_ONLY_PUBLIC_KEY_HASH: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH }} WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM }} + CONTACT_ADDRESS_PUBLIC_KEY_HASH: ${{ secrets.CONTACT_ADDRESS_PUBLIC_KEY_HASH }} + CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM: ${{ secrets.CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM }} - 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 2c9e7b2d6..e435e1564 100644 --- a/.github/workflows/secrets-setup/action.yml +++ b/.github/workflows/secrets-setup/action.yml @@ -47,6 +47,10 @@ inputs: required: false WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: required: false + CONTACT_ADDRESS_PUBLIC_KEY_HASH: + required: false + CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM: + required: false runs: using: 'composite' @@ -85,4 +89,6 @@ runs: IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM=${{ inputs.IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} WATCH_ONLY_PUBLIC_KEY_HASH=${{ inputs.WATCH_ONLY_PUBLIC_KEY_HASH }} WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM=${{ inputs.WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM }} + CONTACT_ADDRESS_PUBLIC_KEY_HASH=${{ inputs.CONTACT_ADDRESS_PUBLIC_KEY_HASH }} + CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM=${{ inputs.CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM }} EOF diff --git a/e2e/.env.dist b/e2e/.env.dist index de49cf5ec..da5019432 100644 --- a/e2e/.env.dist +++ b/e2e/.env.dist @@ -11,3 +11,6 @@ IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM= WATCH_ONLY_PUBLIC_KEY_HASH= WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM= + +CONTACT_ADDRESS_PUBLIC_KEY_HASH= +CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM= diff --git a/e2e/package.json b/e2e/package.json index 9744b2d51..e58bff95c 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -7,7 +7,8 @@ "start": "./node_modules/.bin/cucumber-js --fail-fast", "start:dev": "yarn start --tags @dev", "start:remove_account": "yarn start --tags @remove_account", - "start:@create_account": "yarn start --tags @create_account", + "start:create_account": "yarn start --tags @create_account", + "start:address_book": "yarn start --tags @address_book", "test": "yarn start --exit --tags 'not @dev'", "ts": "tsc --pretty" }, diff --git a/e2e/src/features/address-book.feature b/e2e/src/features/address-book.feature new file mode 100644 index 000000000..aee78bbfc --- /dev/null +++ b/e2e/src/features/address-book.feature @@ -0,0 +1,18 @@ +Feature: Address book +@address_book + Scenario: As a user, I'd like to add contact + Given I have imported an existing account + + And I press Account Icon on the Header page + And I am on the AccountsDropdown page + And I press Settings Button on the Account Drop-down page + + And I am on the Settings page + And I press Address Book Button on the Settings page + + And I am on the AddressBook page + And I enter contactPublicKey into Address Input on the Address Book page + And I enter shortRandomContent into Name Input on the Address Book page + And I press Add Contact Button on the Address Book page + + Then I check if added contact is added and displayed diff --git a/e2e/src/features/create-new-wallet.feature b/e2e/src/features/create-new-wallet.feature index 5b5eeb13a..2d9939361 100644 --- a/e2e/src/features/create-new-wallet.feature +++ b/e2e/src/features/create-new-wallet.feature @@ -17,6 +17,7 @@ Feature: Create a new wallet And I am on the SetWallet page And I enter defaultPassword into Password Field on the Register Form page And I enter defaultPassword into Repeat Password Field on the Register Form page + And I press Analytics Check Box on the Register Form page And I press Skip Onboarding Checkbox on the Register Form page And I press Accept Terms Checkbox on the Register Form page And I press Create Button on the Register Form page diff --git a/e2e/src/features/import-existing-wallet.feature b/e2e/src/features/import-existing-wallet.feature index 32c9d9715..466c9fb00 100644 --- a/e2e/src/features/import-existing-wallet.feature +++ b/e2e/src/features/import-existing-wallet.feature @@ -12,6 +12,7 @@ Feature: Import existing wallet And I enter defaultPassword into Password Field on the Register Form page And I enter defaultPassword into Repeat Password Field on the Register Form page And I press Skip Onboarding Checkbox on the Register Form page + And I press Analytics Check Box on the Register Form page And I press Accept Terms Checkbox on the Register Form page And I press Import Button on the Register Form page diff --git a/e2e/src/page-objects/index.ts b/e2e/src/page-objects/index.ts index 5528fba78..3f1e81647 100644 --- a/e2e/src/page-objects/index.ts +++ b/e2e/src/page-objects/index.ts @@ -1,3 +1,4 @@ +import { AddressBookPage } from 'e2e/src/page-objects/pages/address-book.page'; import { NetworksDropDown } from 'e2e/src/page-objects/pages/drop-down-lists/networks.drop-down'; import { NewsletterModalPage } from 'e2e/src/page-objects/pages/newsletter-modal.page'; import { OnRumModalPage } from 'e2e/src/page-objects/pages/on-rum-modal.page'; @@ -53,5 +54,6 @@ export const Pages = { OnRumpModal: new OnRumModalPage(), NewsletterModal: new NewsletterModalPage(), Swap: new SwapPage(), - RemoveAccount: new RemoveAccountPage() + RemoveAccount: new RemoveAccountPage(), + AddressBook: new AddressBookPage() }; diff --git a/e2e/src/page-objects/pages/address-book.page.ts b/e2e/src/page-objects/pages/address-book.page.ts new file mode 100644 index 000000000..11600f4af --- /dev/null +++ b/e2e/src/page-objects/pages/address-book.page.ts @@ -0,0 +1,33 @@ +import { AddressBookSelectors } from 'src/app/templates/AddressBook/AddressBook.selectors'; + +import { Page } from '../../classes/page.class'; +import { createPageElement, findElement } from '../../utils/search.utils'; + +export class AddressBookPage extends Page { + addressInput = createPageElement(AddressBookSelectors.addressInput); + nameInput = createPageElement(AddressBookSelectors.nameInput); + addContactButton = createPageElement(AddressBookSelectors.addContactButton); + contactItem = createPageElement(AddressBookSelectors.contactItem); + deleteContactButton = createPageElement(AddressBookSelectors.deleteContactButton); + contactOwnLabelText = createPageElement(AddressBookSelectors.contactOwnLabelText); + + async isVisible() { + await this.addressInput.waitForDisplayed(); + await this.nameInput.waitForDisplayed(); + await this.addContactButton.waitForDisplayed(); + await this.contactItem.waitForDisplayed(); + await this.contactOwnLabelText.waitForDisplayed(); + } + + async isContactAdded(hash: string) { + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject(new Error(`The contact with address: '${hash}' was not found within 5 seconds.`)); + }, 5000); + }); + + const findElementPromise = findElement(AddressBookSelectors.contactItem, { hash }); + + return Promise.race([findElementPromise, timeoutPromise]); + } +} diff --git a/e2e/src/page-objects/pages/send.page.ts b/e2e/src/page-objects/pages/send.page.ts index 23ce3392e..c951137db 100644 --- a/e2e/src/page-objects/pages/send.page.ts +++ b/e2e/src/page-objects/pages/send.page.ts @@ -9,6 +9,8 @@ export class SendPage extends Page { amountInput = createPageElement(SendFormSelectors.amountInput); recipientInput = createPageElement(SendFormSelectors.recipientInput); sendButton = createPageElement(SendFormSelectors.sendButton); + contactItemButton = createPageElement(SendFormSelectors.contactItemButton); + contactHashValue = createPageElement(SendFormSelectors.contactHashValue); async isVisible() { await this.assetDropDown.waitForDisplayed(); diff --git a/e2e/src/page-objects/pages/setWalletPassword.page.ts b/e2e/src/page-objects/pages/setWalletPassword.page.ts index b4fbd0e50..d642c728b 100644 --- a/e2e/src/page-objects/pages/setWalletPassword.page.ts +++ b/e2e/src/page-objects/pages/setWalletPassword.page.ts @@ -6,6 +6,7 @@ import { createPageElement } from '../../utils/search.utils'; export class setWalletPage extends Page { passwordField = createPageElement(setWalletPasswordSelectors.passwordField); repeatPasswordField = createPageElement(setWalletPasswordSelectors.repeatPasswordField); + analyticsCheckbox = createPageElement(setWalletPasswordSelectors.analyticsCheckBox); skipOnboarding = createPageElement(setWalletPasswordSelectors.skipOnboardingCheckbox); acceptTerms = createPageElement(setWalletPasswordSelectors.acceptTermsCheckbox); importButton = createPageElement(setWalletPasswordSelectors.importButton); @@ -13,6 +14,7 @@ export class setWalletPage extends Page { async isVisible() { await this.passwordField.waitForDisplayed(); await this.repeatPasswordField.waitForDisplayed(); + await this.analyticsCheckbox.waitForDisplayed(); await this.skipOnboarding.waitForDisplayed(); await this.acceptTerms.waitForDisplayed(); } diff --git a/e2e/src/step-definitions/address-book.steps.ts b/e2e/src/step-definitions/address-book.steps.ts new file mode 100644 index 000000000..770dba4d8 --- /dev/null +++ b/e2e/src/step-definitions/address-book.steps.ts @@ -0,0 +1,22 @@ +import { Given } from '@cucumber/cucumber'; +import { expect } from 'chai'; + +import { Pages } from 'e2e/src/page-objects'; +import { envVars } from 'e2e/src/utils/env.utils'; +import { MEDIUM_TIMEOUT } from 'e2e/src/utils/timing.utils'; + +Given(/I check if added contact is added and displayed/, { timeout: MEDIUM_TIMEOUT }, async () => { + // Checking if added contact is displaying 'Current contacts' list + await Pages.AddressBook.isContactAdded(envVars.CONTACT_ADDRESS_PUBLIC_KEY_HASH); + + // Checking if added contact is displayed in the 'Recipient' drop-down on the Send Page + await Pages.Header.templeLogoButton.click(); + await Pages.Home.isVisible(); + await Pages.Home.SendButton.click(); + await Pages.Send.isVisible(); + await Pages.Send.recipientInput.click(); + await Pages.Send.contactItemButton.waitForDisplayed(); + + const contactHashValue = await Pages.Send.contactHashValue.getText(); + expect(contactHashValue).eql(envVars.CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM); +}); diff --git a/e2e/src/step-definitions/common.steps.ts b/e2e/src/step-definitions/common.steps.ts index 131f03599..c297ad820 100644 --- a/e2e/src/step-definitions/common.steps.ts +++ b/e2e/src/step-definitions/common.steps.ts @@ -44,6 +44,7 @@ Given(/I have imported an existing account/, { timeout: LONG_TIMEOUT }, async () await Pages.SetWallet.isVisible(); await Pages.SetWallet.passwordField.type(BrowserContext.password); await Pages.SetWallet.repeatPasswordField.type(BrowserContext.password); + await Pages.SetWallet.analyticsCheckbox.click(); await Pages.SetWallet.skipOnboarding.click(); await Pages.SetWallet.acceptTerms.click(); await Pages.SetWallet.importButton.click(); diff --git a/e2e/src/utils/env.utils.ts b/e2e/src/utils/env.utils.ts index b28f1b4dd..ba7d14a95 100644 --- a/e2e/src/utils/env.utils.ts +++ b/e2e/src/utils/env.utils.ts @@ -14,7 +14,9 @@ export const envVars = { IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY: getEnv('IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY'), IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: getEnv('IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM'), WATCH_ONLY_PUBLIC_KEY_HASH: getEnv('WATCH_ONLY_PUBLIC_KEY_HASH'), - WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: getEnv('WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM') + WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: getEnv('WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM'), + CONTACT_ADDRESS_PUBLIC_KEY_HASH: getEnv('CONTACT_ADDRESS_PUBLIC_KEY_HASH'), + CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM: getEnv('CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM') }; Object.entries(envVars).forEach(([key, val]) => { diff --git a/e2e/src/utils/input-data.utils.ts b/e2e/src/utils/input-data.utils.ts index 6303e6713..5ba269221 100644 --- a/e2e/src/utils/input-data.utils.ts +++ b/e2e/src/utils/input-data.utils.ts @@ -17,6 +17,7 @@ export const iEnterValues = { defaultSeedPhrase: envVars.DEFAULT_HD_ACCOUNT_SEED_PHRASE, defaultPassword: envVars.DEFAULT_PASSWORD, watchOnlyPublicKey: envVars.WATCH_ONLY_PUBLIC_KEY_HASH, + contactPublicKey: envVars.CONTACT_ADDRESS_PUBLIC_KEY_HASH, bakerAddress: '', shortRandomContent: generateRandomContent(), longRandomContent: 'long random content for test + ' + generateRandomContent(), diff --git a/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.selectors.ts b/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.selectors.ts index f5634bea2..4a2493bea 100644 --- a/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.selectors.ts +++ b/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.selectors.ts @@ -1,6 +1,7 @@ export enum setWalletPasswordSelectors { passwordField = 'Register Form/Password Field', repeatPasswordField = 'Register Form/Repeat Password Field', + analyticsCheckBox = 'Register Form/Analytics Check Box', skipOnboardingCheckbox = 'Register Form/Skip Onboarding Checkbox', acceptTermsCheckbox = 'Register Form/Accept Terms Checkbox', importButton = 'Register Form/Import Button', diff --git a/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx b/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx index f0f7a1aaf..d00da1c63 100644 --- a/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx +++ b/src/app/pages/NewWallet/setWalletPassword/SetWalletPassword.tsx @@ -242,6 +242,7 @@ export const SetWalletPassword: FC = ({ /> } containerClassName="mb-4" + testID={setWalletPasswordSelectors.analyticsCheckBox} /> > = ({ item, actions }) => ( -
+
{item.name} @@ -214,6 +219,7 @@ const ContactContent: React.FC @@ -225,6 +231,7 @@ const ContactContent: React.FC diff --git a/src/app/templates/SendForm/ContactsDropdownItem.tsx b/src/app/templates/SendForm/ContactsDropdownItem.tsx index 860a91c31..7cf55865b 100644 --- a/src/app/templates/SendForm/ContactsDropdownItem.tsx +++ b/src/app/templates/SendForm/ContactsDropdownItem.tsx @@ -6,6 +6,7 @@ import { Button } from 'app/atoms/Button'; import HashShortView from 'app/atoms/HashShortView'; import Identicon from 'app/atoms/Identicon'; import Name from 'app/atoms/Name'; +import { setTestID } from 'lib/analytics'; import { T } from 'lib/i18n'; import { TempleContact } from 'lib/temple/types'; import { useScrollIntoView } from 'lib/ui/use-scroll-into-view'; @@ -39,7 +40,10 @@ const ContactsDropdownItem: FC = ({ contact, active,
{contact.name} - +
diff --git a/src/app/templates/SendForm/selectors.ts b/src/app/templates/SendForm/selectors.ts index 39bdee0b6..997aca998 100644 --- a/src/app/templates/SendForm/selectors.ts +++ b/src/app/templates/SendForm/selectors.ts @@ -1,6 +1,7 @@ export enum SendFormSelectors { assetItemButton = 'Send Form/Asset Item Button', contactItemButton = 'Send Form/Contact Item Button', + contactHashValue = 'Send Form/Contact Hash Value', assetDropDown = 'Send Form/Asset Drop-down', assetDropDownSearchInput = 'Send Form/Asset Drop-down Search Input', assetDropDownItem = 'Send Form/Asset Drop-down Item', From 62aa2208ab1e90cdd2642f0997f90b9057f98e8c Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Wed, 12 Jul 2023 16:55:46 +0300 Subject: [PATCH 10/14] import changed --- src/app/templates/AddressBook/AddressBook.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/templates/AddressBook/AddressBook.tsx b/src/app/templates/AddressBook/AddressBook.tsx index 037a07f16..c228ebd11 100644 --- a/src/app/templates/AddressBook/AddressBook.tsx +++ b/src/app/templates/AddressBook/AddressBook.tsx @@ -8,6 +8,7 @@ import Identicon from 'app/atoms/Identicon'; import Name from 'app/atoms/Name'; import SubTitle from 'app/atoms/SubTitle'; import { ReactComponent as CloseIcon } from 'app/icons/close.svg'; +import { setAnotherSelector, setTestID } from 'lib/analytics'; import { t, T } from 'lib/i18n'; import { isDomainNameValid, useTezosDomainsClient, useContacts, useFilteredContacts } from 'lib/temple/front'; import { isAddressValid } from 'lib/temple/helpers'; @@ -15,7 +16,6 @@ import { TempleContact } from 'lib/temple/types'; import { useConfirm } from 'lib/ui/dialog'; import { withErrorHumanDelay } from 'lib/ui/humanDelay'; -import { setAnotherSelector, setTestID } from '../../../lib/analytics'; import CustomSelect, { OptionRenderProps } from '../CustomSelect'; import HashChip from '../HashChip'; // eslint-disable-next-line import/namespace From 9b24742f506008d2f8d66882ec31fba412cf2d36 Mon Sep 17 00:00:00 2001 From: Vlad Novak Date: Wed, 19 Jul 2023 13:01:25 +0300 Subject: [PATCH 11/14] send and delegate are turned on --- e2e/src/features/delegate.feature | 2 +- e2e/src/features/send.feature | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e/src/features/delegate.feature b/e2e/src/features/delegate.feature index 8bcc042f6..630cb6390 100644 --- a/e2e/src/features/delegate.feature +++ b/e2e/src/features/delegate.feature @@ -1,5 +1,5 @@ Feature: Delegate - @dev + Scenario: As a user, i'd like to re-delegate TEZ to a baker Given I have imported an existing account And I press Selected Network Button on the Network Select page diff --git a/e2e/src/features/send.feature b/e2e/src/features/send.feature index 185a843be..c522f0e33 100644 --- a/e2e/src/features/send.feature +++ b/e2e/src/features/send.feature @@ -1,5 +1,5 @@ Feature: Send - @dev + Scenario: As a user, I'd like to send my funds to another account Given I have imported an existing account And I press Selected Network Button on the Network Select page From c44c61afe2db748d6906e99fc8a331b311ddacbd Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 Jul 2023 13:23:53 +0300 Subject: [PATCH 12/14] TW-815: [e2e] Add contact positive. Transfered E2E to Manual builds --- .github/workflows/code-quality.yml | 26 +-------------------- .github/workflows/manual-builds.yml | 35 +++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 36dfc5241..233f2e04d 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -45,20 +45,8 @@ jobs: TEMPLE_WALLET_API_URL: ${{ vars.TEMPLE_WALLET_API_URL }} TEMPLE_WALLET_STATIC_API_URL: ${{ vars.TEMPLE_WALLET_STATIC_API_URL }} TEMPLE_WALLET_DEXES_API_URL: ${{ vars.TEMPLE_WALLET_DEXES_API_URL }} - DEFAULT_HD_ACCOUNT_SEED_PHRASE: ${{ secrets.DEFAULT_HD_ACCOUNT_SEED_PHRASE }} - DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY }} - DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: ${{ secrets.DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} - DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY }} - DEFAULT_PASSWORD: ${{ secrets.DEFAULT_PASSWORD }} - IMPORTED_HD_ACCOUNT_SEED_PHRASE: ${{ secrets.IMPORTED_HD_ACCOUNT_SEED_PHRASE }} - IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY: ${{ secrets.IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY }} - IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: ${{ secrets.IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} - TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }} TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }} - WATCH_ONLY_PUBLIC_KEY_HASH: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH }} - WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM }} - CONTACT_ADDRESS_PUBLIC_KEY_HASH: ${{ secrets.CONTACT_ADDRESS_PUBLIC_KEY_HASH }} - CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM: ${{ secrets.CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM }} + TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }} - name: Install dependencies and code quality check uses: ./.github/workflows/code-quality-check @@ -66,18 +54,6 @@ jobs: - name: Create Chrome build run: yarn build:chrome - - name: Perform end-to-end tests - uses: ./.github/workflows/e2e - - - name: Upload E2E video to Slack - if: failure() - uses: adrey/slack-file-upload-action@master - with: - token: ${{ secrets.SLACK_E2E_VIDEO_UPLOADER_TOKEN }} - path: e2e/video-rep/test-runs.mp4 - channel: temple-test-runs - initial_comment: (Run ) - - name: Upload artifact uses: actions/upload-artifact@v2 with: diff --git a/.github/workflows/manual-builds.yml b/.github/workflows/manual-builds.yml index 472c73bc0..4236d9b05 100644 --- a/.github/workflows/manual-builds.yml +++ b/.github/workflows/manual-builds.yml @@ -1,4 +1,5 @@ name: Manual builds +run-name: Manual build of ${{ github.ref_name }} (Chrome=${{ github.event.inputs.chrome == 'true' || github.event.inputs.chromePlusE2E == 'true' }}; Firefox=${{ github.event.inputs.firefox == 'true' }}; E2E=${{ github.event.inputs.chromePlusE2E == 'true' }}) on: workflow_dispatch: @@ -7,6 +8,10 @@ on: type: boolean description: Chrome build required: false + chromePlusE2E: + type: boolean + description: Chrome build + E2E + required: false firefox: type: boolean description: Firefox build @@ -53,16 +58,29 @@ jobs: TEMPLE_WALLET_DEXES_API_URL: ${{ vars.TEMPLE_WALLET_DEXES_API_URL }} TEMPLE_WALLET_ROUTE3_AUTH_TOKEN: ${{ vars.TEMPLE_WALLET_ROUTE3_AUTH_TOKEN }} TEMPLE_WALLET_MOONPAY_API_KEY: ${{ secrets.TEMPLE_WALLET_MOONPAY_API_KEY }} + # [e2e] + DEFAULT_HD_ACCOUNT_SEED_PHRASE: ${{ secrets.DEFAULT_HD_ACCOUNT_SEED_PHRASE }} + DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_FIRST_PRIVATE_KEY }} + DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: ${{ secrets.DEFAULT_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} + DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY: ${{ secrets.DEFAULT_HD_ACCOUNT_SECOND_PRIVATE_KEY }} + DEFAULT_PASSWORD: ${{ secrets.DEFAULT_PASSWORD }} + IMPORTED_HD_ACCOUNT_SEED_PHRASE: ${{ secrets.IMPORTED_HD_ACCOUNT_SEED_PHRASE }} + IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY: ${{ secrets.IMPORTED_HD_ACCOUNT_FIRST_PRIVATE_KEY }} + IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM: ${{ secrets.IMPORTED_HD_ACCOUNT_FIRST_HASH_SHORT_FORM }} + WATCH_ONLY_PUBLIC_KEY_HASH: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH }} + WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM: ${{ secrets.WATCH_ONLY_PUBLIC_KEY_HASH_SHORT_FORM }} + CONTACT_ADDRESS_PUBLIC_KEY_HASH: ${{ secrets.CONTACT_ADDRESS_PUBLIC_KEY_HASH }} + CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM: ${{ secrets.CONTACT_ADDRESS_PUBLIC_KEY_HASH_SHORT_FORM }} - name: Install dependencies and code quality uses: ./.github/workflows/code-quality-check - name: Create Chrome build - if: ${{ github.event.inputs.chrome == 'true' }} + if: ${{ github.event.inputs.chrome == 'true' || github.event.inputs.chromePlusE2E == 'true' }} run: yarn build:chrome - name: Upload Chrome artifact - if: ${{ github.event.inputs.chrome == 'true' }} + if: ${{ github.event.inputs.chrome == 'true' || github.event.inputs.chromePlusE2E == 'true' }} uses: actions/upload-artifact@v2 with: name: chrome @@ -70,6 +88,19 @@ jobs: if-no-files-found: error retention-days: 30 + - name: Perform end-to-end tests + if: ${{ github.event.inputs.chromePlusE2E == 'true' }} + uses: ./.github/workflows/e2e + + - name: Upload E2E video to Slack + if: ${{ failure() && github.event.inputs.chromePlusE2E == 'true' }} + uses: adrey/slack-file-upload-action@master + with: + token: ${{ secrets.SLACK_E2E_VIDEO_UPLOADER_TOKEN }} + path: e2e/video-rep/test-runs.mp4 + channel: temple-test-runs + initial_comment: (Run ) + - name: Create Firefox build if: ${{ github.event.inputs.firefox == 'true' }} run: yarn build:firefox From 07cce10181363fd7e32b84584c14dc12ab20f30e Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 Jul 2023 13:34:17 +0300 Subject: [PATCH 13/14] TW-815: [e2e] Add contact positive. Refactor --- e2e/src/page-objects/pages/address-book.page.ts | 17 +++++------------ e2e/src/utils/timing.utils.ts | 2 ++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/e2e/src/page-objects/pages/address-book.page.ts b/e2e/src/page-objects/pages/address-book.page.ts index 11600f4af..face5ff98 100644 --- a/e2e/src/page-objects/pages/address-book.page.ts +++ b/e2e/src/page-objects/pages/address-book.page.ts @@ -1,7 +1,8 @@ import { AddressBookSelectors } from 'src/app/templates/AddressBook/AddressBook.selectors'; -import { Page } from '../../classes/page.class'; -import { createPageElement, findElement } from '../../utils/search.utils'; +import { Page } from 'e2e/src/classes/page.class'; +import { createPageElement, findElement } from 'e2e/src/utils/search.utils'; +import { VERY_SHORT_TIMEOUT } from 'e2e/src/utils/timing.utils'; export class AddressBookPage extends Page { addressInput = createPageElement(AddressBookSelectors.addressInput); @@ -19,15 +20,7 @@ export class AddressBookPage extends Page { await this.contactOwnLabelText.waitForDisplayed(); } - async isContactAdded(hash: string) { - const timeoutPromise = new Promise((_, reject) => { - setTimeout(() => { - reject(new Error(`The contact with address: '${hash}' was not found within 5 seconds.`)); - }, 5000); - }); - - const findElementPromise = findElement(AddressBookSelectors.contactItem, { hash }); - - return Promise.race([findElementPromise, timeoutPromise]); + isContactAdded(hash: string) { + return findElement(AddressBookSelectors.contactItem, { hash }, VERY_SHORT_TIMEOUT); } } diff --git a/e2e/src/utils/timing.utils.ts b/e2e/src/utils/timing.utils.ts index f776e05ce..cab91d36e 100644 --- a/e2e/src/utils/timing.utils.ts +++ b/e2e/src/utils/timing.utils.ts @@ -6,6 +6,8 @@ export const MEDIUM_TIMEOUT = 30_000; export const SHORT_TIMEOUT = 15_000; +export const VERY_SHORT_TIMEOUT = 5_000; + export const RETRY_OPTIONS = { minTimeout: 300, maxRetryTime: 15_000 From bfb058d33ed4ec527ad34c657696f3091e94cd2e Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 20 Jul 2023 13:53:53 +0300 Subject: [PATCH 14/14] TW-815: [e2e] Add contact positive. Refactor --- .../page-objects/pages/address-book.page.ts | 7 +++++- e2e/src/utils/search.utils.ts | 22 ++++++++++++++----- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/e2e/src/page-objects/pages/address-book.page.ts b/e2e/src/page-objects/pages/address-book.page.ts index face5ff98..58da4d5f0 100644 --- a/e2e/src/page-objects/pages/address-book.page.ts +++ b/e2e/src/page-objects/pages/address-book.page.ts @@ -21,6 +21,11 @@ export class AddressBookPage extends Page { } isContactAdded(hash: string) { - return findElement(AddressBookSelectors.contactItem, { hash }, VERY_SHORT_TIMEOUT); + return findElement( + AddressBookSelectors.contactItem, + { hash }, + VERY_SHORT_TIMEOUT, + `The contact with address: '${hash}' not found` + ); } } diff --git a/e2e/src/utils/search.utils.ts b/e2e/src/utils/search.utils.ts index 9916f6802..3348ce309 100644 --- a/e2e/src/utils/search.utils.ts +++ b/e2e/src/utils/search.utils.ts @@ -9,14 +9,24 @@ const buildTestIDSelector = (testID: string) => `[data-testid="${testID}"]`; type OtherSelectors = Record; -export const findElement = async (testID: string, otherSelectors?: OtherSelectors, timeout = MEDIUM_TIMEOUT) => { +export const findElement = async ( + testID: string, + otherSelectors?: OtherSelectors, + timeout = MEDIUM_TIMEOUT, + errorTitle?: string +) => { const selector = buildSelector(testID, otherSelectors); - return await findElementBySelectors(selector, timeout); + return await findElementBySelectors(selector, timeout, errorTitle); }; -export const findElementBySelectors = async (selectors: string, timeout = MEDIUM_TIMEOUT) => { - const element = await BrowserContext.page.waitForSelector(selectors, { visible: true, timeout }); +export const findElementBySelectors = async (selectors: string, timeout = MEDIUM_TIMEOUT, errorTitle?: string) => { + const element = await BrowserContext.page.waitForSelector(selectors, { visible: true, timeout }).catch(error => { + if (errorTitle && error instanceof Error) { + error.message = `${errorTitle}\n` + error.message; + } + throw error; + }); if (isDefined(element)) { return element; @@ -40,11 +50,11 @@ export const findElements = async (testID: string) => { class PageElement { constructor(public testID: string, public otherSelectors?: OtherSelectors, public notSelectors?: OtherSelectors) {} - findElement(timeout?: number) { + findElement(timeout?: number, errorTitle?: string) { let selectors = buildSelector(this.testID, this.otherSelectors); if (this.notSelectors) selectors += buildNotSelector(this.notSelectors); - return findElementBySelectors(selectors, timeout); + return findElementBySelectors(selectors, timeout, errorTitle); } waitForDisplayed(timeout?: number) { return this.findElement(timeout);