From 4cd3f3ce364139ec5238ae11684f38106ce4548a Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:02:49 +0100 Subject: [PATCH] Tests: Cypress tests imrpovements (#2816) * Update tests structure * Increase containers for parallel execution * Update workflows * Add test ids --- .github/workflows/ondemand.yml | 45 +++++ .github/workflows/regression.yml | 48 +++++ .github/workflows/safe-apps-e2e.yml | 2 +- .github/workflows/{e2e.yml => smoke.yml} | 4 +- cypress.config.js | 12 +- cypress/e2e/create_safe.cy.js | 21 -- cypress/e2e/custom_apps.cy.js | 29 --- cypress/e2e/intercom.cy.js | 44 ----- cypress/e2e/non_owner_spending_limit.cy.js | 54 ----- cypress/e2e/pages/balances.pages.js | 2 +- cypress/e2e/pages/create_wallet.pages.js | 11 +- cypress/e2e/pages/main.page.js | 10 + cypress/e2e/pages/navigation.page.js | 2 + cypress/e2e/pages/owners.pages.js | 15 +- cypress/e2e/regression/add_owner.cy.js | 55 ++++++ cypress/e2e/regression/address_book.cy.js | 65 ++++++ cypress/e2e/regression/assets.cy.js | 185 ++++++++++++++++++ .../e2e/{smoke => regression}/balances.cy.js | 0 .../balances_pagination.cy.js | 0 cypress/e2e/regression/batch_tx.cy.js | 33 ++++ .../e2e/{smoke => regression}/beamer.cy.js | 0 .../create_safe_google.cy.js | 0 .../e2e/regression/create_safe_simple.cy.js | 123 ++++++++++++ .../import_export_data.cy.js | 3 +- cypress/e2e/regression/load_safe.cy.js | 81 ++++++++ cypress/e2e/regression/nfts.cy.js | 28 +++ .../pending_actions.cy.js | 0 cypress/e2e/regression/remove_owner.cy.js | 41 ++++ cypress/e2e/regression/replace_owner.cy.js | 71 +++++++ cypress/e2e/regression/tx_history.cy.js | 31 +++ cypress/e2e/smoke/add_owner.cy.js | 65 ++---- cypress/e2e/smoke/address_book.cy.js | 56 +----- cypress/e2e/smoke/assets.cy.js | 178 +---------------- cypress/e2e/smoke/batch_tx.cy.js | 24 +-- cypress/e2e/smoke/create_safe_simple.cy.js | 119 +---------- cypress/e2e/smoke/create_tx.cy.js | 8 +- cypress/e2e/smoke/dashboard.cy.js | 10 +- cypress/e2e/smoke/landing.cy.js | 4 +- cypress/e2e/smoke/load_safe.cy.js | 56 +----- cypress/e2e/smoke/nfts.cy.js | 21 +- cypress/e2e/smoke/remove_owner.cy.js | 41 +--- cypress/e2e/smoke/replace_owner.cy.js | 66 +------ cypress/e2e/smoke/tx_history.cy.js | 13 +- cypress/e2e/spending_limit.cy.js | 54 ----- cypress/e2e/tx_modal.cy.js | 164 ---------------- cypress/e2e/tx_simulation.cy.js | 62 ------ cypress/support/constants.js | 2 + .../create/steps/OwnerPolicyStep/index.tsx | 10 +- .../create/steps/ReviewStep/index.tsx | 2 +- .../create/steps/SetNameStep/index.tsx | 4 +- .../settings/owner/OwnerList/index.tsx | 1 + .../tx-flow/flows/AddOwner/ChooseOwner.tsx | 4 +- .../flows/RemoveOwner/SetThreshold.tsx | 2 +- src/pages/settings/setup.tsx | 2 +- 54 files changed, 936 insertions(+), 1047 deletions(-) create mode 100644 .github/workflows/ondemand.yml create mode 100644 .github/workflows/regression.yml rename .github/workflows/{e2e.yml => smoke.yml} (94%) delete mode 100644 cypress/e2e/create_safe.cy.js delete mode 100644 cypress/e2e/custom_apps.cy.js delete mode 100644 cypress/e2e/intercom.cy.js delete mode 100644 cypress/e2e/non_owner_spending_limit.cy.js create mode 100644 cypress/e2e/regression/add_owner.cy.js create mode 100644 cypress/e2e/regression/address_book.cy.js create mode 100644 cypress/e2e/regression/assets.cy.js rename cypress/e2e/{smoke => regression}/balances.cy.js (100%) rename cypress/e2e/{smoke => regression}/balances_pagination.cy.js (100%) create mode 100644 cypress/e2e/regression/batch_tx.cy.js rename cypress/e2e/{smoke => regression}/beamer.cy.js (100%) rename cypress/e2e/{smoke => regression}/create_safe_google.cy.js (100%) create mode 100644 cypress/e2e/regression/create_safe_simple.cy.js rename cypress/e2e/{smoke => regression}/import_export_data.cy.js (95%) create mode 100644 cypress/e2e/regression/load_safe.cy.js create mode 100644 cypress/e2e/regression/nfts.cy.js rename cypress/e2e/{smoke => regression}/pending_actions.cy.js (100%) create mode 100644 cypress/e2e/regression/remove_owner.cy.js create mode 100644 cypress/e2e/regression/replace_owner.cy.js create mode 100644 cypress/e2e/regression/tx_history.cy.js delete mode 100644 cypress/e2e/spending_limit.cy.js delete mode 100644 cypress/e2e/tx_modal.cy.js delete mode 100644 cypress/e2e/tx_simulation.cy.js diff --git a/.github/workflows/ondemand.yml b/.github/workflows/ondemand.yml new file mode 100644 index 0000000000..77fd680078 --- /dev/null +++ b/.github/workflows/ondemand.yml @@ -0,0 +1,45 @@ +name: Regression on demand tests + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + e2e: + runs-on: ubuntu-20.04 + name: Cypress Regression on demand tests + strategy: + fail-fast: false + matrix: + containers: [1, 2, 3, 4, 5] + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/workflows/yarn + + - name: Install Cypress + run: | + ./node_modules/.bin/cypress install + + - uses: ./.github/workflows/build + with: + secrets: ${{ toJSON(secrets) }} + e2e_mnemonic: ${{ secrets.NEXT_PUBLIC_CYPRESS_MNEMONIC }} + + - name: Serve + run: yarn serve & + + - uses: cypress-io/github-action@v4 + with: + parallel: true + spec: cypress/e2e/**/*.cy.js + browser: chrome + record: true + config: baseUrl=http://localhost:8080 + group: 'Regression on demand tests' + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/regression.yml b/.github/workflows/regression.yml new file mode 100644 index 0000000000..82719bbe95 --- /dev/null +++ b/.github/workflows/regression.yml @@ -0,0 +1,48 @@ +name: Regression tests + +on: + pull_request: + branches: + - 'release**' + - 'release/**' + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + e2e: + runs-on: ubuntu-20.04 + name: Cypress Regression tests + strategy: + fail-fast: false + matrix: + containers: [1, 2, 3] + steps: + - uses: actions/checkout@v3 + + - uses: ./.github/workflows/yarn + + - name: Install Cypress + run: | + ./node_modules/.bin/cypress install + + - uses: ./.github/workflows/build + with: + secrets: ${{ toJSON(secrets) }} + e2e_mnemonic: ${{ secrets.NEXT_PUBLIC_CYPRESS_MNEMONIC }} + + - name: Serve + run: yarn serve & + + - uses: cypress-io/github-action@v4 + with: + parallel: true + spec: cypress/e2e/**/*.cy.js + browser: chrome + record: true + config: baseUrl=http://localhost:8080 + group: 'Regression tests' + env: + CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/safe-apps-e2e.yml b/.github/workflows/safe-apps-e2e.yml index 7053c80d4d..35c0add6ad 100644 --- a/.github/workflows/safe-apps-e2e.yml +++ b/.github/workflows/safe-apps-e2e.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - containers: [1, 2, 3] + containers: [1, 2] steps: - uses: actions/checkout@v3 diff --git a/.github/workflows/e2e.yml b/.github/workflows/smoke.yml similarity index 94% rename from .github/workflows/e2e.yml rename to .github/workflows/smoke.yml index fc2c709f25..d2d0ff5acc 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/smoke.yml @@ -1,4 +1,4 @@ -name: e2e +name: Smoke tests on: pull_request: @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - containers: [1, 2, 3] + containers: [1, 2, 3, 4, 5] steps: - uses: actions/checkout@v3 diff --git a/cypress.config.js b/cypress.config.js index ff3bf4baf8..cb3c4e200d 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -1,4 +1,5 @@ import { defineConfig } from 'cypress' +import * as fs from 'fs' export default defineConfig({ projectId: 'exhdra', @@ -8,8 +9,17 @@ export default defineConfig({ runMode: 1, openMode: 0, }, - e2e: { + setupNodeEvents(on, config) { + on('after:spec', (spec, results) => { + if (results && results.video) { + const failures = results.tests.some((test) => test.attempts.some((attempt) => attempt.state === 'failed')) + if (!failures) { + fs.unlinkSync(results.video) + } + } + }) + }, baseUrl: 'http://localhost:3000', testIsolation: false, hideXHR: true, diff --git a/cypress/e2e/create_safe.cy.js b/cypress/e2e/create_safe.cy.js deleted file mode 100644 index 3906b002d2..0000000000 --- a/cypress/e2e/create_safe.cy.js +++ /dev/null @@ -1,21 +0,0 @@ -describe('Create Safe', () => { - it('should create a new Safe', () => { - cy.visit('/welcome') - - // Close cookie banner - cy.contains('button', 'Accept all').click() - - // Ensure wallet is connected to correct chain via header - cy.contains('E2E Wallet @ Goerli') - - cy.contains('Create new Safe').click() - - // Name - cy.wait(1000) // Wait for form default values to populate - cy.contains('button', 'Next').click() - - // Owners and confirmations - cy.wait(1000) // Wait for form default values to populate - cy.contains('button', 'Next').click() - }) -}) diff --git a/cypress/e2e/custom_apps.cy.js b/cypress/e2e/custom_apps.cy.js deleted file mode 100644 index 4c008309bc..0000000000 --- a/cypress/e2e/custom_apps.cy.js +++ /dev/null @@ -1,29 +0,0 @@ -const appUrl = 'https://safe-custom-app.com' - -describe('When visiting a custom Safe App', () => { - before(() => { - cy.fixture('safe-app').then((html) => { - cy.intercept('GET', `${appUrl}`, html) - cy.intercept('GET', `${appUrl}/manifest.json`, { - name: 'Cypress Test App', - description: 'Cypress Test App Description', - icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml' }], - }) - }) - - cy.visitSafeApp(`${appUrl}`) - cy.wait(1000) - cy.contains('button', 'Accept all').click() - }) - - it('should show the custom app warning', () => { - cy.findByRole('heading', { content: /warning/i }) - cy.findByText('https://safe-custom-app.com') - cy.findByText('Check the link you are using') - - cy.findByRole('checkbox').should('exist').click() - cy.findByRole('button', { name: /continue/i }).click() - cy.reload() - cy.findByRole('heading', { content: /warning/i }).should('not.exist') - }) -}) diff --git a/cypress/e2e/intercom.cy.js b/cypress/e2e/intercom.cy.js deleted file mode 100644 index 0067f6e812..0000000000 --- a/cypress/e2e/intercom.cy.js +++ /dev/null @@ -1,44 +0,0 @@ -const RINKEBY_TEST_SAFE = 'rin:0xFfDC1BcdeC18b1196e7FA04246295DE3A17972Ac' -const intercomIframe = 'iframe[id="intercom-frame"]' -const intercomButton = '[aria-label="Open Intercom Messenger"]' -const fakeIntercomButton = 'img[alt="Open Intercom"]' - -describe('Intercom and cookie prefs', () => { - it('should show the Intercom chat if cookies are enabled', () => { - cy.visit('/') - - // Don't accept Intercom cookies - cy.contains('a', 'Accept selection').click() - - // Click on the Intercom button - cy.get(fakeIntercomButton).click() - - // Cookie preferences appear - cy.contains( - 'You attempted to open the customer support chat. Please accept the community support & updates cookies', - ) - - cy.contains('a', 'Accept all').click() - - // Intercom is now active - cy.get(intercomIframe).should('exist') - cy.get(fakeIntercomButton).should('not.exist') - cy.get(intercomButton).click() - cy.get('#intercom-container').should('exist') - - // Intercom should be disabled on a Safe App page - cy.visit(`/${RINKEBY_TEST_SAFE}/apps?appUrl=https://safe-apps.dev.gnosisdev.com/drain-safe`) - cy.get(intercomButton).should('not.exist') - - // Go to Settings and change the cookie settings - cy.visit(`/${RINKEBY_TEST_SAFE}/settings`) - cy.get(intercomIframe).should('exist') - cy.contains('button', 'Preferences').click() - cy.contains('Community support & updates').click() - cy.contains('a', 'Accept selection').click() - - // Intercom should be now disabled - cy.get(intercomButton).should('not.exist') - cy.get(fakeIntercomButton).should('be.visible') - }) -}) diff --git a/cypress/e2e/non_owner_spending_limit.cy.js b/cypress/e2e/non_owner_spending_limit.cy.js deleted file mode 100644 index 581a9f426b..0000000000 --- a/cypress/e2e/non_owner_spending_limit.cy.js +++ /dev/null @@ -1,54 +0,0 @@ -const HW_WALLET = '0xff6E053fBf4E5895eDec09146Bc10f705E8c4b3D' -const SPENDING_LIMIT_SAFE = 'gor:0xBE3C5aFF7f66c23fe71c3047911f9Aa0026b281B' - -describe('Check non-owner spending limit beneficiary modal', () => { - before(() => { - cy.visit(`/${SPENDING_LIMIT_SAFE}/home`, { failOnStatusCode: false }) - - cy.contains('Accept selection').click() - }) - - it('should open the spending limit modal', () => { - // Assert that "New transaction" button is visible - cy.contains('New transaction', { - timeout: 60_000, // `lastWallet` takes a while initialize in CI - }).should('be.visible') - - // Open the new transaction modal - cy.contains('New transaction').click() - }) - - it('should draft a spending limit transaction', () => { - // Modal is open - cy.contains('h2', 'New transaction').should('be.visible') - - cy.contains('Send tokens').click() - - // Fill transaction data - cy.get('input[name="recipient"]').type(SPENDING_LIMIT_SAFE) - - // Click on the Token selector - cy.get('input[name="tokenAddress"]').prev().click() - cy.get('ul[role="listbox"]').contains('Görli Ether').click() - - // Insert max amount - cy.contains('Spending Limit Transaction (100 GOR)').click() - - // Insert max amount - cy.contains('Max').click() - - cy.contains('Next').click() - }) - - it('should review the spending limit transaction', () => { - cy.contains( - 'Spending limit transactions only appear in the interface once they are successfully processed and indexed. Pending transactions can only be viewed in your signer wallet application or under your wallet address on a Blockchain Explorer.', - ) - - // Alias for New transaction modal - cy.contains('h2', 'Review transaction').parents('div').as('modal') - - // Estimation is loaded - cy.get('button[type="submit"]').should('not.be.disabled') - }) -}) diff --git a/cypress/e2e/pages/balances.pages.js b/cypress/e2e/pages/balances.pages.js index 0cb82caeb1..ebd08d0de9 100644 --- a/cypress/e2e/pages/balances.pages.js +++ b/cypress/e2e/pages/balances.pages.js @@ -22,7 +22,7 @@ const hiddenTokenIcon = 'svg[data-testid="VisibilityOffOutlinedIcon"]' const hideTokenDefaultString = 'Hide tokens' const assetNameSortBtnStr = 'Asset' const assetBalanceSortBtnStr = 'Balance' -const sendBtnStr = 'Send' +export const sendBtnStr = 'Send' const sendTokensStr = 'Send tokens' const pageRowsDefault = '25' diff --git a/cypress/e2e/pages/create_wallet.pages.js b/cypress/e2e/pages/create_wallet.pages.js index ed5fa6db89..38ff86234e 100644 --- a/cypress/e2e/pages/create_wallet.pages.js +++ b/cypress/e2e/pages/create_wallet.pages.js @@ -14,13 +14,15 @@ const createNewSafeBtn = 'span[data-track="create-safe: Continue to creation"]' const connectWalletBtn = 'Connect wallet' const googleConnectBtn = '[data-testid="google-connect-btn"]' const googleSignedinBtn = '[data-testid="signed-in-account-btn"]' -const googleAccountInfoHeader = '[data-testid="open-account-center"]' +export const accountInfoHeader = '[data-testid="open-account-center"]' const reviewStepOwnerInfo = '[data-testid="review-step-owner-info"]' const reviewStepNextBtn = '[data-testid="review-step-next-btn"]' const safeCreationStatusInfo = '[data-testid="safe-status-info"]' const startUsingSafeBtn = '[data-testid="start-using-safe-btn"]' const sponsorIcon = '[data-testid="sponsor-icon"]' const networkFeeSection = '[data-tetid="network-fee-section"]' +const nextBtn = '[data-testid="next-btn"]' +const backBtn = '[data-testid="back-btn"]' const sponsorStr = 'Your account is sponsored by Goerli' const safeCreationProcessing = 'Transaction is being executed' @@ -32,6 +34,9 @@ export const walletName = 'test1-sepolia-safe' export const defaltSepoliaPlaceholder = 'Sepolia Safe' const welcomeToSafeStr = 'Welcome to Safe' +export function clickOnBackBtn() { + cy.get(backBtn).should('be.enabled').click() +} export function verifySafeIsBeingCreated() { cy.get(safeCreationStatusInfo).should('have.text', safeCreationProcessing) } @@ -68,7 +73,7 @@ export function verifyGoogleSignin() { } export function verifyGoogleAccountInfoInHeader() { - return cy.get(googleAccountInfoHeader).should('exist') + return cy.get(accountInfoHeader).should('exist') } export function verifyPolicy1_1() { @@ -128,7 +133,7 @@ export function selectNetwork(network, regex = false) { } export function clickOnNextBtn() { - cy.contains('button', 'Next').click() + cy.get(nextBtn).should('be.enabled').click() } export function verifyOwnerName(name, index) { diff --git a/cypress/e2e/pages/main.page.js b/cypress/e2e/pages/main.page.js index b3a4c2de03..5cd87d4d05 100644 --- a/cypress/e2e/pages/main.page.js +++ b/cypress/e2e/pages/main.page.js @@ -11,6 +11,16 @@ export function clickOnSideMenuItem(item) { cy.get('p').contains(item).click() } +export function waitForTrnsactionHistoryToComplete() { + cy.intercept('GET', constants.transactionHistoryEndpoint).as('History') + cy.wait('@History') +} + +export function waitForSafeListRequestToComplete() { + cy.intercept('GET', constants.safeListEndpoint).as('Safes') + cy.wait('@Safes') +} + export function acceptCookies(index = 0) { cy.wait(1000) diff --git a/cypress/e2e/pages/navigation.page.js b/cypress/e2e/pages/navigation.page.js index e5deb73c08..8dd0ab339f 100644 --- a/cypress/e2e/pages/navigation.page.js +++ b/cypress/e2e/pages/navigation.page.js @@ -1,4 +1,6 @@ export const sideNavSettingsIcon = '[data-testid="settings-nav-icon"]' +export const setupSection = '[data-testid="setup-section"]' +export const modalBackBtn = '[data-testid="modal-back-btn"]' export function clickOnSideNavigation(option) { cy.get(option).should('exist').click() diff --git a/cypress/e2e/pages/owners.pages.js b/cypress/e2e/pages/owners.pages.js index cb4c521b67..f0276a325b 100644 --- a/cypress/e2e/pages/owners.pages.js +++ b/cypress/e2e/pages/owners.pages.js @@ -1,11 +1,12 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' +import * as createWallet from '../pages/create_wallet.pages' +import * as navigation from '../pages/navigation.page' const copyToClipboardBtn = 'button[aria-label="Copy to clipboard"]' const tooltipLabel = (label) => `span[aria-label="${label}"]` const removeOwnerBtn = 'span[data-track="settings: Remove owner"] > span > button' const replaceOwnerBtn = 'span[data-track="settings: Replace owner"] > span > button' -const addOwnerBtn = 'span[data-track="settings: Add owner"] > button' const tooltip = 'div[role="tooltip"]' const expandMoreIcon = 'svg[data-testid="ExpandMoreIcon"]' const sentinelStart = 'div[data-testid="sentinelStart"]' @@ -21,12 +22,13 @@ const existingOwnerAddressInput = (index) => `input[name="owners.${index}.addres const existingOwnerNameInput = (index) => `input[name="owners.${index}.name"]` const singleOwnerNameInput = 'input[name="name"]' const finishTransactionBtn = '[data-testid="finish-transaction-btn"]' +const addOwnerBtn = '[data-testid="add-owner-btn"]' +const addOwnerNextBtn = '[data-testid="add-owner-next-btn"]' const disconnectBtnStr = 'Disconnect' const notConnectedStatus = 'Connect' const e2eWalletStr = 'E2E Wallet' const max50charsLimitStr = 'Maximum 50 symbols' -const nextBtnStr = 'Next' const executeBtnStr = 'Execute' const backbtnStr = 'Back' const removeOwnerStr = 'Remove owner' @@ -160,12 +162,11 @@ export function clickOnConnectBtn() { } export function waitForConnectionStatus() { - cy.get('div').contains(e2eWalletStr) + cy.get(createWallet.accountInfoHeader).should('exist') } export function openAddOwnerWindow() { - cy.get('span').contains(addNewOwnerStr).click() - cy.wait(1000) + cy.get(addOwnerBtn).should('be.enabled').click() cy.get(newOwnerName).should('be.visible') cy.get(newOwnerAddress).should('be.visible') } @@ -203,11 +204,11 @@ export function verifyNewOwnerName(name) { } export function clickOnNextBtn() { - cy.get('button').contains(nextBtnStr).click() + cy.get(addOwnerNextBtn).should('be.enabled').click() } export function clickOnBackBtn() { - cy.get('button').contains(backbtnStr).click() + cy.get(navigation.modalBackBtn).should('be.enabled').click() } export function verifyConfirmTransactionWindowDisplayed() { diff --git a/cypress/e2e/regression/add_owner.cy.js b/cypress/e2e/regression/add_owner.cy.js new file mode 100644 index 0000000000..2bb15fc757 --- /dev/null +++ b/cypress/e2e/regression/add_owner.cy.js @@ -0,0 +1,55 @@ +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as owner from '../pages/owners.pages' +import * as addressBook from '../pages/address_book.page' + +describe('Add Owners tests', () => { + beforeEach(() => { + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) + cy.clearLocalStorage() + main.acceptCookies() + cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) + }) + + it('Verify Tooltip displays correct message for disconnected user', () => { + owner.waitForConnectionStatus() + owner.clickOnWalletExpandMoreIcon() + owner.clickOnDisconnectBtn() + owner.verifyAddOwnerBtnIsDisabled() + }) + + it('Verify the Add New Owner Form can be opened', () => { + owner.waitForConnectionStatus() + owner.openAddOwnerWindow() + }) + + it('Verify error message displayed if character limit is exceeded in Name input', () => { + owner.waitForConnectionStatus() + owner.openAddOwnerWindow() + owner.typeOwnerName(main.generateRandomString(51)) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) + }) + + it('Verify that the "Name" field is auto-filled with the relevant name from Address Book', () => { + cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) + addressBook.clickOnCreateEntryBtn() + addressBook.typeInName(constants.addresBookContacts.user1.name) + addressBook.typeInAddress(constants.addresBookContacts.user1.address) + addressBook.clickOnSaveEntryBtn() + addressBook.verifyNewEntryAdded(constants.addresBookContacts.user1.name, constants.addresBookContacts.user1.address) + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) + owner.waitForConnectionStatus() + owner.openAddOwnerWindow() + owner.typeOwnerAddress(constants.addresBookContacts.user1.address) + owner.selectNewOwner(constants.addresBookContacts.user1.name) + owner.verifyNewOwnerName(constants.addresBookContacts.user1.name) + }) + + it('Verify that Name field not mandatory', () => { + owner.waitForConnectionStatus() + owner.openAddOwnerWindow() + owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) + owner.clickOnNextBtn() + owner.verifyConfirmTransactionWindowDisplayed() + }) +}) diff --git a/cypress/e2e/regression/address_book.cy.js b/cypress/e2e/regression/address_book.cy.js new file mode 100644 index 0000000000..bd7ac23a16 --- /dev/null +++ b/cypress/e2e/regression/address_book.cy.js @@ -0,0 +1,65 @@ +import 'cypress-file-upload' +const path = require('path') +import { format } from 'date-fns' +import * as constants from '../../support/constants' +import * as addressBook from '../../e2e/pages/address_book.page' +import * as main from '../../e2e/pages/main.page' + +const NAME = 'Owner1' +const EDITED_NAME = 'Edited Owner1' + +describe('Address book tests', () => { + beforeEach(() => { + cy.clearLocalStorage() + cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) + main.acceptCookies() + }) + + //TODO: Use localstorage for setting up/deleting entries + it('Verify entered entry in Name input can be saved', () => { + addressBook.clickOnCreateEntryBtn() + addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) + addressBook.clickOnEditEntryBtn() + addressBook.typeInNameInput(EDITED_NAME) + addressBook.clickOnSaveButton() + addressBook.verifyNameWasChanged(NAME, EDITED_NAME) + }) + + //TODO: Rework to use Polygon. Replace Verify csv file can be imported (Goerli) with this test + it.skip('Verify that Sepolia and Polygon addresses can be imported', () => { + // Go to a Safe on Gnosis Chain + cy.get('header') + .contains(/^G(ö|oe)rli$/) + .click() + cy.contains('Gnosis Chain').click() + + // Navigate to the Address Book page + cy.visit(`/address-book?safe=${constants.GNO_TEST_SAFE}`) + + // Waits for the Address Book table to be in the page + cy.contains('p', 'Address book').should('be.visible') + + // Finds the imported Gnosis Chain address + cy.contains(constants.GNO_CSV_ENTRY.name).should('exist') + cy.contains(constants.GNO_CSV_ENTRY.address).should('exist') + }) + + // TODO: Rework with localstorage. Change title in Testrail. New title "...exported" + it('Verify the address book file can be downloaded', () => { + addressBook.clickOnImportFileBtn() + addressBook.importFile() + // Download the export file + const date = format(new Date(), 'yyyy-MM-dd', { timeZone: 'UTC' }) + const fileName = `safe-address-book-${date}.csv` //name that is given to the file automatically + + addressBook.clickOnExportFileBtn() + //This is the submit button for the Export modal. It requires an actuall class or testId to differentiate + //from the Export button at the top of the AB table + addressBook.confirmExport() + + const downloadsFolder = Cypress.config('downloadsFolder') + //File reading is failing in the CI. Can be tested locally + cy.readFile(path.join(downloadsFolder, fileName)).should('exist') + // TODO: Add verifications on address and chain from file, import and export files should be equal including all chains from origin + }) +}) diff --git a/cypress/e2e/regression/assets.cy.js b/cypress/e2e/regression/assets.cy.js new file mode 100644 index 0000000000..d50cb2be18 --- /dev/null +++ b/cypress/e2e/regression/assets.cy.js @@ -0,0 +1,185 @@ +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as balances from '../pages/balances.pages' +import * as owner from '../pages/owners.pages' + +const ASSET_NAME_COLUMN = 0 +const TOKEN_AMOUNT_COLUMN = 1 +const FIAT_AMOUNT_COLUMN = 2 + +describe('Assets tests', () => { + const fiatRegex = balances.fiatRegex + + beforeEach(() => { + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) + cy.clearLocalStorage() + main.acceptCookies() + }) + + it('Verify that non-native tokens are present and have balance', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.verifyBalance(balances.currencyDaiCap, TOKEN_AMOUNT_COLUMN, balances.currencyDaiAlttext) + balances.verifyTokenBalanceFormat( + balances.currencyDaiCap, + balances.currencyDaiFormat_2, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + balances.verifyBalance(balances.currencyAave, TOKEN_AMOUNT_COLUMN, balances.currencyAaveAlttext) + balances.verifyTokenBalanceFormat( + balances.currencyAave, + balances.currentcyAaveFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + balances.verifyBalance(balances.currencyLink, TOKEN_AMOUNT_COLUMN, balances.currencyLinkAlttext) + balances.verifyTokenBalanceFormat( + balances.currencyLink, + balances.currentcyLinkFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + balances.verifyBalance(balances.currencyTestTokenA, TOKEN_AMOUNT_COLUMN, balances.currencyTestTokenAAlttext) + balances.verifyTokenBalanceFormat( + balances.currencyTestTokenA, + balances.currentcyTestTokenAFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + balances.verifyBalance(balances.currencyTestTokenB, TOKEN_AMOUNT_COLUMN, balances.currencyTestTokenBAlttext) + balances.verifyTokenBalanceFormat( + balances.currencyTestTokenB, + balances.currentcyTestTokenBFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + balances.verifyBalance(balances.currencyUSDC, TOKEN_AMOUNT_COLUMN, balances.currencyTestUSDCAlttext) + balances.verifyTokenBalanceFormat( + balances.currencyUSDC, + balances.currentcyTestUSDCFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + }) + + it('Verify that every token except the native token has a "go to blockexplorer link"', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + // Specifying true for Sepolia. Will delete the flag once completely migrate to Sepolia + balances.verifyAssetNameHasExplorerLink(balances.currencyUSDC, ASSET_NAME_COLUMN, true) + balances.verifyAssetNameHasExplorerLink(balances.currencyTestTokenB, ASSET_NAME_COLUMN, true) + balances.verifyAssetNameHasExplorerLink(balances.currencyTestTokenA, ASSET_NAME_COLUMN, true) + balances.verifyAssetNameHasExplorerLink(balances.currencyLink, ASSET_NAME_COLUMN, true) + balances.verifyAssetNameHasExplorerLink(balances.currencyAave, ASSET_NAME_COLUMN, true) + balances.verifyAssetNameHasExplorerLink(balances.currencyDaiCap, ASSET_NAME_COLUMN, true) + balances.verifyAssetExplorerLinkNotAvailable(constants.tokenNames.sepoliaEther, ASSET_NAME_COLUMN) + }) + + it('Verify the default Fiat currency and the effects after changing it', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.verifyFirstRowDoesNotContainCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) + balances.verifyFirstRowContainsCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) + balances.clickOnCurrencyDropdown() + balances.selectCurrency(balances.currencyEUR) + balances.verifyFirstRowDoesNotContainCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) + balances.verifyFirstRowContainsCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) + }) + + it('Verify that a tool tip is shown pointing to "Token list" dropdown', () => { + //Spam warning message is removed in beforeEach hook + cy.reload() + }) + + it('Verify that checking the checkboxes increases the token selected counter', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(balances.currencyLink) + balances.checkTokenCounter(1) + }) + + it('Verify that selecting tokens and saving hides them from the table', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(balances.currencyLink) + balances.saveHiddenTokenSelection() + main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink]) + }) + + it('Verify that Cancel closes the menu and does not change the table status', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(balances.currencyLink) + balances.clickOnTokenCheckbox(balances.currencyAave) + balances.saveHiddenTokenSelection() + main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(balances.currencyLink) + balances.clickOnTokenCheckbox(balances.currencyAave) + balances.cancelSaveHiddenTokenSelection() + main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) + }) + + it('Verify that Deselect All unchecks all tokens from the list', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(balances.currencyLink) + balances.clickOnTokenCheckbox(balances.currencyAave) + balances.deselecAlltHiddenTokenSelection() + balances.verifyEachRowHasCheckbox(constants.checkboxStates.unchecked) + }) + + it('Verify the Hidden tokens counter works for spam tokens', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(balances.currencyLink) + balances.saveHiddenTokenSelection() + balances.checkHiddenTokenBtnCounter(1) + }) + + it('Verify the Hidden tokens counter works for native tokens', () => { + balances.openHideTokenMenu() + balances.clickOnTokenCheckbox(constants.tokenNames.sepoliaEther) + balances.saveHiddenTokenSelection() + balances.checkHiddenTokenBtnCounter(1) + }) + + it('Verify you can hide tokens from the eye icon in the table rows', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.hideAsset(balances.currencyLink) + }) + + it('Verify the sorting of "Assets" and "Balance" in the table', () => { + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.verifyTableRows(7) + balances.clickOnTokenNameSortBtn() + balances.verifyTokenNamesOrder() + balances.clickOnTokenNameSortBtn() + balances.verifyTokenNamesOrder('descending') + balances.clickOnTokenBalanceSortBtn() + balances.verifyTokenBalanceOrder() + balances.clickOnTokenBalanceSortBtn() + balances.verifyTokenBalanceOrder('descending') + }) + + // TODO: New title: "Verify that when owner is disconnected, Send button is disabled". + // Modify test accordingly. Include in smoke. + it('Verify that the Send button shows when hovering a row', () => { + owner.clickOnWalletExpandMoreIcon() + owner.clickOnDisconnectBtn() + balances.selectTokenList(balances.tokenListOptions.allTokens) + balances.showSendBtn(0) + owner.verifyTooltiptext(owner.disconnectedUserErrorMsg) + // Removed the part that checks for a non owner error message in the tooltip + // because the safe has no assets, and we don't have a safe with assets where e2e wallet is not an owner + }) +}) diff --git a/cypress/e2e/smoke/balances.cy.js b/cypress/e2e/regression/balances.cy.js similarity index 100% rename from cypress/e2e/smoke/balances.cy.js rename to cypress/e2e/regression/balances.cy.js diff --git a/cypress/e2e/smoke/balances_pagination.cy.js b/cypress/e2e/regression/balances_pagination.cy.js similarity index 100% rename from cypress/e2e/smoke/balances_pagination.cy.js rename to cypress/e2e/regression/balances_pagination.cy.js diff --git a/cypress/e2e/regression/batch_tx.cy.js b/cypress/e2e/regression/batch_tx.cy.js new file mode 100644 index 0000000000..7ae147fee7 --- /dev/null +++ b/cypress/e2e/regression/batch_tx.cy.js @@ -0,0 +1,33 @@ +import * as batch from '../pages/batches.pages' +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as owner from '../../e2e/pages/owners.pages.js' + +const currentNonce = 3 +const funds_first_tx = '0.001' +const funds_second_tx = '0.002' + +describe('Batch transaction tests', () => { + beforeEach(() => { + cy.clearLocalStorage() + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) + owner.waitForConnectionStatus() + main.acceptCookies() + }) + + // TODO: Check if localstorage can be used to add batches + // Rework test + it('Verify the Add batch button is present in a transaction form', () => { + //The "true" is to validate that the add to batch button is not visible if "Yes, execute" is selected + batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) + }) + + it('Verify a second transaction can be added to the batch', () => { + batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) + cy.wait(1000) + batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) + batch.verifyBatchIconCount(2) + batch.clickOnBatchCounter() + batch.verifyAmountTransactionsInBatch(2) + }) +}) diff --git a/cypress/e2e/smoke/beamer.cy.js b/cypress/e2e/regression/beamer.cy.js similarity index 100% rename from cypress/e2e/smoke/beamer.cy.js rename to cypress/e2e/regression/beamer.cy.js diff --git a/cypress/e2e/smoke/create_safe_google.cy.js b/cypress/e2e/regression/create_safe_google.cy.js similarity index 100% rename from cypress/e2e/smoke/create_safe_google.cy.js rename to cypress/e2e/regression/create_safe_google.cy.js diff --git a/cypress/e2e/regression/create_safe_simple.cy.js b/cypress/e2e/regression/create_safe_simple.cy.js new file mode 100644 index 0000000000..f8627e89d9 --- /dev/null +++ b/cypress/e2e/regression/create_safe_simple.cy.js @@ -0,0 +1,123 @@ +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as createwallet from '../pages/create_wallet.pages' +import * as owner from '../pages/owners.pages' + +describe('Safe creation tests', () => { + beforeEach(() => { + cy.visit(constants.welcomeUrl + '?chain=sep') + main.waitForSafeListRequestToComplete() + cy.clearLocalStorage() + main.acceptCookies() + }) + + it('Verify Next button is disabled until switching to network is done', () => { + owner.waitForConnectionStatus() + createwallet.selectNetwork(constants.networks.ethereum) + createwallet.clickOnCreateNewSafeBtn() + createwallet.checkNetworkChangeWarningMsg() + createwallet.verifyNextBtnIsDisabled() + createwallet.selectNetwork(constants.networks.sepolia) + createwallet.verifyNextBtnIsEnabled() + }) + + // TODO: Check unit tests + it('Verify error message is displayed if wallet name input exceeds 50 characters', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + createwallet.typeWalletName(main.generateRandomString(51)) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) + createwallet.clearWalletName() + }) + + // TODO: Replace wallet with Safe + // TODO: Check unit tests + it('Verify there is no error message is displayed if wallet name input contains less than 50 characters', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + createwallet.typeWalletName(main.generateRandomString(50)) + owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars) + }) + + it('Verify current connected account is shown as default owner', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + createwallet.clickOnNextBtn() + owner.verifyExistingOwnerAddress(0, constants.DEFAULT_OWNER_ADDRESS) + }) + + // TODO: Check unit tests + it('Verify error message is displayed if owner name input exceeds 50 characters', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + owner.typeExistingOwnerName(main.generateRandomString(51)) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) + }) + + // TODO: Check unit tests + it('Verify there is no error message is displayed if owner name input contains less than 50 characters', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + owner.typeExistingOwnerName(main.generateRandomString(50)) + owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars) + }) + + it('Verify data persistence', () => { + const ownerName = 'David' + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + createwallet.clickOnNextBtn() + createwallet.clickOnAddNewOwnerBtn() + createwallet.typeOwnerName(ownerName, 1) + createwallet.typeOwnerAddress(constants.SEPOLIA_OWNER_2, 1) + createwallet.clickOnBackBtn() + createwallet.clearWalletName() + createwallet.typeWalletName(createwallet.walletName) + createwallet.clickOnNextBtn() + createwallet.clickOnNextBtn() + createwallet.verifySafeNameInSummaryStep(createwallet.walletName) + createwallet.verifyOwnerNameInSummaryStep(ownerName) + createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) + createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) + createwallet.verifyThresholdStringInSummaryStep(1, 2) + createwallet.verifyNetworkInSummaryStep(constants.networks.sepolia) + createwallet.clickOnBackBtn() + createwallet.clickOnBackBtn() + cy.wait(1000) + createwallet.clickOnNextBtn() + createwallet.clickOnNextBtn() + createwallet.verifySafeNameInSummaryStep(createwallet.walletName) + createwallet.verifyOwnerNameInSummaryStep(ownerName) + createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) + createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) + createwallet.verifyThresholdStringInSummaryStep(1, 2) + createwallet.verifyNetworkInSummaryStep(constants.networks.sepolia) + createwallet.verifyEstimatedFeeInSummaryStep() + }) + + it('Verify tip is displayed on right side for threshold 1/1', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + createwallet.clickOnNextBtn() + createwallet.verifyPolicy1_1() + }) + + // TODO: Check unit tests + it('Verify address input validation rules', () => { + owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() + createwallet.clickOnNextBtn() + createwallet.clickOnAddNewOwnerBtn() + createwallet.typeOwnerAddress(main.generateRandomString(10), 1) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidFormat) + + createwallet.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS, 1) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.ownerAdded) + + createwallet.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS.toUpperCase(), 1) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidChecksum) + + createwallet.typeOwnerAddress(constants.ENS_TEST_SEPOLIA_INVALID, 1) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.failedResolve) + }) +}) diff --git a/cypress/e2e/smoke/import_export_data.cy.js b/cypress/e2e/regression/import_export_data.cy.js similarity index 95% rename from cypress/e2e/smoke/import_export_data.cy.js rename to cypress/e2e/regression/import_export_data.cy.js index 2d99d5d9b7..379314b48a 100644 --- a/cypress/e2e/smoke/import_export_data.cy.js +++ b/cypress/e2e/regression/import_export_data.cy.js @@ -36,6 +36,7 @@ describe('Import Export Data tests', () => { file.verifyAppsAreVisible(appNames) }) + // TODO: Revisit logic it('Verify imported data in settings', () => { const unchecked = [file.prependChainPrefixStr, file.copyAddressStr] const checked = [file.darkModeStr] @@ -45,7 +46,7 @@ describe('Import Export Data tests', () => { file.verifyCheckboxes(checked, true) }) - it('Verifies data for export in Data tab', () => { + it('Verify data for export in Data tab', () => { file.clickOnShowMoreTabsBtn() file.verifDataTabBtnIsVisible() file.clickOnDataTab() diff --git a/cypress/e2e/regression/load_safe.cy.js b/cypress/e2e/regression/load_safe.cy.js new file mode 100644 index 0000000000..3099d130d6 --- /dev/null +++ b/cypress/e2e/regression/load_safe.cy.js @@ -0,0 +1,81 @@ +import 'cypress-file-upload' +import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as safe from '../pages/load_safe.pages' +import * as createwallet from '../pages/create_wallet.pages' + +const testSafeName = 'Test safe name' +const testOwnerName = 'Test Owner Name' +// TODO +const SAFE_ENS_NAME = 'test20.eth' +const SAFE_ENS_NAME_TRANSLATED = constants.EOA + +const EOA_ADDRESS = constants.EOA + +const INVALID_ADDRESS_ERROR_MSG = 'Address given is not a valid Safe address' + +// TODO +const OWNER_ENS_DEFAULT_NAME = 'test20.eth' +const OWNER_ADDRESS = constants.EOA + +describe('Load Safe tests', () => { + beforeEach(() => { + cy.clearLocalStorage() + cy.visit(constants.loadNewSafeSepoliaUrl) + main.acceptCookies() + cy.wait(2000) + }) + + it('Verify a network can be selected in the Safe', () => { + safe.clickNetworkSelector(constants.networks.sepolia) + safe.selectPolygon() + cy.wait(2000) + safe.clickNetworkSelector(constants.networks.polygon) + safe.selectSepolia() + }) + + // TODO: Devide by positive and negative tests + it('Verify only valid Safe name can be accepted', () => { + // alias the address input label + cy.get('input[name="address"]').parent().prev('label').as('addressLabel') + + createwallet.verifyDefaultWalletName(createwallet.defaltSepoliaPlaceholder) + safe.verifyIncorrectAddressErrorMessage() + safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_1) + + // Type an invalid address + // cy.get('input[name="address"]').clear().type(EOA_ADDRESS) + // cy.get('@addressLabel').contains(INVALID_ADDRESS_ERROR_MSG) + + // Type a ENS name + // TODO: register a goerli ENS name for the test Safe + // cy.get('input[name="address"]').clear().type(SAFE_ENS_NAME) + // giving time to the ENS name to be translated + // cy.get('input[name="address"]', { timeout: 10000 }).should('have.value', `rin:${SAFE_ENS_NAME_TRANSLATED}`) + + // Uploading a QR code + // TODO: fix this + // cy.findByTestId('QrCodeIcon').click() + // cy.contains('Upload an image').click() + // cy.get('[type="file"]').attachFile('../fixtures/goerli_safe_QR.png') + + safe.verifyAddressInputValue() + safe.clickOnNextBtn() + }) + + it('Verify custom name in the first owner can be set', () => { + safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_1) + safe.clickOnNextBtn() + createwallet.typeOwnerName(testOwnerName, 0) + safe.clickOnNextBtn() + }) + + it('Verify Safe and owner names are displayed in the Review step', () => { + safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_1) + safe.clickOnNextBtn() + createwallet.typeOwnerName(testOwnerName, 0) + safe.clickOnNextBtn() + safe.verifyDataInReviewSection(testSafeName, testOwnerName) + safe.clickOnAddBtn() + }) +}) diff --git a/cypress/e2e/regression/nfts.cy.js b/cypress/e2e/regression/nfts.cy.js new file mode 100644 index 0000000000..74f7791d24 --- /dev/null +++ b/cypress/e2e/regression/nfts.cy.js @@ -0,0 +1,28 @@ +import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as nfts from '../pages/nfts.pages' + +const nftsName = 'CatFactory' +const nftsAddress = '0x373B...866c' +const nftsTokenID = 'CF' + +describe('NFTs tests', () => { + beforeEach(() => { + cy.clearLocalStorage() + cy.visit(constants.balanceNftsUrl + constants.SEPOLIA_TEST_SAFE_5) + main.acceptCookies() + nfts.waitForNftItems(2) + }) + + // TODO: Add Sign action + it('Verify multipls NFTs can be selected and reviewed', () => { + nfts.verifyInitialNFTData() + nfts.selectNFTs(3) + nfts.deselectNFTs([2], 3) + nfts.sendNFT() + nfts.verifyNFTModalData() + nfts.typeRecipientAddress(constants.SEPOLIA_TEST_SAFE_4) + nfts.clikOnNextBtn() + nfts.verifyReviewModalData(2) + }) +}) diff --git a/cypress/e2e/smoke/pending_actions.cy.js b/cypress/e2e/regression/pending_actions.cy.js similarity index 100% rename from cypress/e2e/smoke/pending_actions.cy.js rename to cypress/e2e/regression/pending_actions.cy.js diff --git a/cypress/e2e/regression/remove_owner.cy.js b/cypress/e2e/regression/remove_owner.cy.js new file mode 100644 index 0000000000..84758dc45a --- /dev/null +++ b/cypress/e2e/regression/remove_owner.cy.js @@ -0,0 +1,41 @@ +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as owner from '../pages/owners.pages' + +describe('Remove Owners tests', () => { + beforeEach(() => { + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) + main.waitForTrnsactionHistoryToComplete() + cy.clearLocalStorage() + main.acceptCookies() + owner.waitForConnectionStatus() + cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) + }) + + it('Verify that "Remove" icon is visible', () => { + owner.verifyRemoveBtnIsEnabled().should('have.length', 2) + }) + + it('Verify Tooltip displays correct message for Non-Owner', () => { + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_4) + main.waitForTrnsactionHistoryToComplete() + owner.waitForConnectionStatus() + owner.verifyRemoveBtnIsDisabled() + }) + + it('Verify Tooltip displays correct message for disconnected user', () => { + owner.clickOnWalletExpandMoreIcon() + owner.clickOnDisconnectBtn() + owner.verifyRemoveBtnIsDisabled() + }) + + it('Verify owner removal form can be opened', () => { + owner.openRemoveOwnerWindow(1) + }) + + it('Verify threshold input displays the upper limit as the current safe number of owners minus one', () => { + owner.openRemoveOwnerWindow(1) + owner.verifyThresholdLimit(1, 1) + owner.getThresholdOptions().should('have.length', 1) + }) +}) diff --git a/cypress/e2e/regression/replace_owner.cy.js b/cypress/e2e/regression/replace_owner.cy.js new file mode 100644 index 0000000000..4d3465983c --- /dev/null +++ b/cypress/e2e/regression/replace_owner.cy.js @@ -0,0 +1,71 @@ +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as owner from '../pages/owners.pages' +import * as addressBook from '../pages/address_book.page' + +describe('Replace Owners tests', () => { + beforeEach(() => { + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) + cy.clearLocalStorage() + main.acceptCookies() + cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) + }) + + it('Verify Tooltip displays correct message for disconnected user', () => { + owner.waitForConnectionStatus() + owner.clickOnWalletExpandMoreIcon() + owner.clickOnDisconnectBtn() + owner.verifyReplaceBtnIsDisabled() + }) + + // TODO: Check unit tests + it('Verify max characters in name field', () => { + owner.waitForConnectionStatus() + owner.openReplaceOwnerWindow() + owner.typeOwnerName(main.generateRandomString(51)) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) + }) + + // TODO: Rework with localstorage + it('Verify that Address input auto-fills with related value', () => { + cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) + addressBook.clickOnCreateEntryBtn() + addressBook.typeInName(constants.addresBookContacts.user1.name) + addressBook.typeInAddress(constants.addresBookContacts.user1.address) + addressBook.clickOnSaveEntryBtn() + addressBook.verifyNewEntryAdded(constants.addresBookContacts.user1.name, constants.addresBookContacts.user1.address) + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) + owner.waitForConnectionStatus() + owner.openReplaceOwnerWindow() + owner.typeOwnerAddress(constants.addresBookContacts.user1.address) + owner.selectNewOwner(constants.addresBookContacts.user1.name) + owner.verifyNewOwnerName(constants.addresBookContacts.user1.name) + }) + + it('Verify that Name field not mandatory. Verify confirmation for owner replacement is displayed', () => { + owner.waitForConnectionStatus() + owner.openReplaceOwnerWindow() + owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) + owner.clickOnNextBtn() + owner.verifyConfirmTransactionWindowDisplayed() + }) + + it('Verify relevant error messages are displayed in Address input', () => { + owner.waitForConnectionStatus() + owner.openReplaceOwnerWindow() + owner.typeOwnerAddress(main.generateRandomString(10)) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidFormat) + + owner.typeOwnerAddress(constants.addresBookContacts.user1.address.toUpperCase()) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidChecksum) + + owner.typeOwnerAddress(constants.SEPOLIA_TEST_SAFE_1) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.ownSafe) + + owner.typeOwnerAddress(constants.addresBookContacts.user1.address.replace('F', 'f')) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidChecksum) + + owner.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS) + owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.alreadyAdded) + }) +}) diff --git a/cypress/e2e/regression/tx_history.cy.js b/cypress/e2e/regression/tx_history.cy.js new file mode 100644 index 0000000000..ea29756d19 --- /dev/null +++ b/cypress/e2e/regression/tx_history.cy.js @@ -0,0 +1,31 @@ +import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as createTx from '../pages/create_tx.pages' + +const OUTGOING = 'Sent' + +const str1 = 'Received' +const str2 = 'Executed' +const str3 = 'Transaction hash' + +describe('Transaction history tests', () => { + beforeEach(() => { + cy.clearLocalStorage() + // Go to the test Safe transaction history + cy.visit(constants.transactionsHistoryUrl + constants.SEPOLIA_TEST_SAFE_5) + + // So that tests that rely on this feature don't randomly fail + cy.window().then((win) => win.localStorage.setItem('SAFE_v2__AB_human-readable', true)) + + main.acceptCookies() + }) + + it('Verify transaction can be expanded/collapsed', () => { + createTx.clickOnTransactionExpandableItem('Oct 20, 2023', () => { + createTx.verifyTransactionStrExists(str1) + createTx.verifyTransactionStrExists(str2) + createTx.verifyTransactionStrExists(str3) + createTx.clickOnExpandIcon() + }) + }) +}) diff --git a/cypress/e2e/smoke/add_owner.cy.js b/cypress/e2e/smoke/add_owner.cy.js index 2397881b65..a4588e261c 100644 --- a/cypress/e2e/smoke/add_owner.cy.js +++ b/cypress/e2e/smoke/add_owner.cy.js @@ -1,68 +1,29 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' import * as owner from '../pages/owners.pages' -import * as addressBook from '../pages/address_book.page' +import * as navigation from '../pages/navigation.page' -describe('Add Owners tests', () => { +describe('[SMOKE] Add Owners tests', () => { beforeEach(() => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) cy.clearLocalStorage() + main.waitForTrnsactionHistoryToComplete() main.acceptCookies() - cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) + main.verifyElementsExist([navigation.setupSection]) }) - it('Verify the presence of "Add Owner" button', () => { + it('[SMOKE] Verify the presence of "Add Owner" button', () => { owner.verifyAddOwnerBtnIsEnabled() }) - it('Verify “Add new owner” button tooltip displays correct message for Non-Owner', () => { + it('[SMOKE] Verify “Add new owner” button tooltip displays correct message for Non-Owner', () => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_2) + main.waitForTrnsactionHistoryToComplete() owner.verifyAddOwnerBtnIsDisabled() }) - it('Verify Tooltip displays correct message for disconnected user', () => { - owner.waitForConnectionStatus() - owner.clickOnWalletExpandMoreIcon() - owner.clickOnDisconnectBtn() - owner.verifyAddOwnerBtnIsDisabled() - }) - - it('Verify the Add New Owner Form can be opened', () => { - owner.waitForConnectionStatus() - owner.openAddOwnerWindow() - }) - - it('Verify error message displayed if character limit is exceeded in Name input', () => { - owner.waitForConnectionStatus() - owner.openAddOwnerWindow() - owner.typeOwnerName(main.generateRandomString(51)) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) - }) - - it('Verify that the "Name" field is auto-filled with the relevant name from Address Book', () => { - cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) - addressBook.clickOnCreateEntryBtn() - addressBook.typeInName(constants.addresBookContacts.user1.name) - addressBook.typeInAddress(constants.addresBookContacts.user1.address) - addressBook.clickOnSaveEntryBtn() - addressBook.verifyNewEntryAdded(constants.addresBookContacts.user1.name, constants.addresBookContacts.user1.address) - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) - owner.waitForConnectionStatus() - owner.openAddOwnerWindow() - owner.typeOwnerAddress(constants.addresBookContacts.user1.address) - owner.selectNewOwner(constants.addresBookContacts.user1.name) - owner.verifyNewOwnerName(constants.addresBookContacts.user1.name) - }) - - it('Verify that Name field not mandatory', () => { - owner.waitForConnectionStatus() - owner.openAddOwnerWindow() - owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) - owner.clickOnNextBtn() - owner.verifyConfirmTransactionWindowDisplayed() - }) - - it('Verify relevant error messages are displayed in Address input', () => { + // TODO: Check if this test is covered with unit tests + it('[SMOKE] Verify relevant error messages are displayed in Address input', () => { owner.waitForConnectionStatus() owner.openAddOwnerWindow() owner.typeOwnerAddress(main.generateRandomString(10)) @@ -81,22 +42,20 @@ describe('Add Owners tests', () => { owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.alreadyAdded) }) - it('Verify default threshold value. Verify correct threshold calculation', () => { + it('[SMOKE] Verify default threshold value. Verify correct threshold calculation', () => { owner.waitForConnectionStatus() owner.openAddOwnerWindow() owner.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS) owner.verifyThreshold(1, 2) }) - it('Verify valid Address validation', () => { + it('[SMOKE] Verify valid Address validation', () => { owner.waitForConnectionStatus() owner.openAddOwnerWindow() owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) owner.clickOnNextBtn() owner.verifyConfirmTransactionWindowDisplayed() - cy.reload() - owner.waitForConnectionStatus() - owner.openAddOwnerWindow() + owner.clickOnBackBtn() owner.typeOwnerAddress(constants.SEPOLIA_TEST_SAFE_2) owner.clickOnNextBtn() owner.verifyConfirmTransactionWindowDisplayed() diff --git a/cypress/e2e/smoke/address_book.cy.js b/cypress/e2e/smoke/address_book.cy.js index b0af66eecd..27093faa55 100644 --- a/cypress/e2e/smoke/address_book.cy.js +++ b/cypress/e2e/smoke/address_book.cy.js @@ -1,6 +1,4 @@ import 'cypress-file-upload' -const path = require('path') -import { format } from 'date-fns' import * as constants from '../../support/constants' import * as addressBook from '../../e2e/pages/address_book.page' import * as main from '../../e2e/pages/main.page' @@ -8,28 +6,21 @@ import * as main from '../../e2e/pages/main.page' const NAME = 'Owner1' const EDITED_NAME = 'Edited Owner1' -describe('Address book tests', () => { +describe('[SMOKE] Address book tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) + main.waitForTrnsactionHistoryToComplete() main.acceptCookies() }) - it('Verify entry can be added', () => { + it('[SMOKE] Verify entry can be added', () => { addressBook.clickOnCreateEntryBtn() addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) }) - it('Verify entered entry in Name input can be saved', () => { - addressBook.clickOnCreateEntryBtn() - addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) - addressBook.clickOnEditEntryBtn() - addressBook.typeInNameInput(EDITED_NAME) - addressBook.clickOnSaveButton() - addressBook.verifyNameWasChanged(NAME, EDITED_NAME) - }) - - it('Verify entry can be deleted', () => { + //TODO: Use localstorage for setting up/deleting entries + it('[SMOKE] Verify entry can be deleted', () => { addressBook.clickOnCreateEntryBtn() addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) // Click the delete button in the first entry @@ -38,45 +29,10 @@ describe('Address book tests', () => { addressBook.verifyEditedNameNotExists(EDITED_NAME) }) - it('Verify csv file can be imported (Goerli)', () => { + it('[SMOKE] Verify csv file can be imported', () => { addressBook.clickOnImportFileBtn() addressBook.importFile() addressBook.verifyImportModalIsClosed() addressBook.verifyDataImported(constants.SEPOLIA_CSV_ENTRY.name, constants.SEPOLIA_CSV_ENTRY.address) }) - - it.skip('Verify Gnosis Chain imported address can be found', () => { - // Go to a Safe on Gnosis Chain - cy.get('header') - .contains(/^G(ö|oe)rli$/) - .click() - cy.contains('Gnosis Chain').click() - - // Navigate to the Address Book page - cy.visit(`/address-book?safe=${constants.GNO_TEST_SAFE}`) - - // Waits for the Address Book table to be in the page - cy.contains('p', 'Address book').should('be.visible') - - // Finds the imported Gnosis Chain address - cy.contains(constants.GNO_CSV_ENTRY.name).should('exist') - cy.contains(constants.GNO_CSV_ENTRY.address).should('exist') - }) - - it('Verify the address book file can be downloaded', () => { - addressBook.clickOnImportFileBtn() - addressBook.importFile() - // Download the export file - const date = format(new Date(), 'yyyy-MM-dd', { timeZone: 'UTC' }) - const fileName = `safe-address-book-${date}.csv` //name that is given to the file automatically - - addressBook.clickOnExportFileBtn() - //This is the submit button for the Export modal. It requires an actuall class or testId to differentiate - //from the Export button at the top of the AB table - addressBook.confirmExport() - - const downloadsFolder = Cypress.config('downloadsFolder') - //File reading is failing in the CI. Can be tested locally - cy.readFile(path.join(downloadsFolder, fileName)).should('exist') - }) }) diff --git a/cypress/e2e/smoke/assets.cy.js b/cypress/e2e/smoke/assets.cy.js index ebdf8cad3a..7eb9e311f2 100644 --- a/cypress/e2e/smoke/assets.cy.js +++ b/cypress/e2e/smoke/assets.cy.js @@ -1,13 +1,12 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' import * as balances from '../pages/balances.pages' -import * as owner from '../pages/owners.pages' const ASSET_NAME_COLUMN = 0 const TOKEN_AMOUNT_COLUMN = 1 const FIAT_AMOUNT_COLUMN = 2 -describe('Assets tests', () => { +describe('[SMOKE] Assets tests', () => { const fiatRegex = balances.fiatRegex beforeEach(() => { @@ -16,99 +15,15 @@ describe('Assets tests', () => { main.acceptCookies() }) - it('Verify that the token tab is selected by default and the table is visible', () => { + it('[SMOKE] Verify that the token tab is selected by default and the table is visible', () => { balances.verifyTokensTabIsSelected('true') }) - it('Verify that the native token is visible', () => { + it('[SMOKE] Verify that the native token is visible', () => { balances.verifyTokenIsPresent(constants.tokenNames.sepoliaEther) }) - it('Verify that non-native tokens are present and have balance', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.verifyBalance(balances.currencyDaiCap, TOKEN_AMOUNT_COLUMN, balances.currencyDaiAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyDaiCap, - balances.currencyDaiFormat_2, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyAave, TOKEN_AMOUNT_COLUMN, balances.currencyAaveAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyAave, - balances.currentcyAaveFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyLink, TOKEN_AMOUNT_COLUMN, balances.currencyLinkAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyLink, - balances.currentcyLinkFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyTestTokenA, TOKEN_AMOUNT_COLUMN, balances.currencyTestTokenAAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyTestTokenA, - balances.currentcyTestTokenAFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyTestTokenB, TOKEN_AMOUNT_COLUMN, balances.currencyTestTokenBAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyTestTokenB, - balances.currentcyTestTokenBFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyUSDC, TOKEN_AMOUNT_COLUMN, balances.currencyTestUSDCAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyUSDC, - balances.currentcyTestUSDCFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - }) - - it('Verify that every token except the native token has a "go to blockexplorer link"', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - // Specifying true for Sepolia. Will delete the flag once completely migrate to Sepolia - balances.verifyAssetNameHasExplorerLink(balances.currencyUSDC, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyTestTokenB, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyTestTokenA, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyLink, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyAave, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyDaiCap, ASSET_NAME_COLUMN, true) - balances.verifyAssetExplorerLinkNotAvailable(constants.tokenNames.sepoliaEther, ASSET_NAME_COLUMN) - }) - - it('Verify the default Fiat currency and the effects after changing it', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.verifyFirstRowDoesNotContainCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) - balances.verifyFirstRowContainsCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) - balances.clickOnCurrencyDropdown() - balances.selectCurrency(balances.currencyEUR) - balances.verifyFirstRowDoesNotContainCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) - balances.verifyFirstRowContainsCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) - }) - - it('Verify that a tool tip is shown pointing to "Token list" dropdown', () => { - //Spam warning message is removed in beforeEach hook - cy.reload() - }) - - it('Verify that Token list dropdown down options show/hide spam tokens', () => { + it('[SMOKE] Verify that Token list dropdown down options show/hide spam tokens', () => { let spamTokens = [ balances.currencyAave, balances.currencyTestTokenA, @@ -124,95 +39,14 @@ describe('Assets tests', () => { main.verifyValuesExist(balances.tokenListTable, spamTokens) }) - it('Verify that "Hide token" button is present and opens the "Hide tokens menu"', () => { + it('[SMOKE] Verify that "Hide token" button is present and opens the "Hide tokens menu"', () => { balances.selectTokenList(balances.tokenListOptions.allTokens) balances.openHideTokenMenu() balances.verifyEachRowHasCheckbox() }) - it('Verify that checking the checkboxes increases the token selected counter', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.checkTokenCounter(1) - }) - - it('Verify that selecting tokens and saving hides them from the table', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.saveHiddenTokenSelection() - main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink]) - }) - - it('Verify that Cancel closes the menu and does not change the table status', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.clickOnTokenCheckbox(balances.currencyAave) - balances.saveHiddenTokenSelection() - main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.clickOnTokenCheckbox(balances.currencyAave) - balances.cancelSaveHiddenTokenSelection() - main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) - }) - - it('Verify that Deselect All unchecks all tokens from the list', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.clickOnTokenCheckbox(balances.currencyAave) - balances.deselecAlltHiddenTokenSelection() - balances.verifyEachRowHasCheckbox(constants.checkboxStates.unchecked) - }) - - it('Verify the Hidden tokens counter works for spam tokens', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.saveHiddenTokenSelection() - balances.checkHiddenTokenBtnCounter(1) - }) - - it('Verify the Hidden tokens counter works for native tokens', () => { - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(constants.tokenNames.sepoliaEther) - balances.saveHiddenTokenSelection() - balances.checkHiddenTokenBtnCounter(1) - }) - - it('Verify you can hide tokens from the eye icon in the table rows', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.hideAsset(balances.currencyLink) - }) - - it('Verify the sorting of "Assets" and "Balance" in the table', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.verifyTableRows(7) - balances.clickOnTokenNameSortBtn() - balances.verifyTokenNamesOrder() - balances.clickOnTokenNameSortBtn() - balances.verifyTokenNamesOrder('descending') - balances.clickOnTokenBalanceSortBtn() - balances.verifyTokenBalanceOrder() - balances.clickOnTokenBalanceSortBtn() - balances.verifyTokenBalanceOrder('descending') - }) - - it('Verify that clicking the button with an owner opens the Send funds form', () => { + it('[SMOKE] Verify that clicking the button with an owner opens the Send funds form', () => { balances.selectTokenList(balances.tokenListOptions.allTokens) balances.clickOnSendBtn(0) }) - - it('Verify that the Send button shows when hovering a row', () => { - owner.clickOnWalletExpandMoreIcon() - owner.clickOnDisconnectBtn() - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.showSendBtn(0) - owner.verifyTooltiptext(owner.disconnectedUserErrorMsg) - // Removed the part that checks for a non owner error message in the tooltip - // because the safe has no assets, and we don't have a safe with assets where e2e wallet is not an owner - }) }) diff --git a/cypress/e2e/smoke/batch_tx.cy.js b/cypress/e2e/smoke/batch_tx.cy.js index c3e93d08fc..df4caff475 100644 --- a/cypress/e2e/smoke/batch_tx.cy.js +++ b/cypress/e2e/smoke/batch_tx.cy.js @@ -7,7 +7,7 @@ const currentNonce = 3 const funds_first_tx = '0.001' const funds_second_tx = '0.002' -describe('Batch transaction tests', () => { +describe('[SMOKE] Batch transaction tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) @@ -15,17 +15,12 @@ describe('Batch transaction tests', () => { main.acceptCookies() }) - it('Verify empty batch list can be opened', () => { + it('[SMOKE] Verify empty batch list can be opened', () => { batch.openBatchtransactionsModal() batch.openNewTransactionModal() }) - it('Verify the Add batch button is present in a transaction form', () => { - //The "true" is to validate that the add to batch button is not visible if "Yes, execute" is selected - batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) - }) - - it('Verify a transaction can be added to the batch', () => { + it('[SMOKE] Verify a transaction can be added to the batch', () => { batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) cy.contains(batch.transactionAddedToBatchStr).should('be.visible') //The batch button in the header shows the transaction count @@ -34,16 +29,7 @@ describe('Batch transaction tests', () => { batch.verifyAmountTransactionsInBatch(1) }) - it('Verify a second transaction can be added to the batch', () => { - batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) - cy.wait(1000) - batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) - batch.verifyBatchIconCount(2) - batch.clickOnBatchCounter() - batch.verifyAmountTransactionsInBatch(2) - }) - - it('Verify the batch can be confirmed and related transactions exist in the form', () => { + it('[SMOKE] Verify the batch can be confirmed and related transactions exist in the form', () => { batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) cy.wait(1000) batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) @@ -58,7 +44,7 @@ describe('Batch transaction tests', () => { cy.get('@TransactionList').find('li').eq(1).find('span').eq(0).contains(funds_first_tx) }) - it('Verify a transaction can be removed from the batch', () => { + it('[SMOKE] Verify a transaction can be removed from the batch', () => { batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) cy.wait(1000) batch.addNewTransactionToBatch(constants.EOA, currentNonce, funds_first_tx) diff --git a/cypress/e2e/smoke/create_safe_simple.cy.js b/cypress/e2e/smoke/create_safe_simple.cy.js index 1b924cd743..3578417fce 100644 --- a/cypress/e2e/smoke/create_safe_simple.cy.js +++ b/cypress/e2e/smoke/create_safe_simple.cy.js @@ -3,13 +3,14 @@ import * as main from '../../e2e/pages/main.page' import * as createwallet from '../pages/create_wallet.pages' import * as owner from '../pages/owners.pages' -describe('Safe creation tests', () => { +describe('[SMOKE] Safe creation tests', () => { beforeEach(() => { cy.visit(constants.welcomeUrl + '?chain=sep') + main.waitForSafeListRequestToComplete() cy.clearLocalStorage() main.acceptCookies() }) - it('Verify a Wallet can be connected', () => { + it('[SMOKE] Verify a Wallet can be connected', () => { createwallet.clickOnCreateNewSafeBtn() owner.clickOnWalletExpandMoreIcon() owner.clickOnDisconnectBtn() @@ -17,62 +18,16 @@ describe('Safe creation tests', () => { createwallet.connectWallet() }) - it('Verify Next button is disabled until switching to network is done', () => { - owner.waitForConnectionStatus() - createwallet.selectNetwork(constants.networks.ethereum) - createwallet.clickOnCreateNewSafeBtn() - createwallet.checkNetworkChangeWarningMsg() - createwallet.verifyNextBtnIsDisabled() - createwallet.selectNetwork(constants.networks.sepolia) - createwallet.verifyNextBtnIsEnabled() - }) - - it('Verify that a new Wallet has default name related to the selected network', () => { + it('[SMOKE] Verify that a new Wallet has default name related to the selected network', () => { owner.waitForConnectionStatus() createwallet.clickOnCreateNewSafeBtn() createwallet.verifyDefaultWalletName(createwallet.defaltSepoliaPlaceholder) }) - it('Verify error message is displayed if wallet name input exceeds 50 characters', () => { + it('[SMOKE] Verify Add and Remove Owner Row works as expected', () => { owner.waitForConnectionStatus() createwallet.clickOnCreateNewSafeBtn() - createwallet.typeWalletName(main.generateRandomString(51)) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) - createwallet.clearWalletName() - }) - - it('Verify there is no error message is displayed if wallet name input contains less than 50 characters', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - createwallet.typeWalletName(main.generateRandomString(50)) - owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars) - }) - - it('Verify current connected account is shown as default owner', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.clickOnNextBtn() - owner.verifyExistingOwnerAddress(0, constants.DEFAULT_OWNER_ADDRESS) - }) - - it('Verify error message is displayed if owner name input exceeds 50 characters', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.typeExistingOwnerName(main.generateRandomString(51)) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) - }) - - it('Verify there is no error message is displayed if owner name input contains less than 50 characters', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.typeExistingOwnerName(main.generateRandomString(50)) - owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars) - }) - - it('Verify Add and Remove Owner Row works as expected', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.clickOnNextBtn() + createwallet.clickOnNextBtn() createwallet.clickOnAddNewOwnerBtn() owner.verifyNumberOfOwners(2) owner.verifyExistingOwnerAddress(1, '') @@ -83,10 +38,10 @@ describe('Safe creation tests', () => { owner.verifyNumberOfOwners(2) }) - it('Verify Threshold Setup', () => { + it('[SMOKE] Verify Threshold Setup', () => { owner.waitForConnectionStatus() createwallet.clickOnCreateNewSafeBtn() - owner.clickOnNextBtn() + createwallet.clickOnNextBtn() createwallet.clickOnAddNewOwnerBtn() createwallet.clickOnAddNewOwnerBtn() owner.verifyNumberOfOwners(3) @@ -100,62 +55,4 @@ describe('Safe creation tests', () => { owner.verifyThresholdLimit(1, 2) createwallet.updateThreshold(1) }) - - it('Verify data persistence', () => { - const ownerName = 'David' - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.clickOnNextBtn() - createwallet.clickOnAddNewOwnerBtn() - createwallet.typeOwnerName(ownerName, 1) - createwallet.typeOwnerAddress(constants.SEPOLIA_OWNER_2, 1) - owner.clickOnBackBtn() - createwallet.clearWalletName() - createwallet.typeWalletName(createwallet.walletName) - owner.clickOnNextBtn() - owner.clickOnNextBtn() - createwallet.verifySafeNameInSummaryStep(createwallet.walletName) - createwallet.verifyOwnerNameInSummaryStep(ownerName) - createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) - createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) - createwallet.verifyThresholdStringInSummaryStep(1, 2) - createwallet.verifyNetworkInSummaryStep(constants.networks.sepolia) - owner.clickOnBackBtn() - owner.clickOnBackBtn() - cy.wait(1000) - owner.clickOnNextBtn() - owner.clickOnNextBtn() - createwallet.verifySafeNameInSummaryStep(createwallet.walletName) - createwallet.verifyOwnerNameInSummaryStep(ownerName) - createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) - createwallet.verifyOwnerAddressInSummaryStep(constants.DEFAULT_OWNER_ADDRESS) - createwallet.verifyThresholdStringInSummaryStep(1, 2) - createwallet.verifyNetworkInSummaryStep(constants.networks.sepolia) - createwallet.verifyEstimatedFeeInSummaryStep() - }) - - it('Verify tip is displayed on right side for threshold 1/1', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.clickOnNextBtn() - createwallet.verifyPolicy1_1() - }) - - it('Verify address input validation rules', () => { - owner.waitForConnectionStatus() - createwallet.clickOnCreateNewSafeBtn() - owner.clickOnNextBtn() - createwallet.clickOnAddNewOwnerBtn() - createwallet.typeOwnerAddress(main.generateRandomString(10), 1) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidFormat) - - createwallet.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS, 1) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.ownerAdded) - - createwallet.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS.toUpperCase(), 1) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidChecksum) - - createwallet.typeOwnerAddress(constants.ENS_TEST_SEPOLIA_INVALID, 1) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.failedResolve) - }) }) diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index 74718d6980..75014058c3 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -5,14 +5,14 @@ import * as createtx from '../../e2e/pages/create_tx.pages' const sendValue = 0.00002 const currentNonce = 11 -describe('Create transactions tests', () => { +describe('[SMOKE] Create transactions tests', () => { before(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) main.acceptCookies() }) - it('Verify a new send token transaction can be created', () => { + it('[SMOKE] Verify a new send token transaction can be created', () => { createtx.clickOnNewtransactionBtn() createtx.clickOnSendTokensBtn() createtx.typeRecipientAddress(constants.EOA) @@ -23,7 +23,7 @@ describe('Create transactions tests', () => { createtx.clickOnNextBtn() }) - it('Verify a transaction can be reviewed, edited and submitted', () => { + it('[SMOKE] Verify a transaction can be reviewed, edited and submitted', () => { createtx.verifySubmitBtnIsEnabled() cy.wait(1000) createtx.verifyNativeTokenTransfer() @@ -36,7 +36,7 @@ describe('Create transactions tests', () => { createtx.clickOnSignTransactionBtn() }) - it('Verify that clicking on notification shows the transaction in queue', () => { + it('[SMOKE] Verify that clicking on notification shows the transaction in queue', () => { createtx.waitForProposeRequest() createtx.clickViewTransaction() createtx.verifySingleTxPage() diff --git a/cypress/e2e/smoke/dashboard.cy.js b/cypress/e2e/smoke/dashboard.cy.js index d3712a5b07..03b239c733 100644 --- a/cypress/e2e/smoke/dashboard.cy.js +++ b/cypress/e2e/smoke/dashboard.cy.js @@ -2,7 +2,7 @@ import * as constants from '../../support/constants' import * as dashboard from '../pages/dashboard.pages' import * as main from '../pages/main.page' -describe('Dashboard tests', () => { +describe('[SMOKE] Dashboard tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) @@ -11,19 +11,19 @@ describe('Dashboard tests', () => { dashboard.verifyConnectTransactStrIsVisible() }) - it('Verify the overview widget is displayed', () => { + it('[SMOKE] Verify the overview widget is displayed', () => { dashboard.verifyOverviewWidgetData() }) - it('Verify the transaction queue widget is displayed', () => { + it('[SMOKE] Verify the transaction queue widget is displayed', () => { dashboard.verifyTxQueueWidget() }) - it('Verify the featured Safe Apps are displayed', () => { + it('[SMOKE] Verify the featured Safe Apps are displayed', () => { dashboard.verifyFeaturedAppsSection() }) - it('Verify the Safe Apps Section is displayed', () => { + it('[SMOKE] Verify the Safe Apps Section is displayed', () => { dashboard.verifySafeAppsSection() }) }) diff --git a/cypress/e2e/smoke/landing.cy.js b/cypress/e2e/smoke/landing.cy.js index 206732446f..f56883364a 100644 --- a/cypress/e2e/smoke/landing.cy.js +++ b/cypress/e2e/smoke/landing.cy.js @@ -1,6 +1,6 @@ import * as constants from '../../support/constants' -describe('Landing page tests', () => { - it('Verify a user will be redirected to welcome page', () => { +describe('[SMOKE] Landing page tests', () => { + it('[SMOKE] Verify a user will be redirected to welcome page', () => { cy.clearLocalStorage() cy.visit('/') cy.url().should('include', constants.welcomeUrl) diff --git a/cypress/e2e/smoke/load_safe.cy.js b/cypress/e2e/smoke/load_safe.cy.js index edce4b7f8b..1d9122eb47 100644 --- a/cypress/e2e/smoke/load_safe.cy.js +++ b/cypress/e2e/smoke/load_safe.cy.js @@ -18,7 +18,7 @@ const INVALID_ADDRESS_ERROR_MSG = 'Address given is not a valid Safe address' const OWNER_ENS_DEFAULT_NAME = 'test20.eth' const OWNER_ADDRESS = constants.EOA -describe('Load Safe tests', () => { +describe('[SMOKE] Load Safe tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.loadNewSafeSepoliaUrl) @@ -26,59 +26,7 @@ describe('Load Safe tests', () => { cy.wait(2000) }) - it('Verify a network can be selected in the Safe', () => { - safe.clickNetworkSelector(constants.networks.sepolia) - safe.selectPolygon() - cy.wait(2000) - safe.clickNetworkSelector(constants.networks.polygon) - safe.selectSepolia() - }) - - it('Verify only valid Safe name can be accepted', () => { - // alias the address input label - cy.get('input[name="address"]').parent().prev('label').as('addressLabel') - - createwallet.verifyDefaultWalletName(createwallet.defaltSepoliaPlaceholder) - safe.verifyIncorrectAddressErrorMessage() - safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_1) - - // Type an invalid address - // cy.get('input[name="address"]').clear().type(EOA_ADDRESS) - // cy.get('@addressLabel').contains(INVALID_ADDRESS_ERROR_MSG) - - // Type a ENS name - // TODO: register a goerli ENS name for the test Safe - // cy.get('input[name="address"]').clear().type(SAFE_ENS_NAME) - // giving time to the ENS name to be translated - // cy.get('input[name="address"]', { timeout: 10000 }).should('have.value', `rin:${SAFE_ENS_NAME_TRANSLATED}`) - - // Uploading a QR code - // TODO: fix this - // cy.findByTestId('QrCodeIcon').click() - // cy.contains('Upload an image').click() - // cy.get('[type="file"]').attachFile('../fixtures/goerli_safe_QR.png') - - safe.verifyAddressInputValue() - safe.clickOnNextBtn() - }) - - it('Verify custom name in the first owner an be set', () => { - safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_1) - safe.clickOnNextBtn() - createwallet.typeOwnerName(testOwnerName, 0) - safe.clickOnNextBtn() - }) - - it('Verify Safe and owner names are displayed in the Review step', () => { - safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_1) - safe.clickOnNextBtn() - createwallet.typeOwnerName(testOwnerName, 0) - safe.clickOnNextBtn() - safe.verifyDataInReviewSection(testSafeName, testOwnerName) - safe.clickOnAddBtn() - }) - - it('Verify the custom Safe name is successfully loaded', () => { + it('[SMOKE] Verify the custom Safe name is successfully loaded', () => { safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_2) safe.clickOnNextBtn() createwallet.typeOwnerName(testOwnerName, 0) diff --git a/cypress/e2e/smoke/nfts.cy.js b/cypress/e2e/smoke/nfts.cy.js index 4a39e1a545..11cd2217d2 100644 --- a/cypress/e2e/smoke/nfts.cy.js +++ b/cypress/e2e/smoke/nfts.cy.js @@ -6,7 +6,7 @@ const nftsName = 'CatFactory' const nftsAddress = '0x373B...866c' const nftsTokenID = 'CF' -describe('NFTs tests', () => { +describe('[SMOKE] NFTs tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.balanceNftsUrl + constants.SEPOLIA_TEST_SAFE_5) @@ -14,34 +14,23 @@ describe('NFTs tests', () => { nfts.waitForNftItems(2) }) - it('Verify that NFTs exist in the table', () => { + it('[SMOKE] Verify that NFTs exist in the table', () => { nfts.verifyNFTNumber(10) }) - it('Verify NFT row contains data', () => { + it('[SMOKE] Verify NFT row contains data', () => { nfts.verifyDataInTable(nftsName, nftsAddress, nftsTokenID) }) - it('Verify NFT preview window can be opened', () => { + it('[SMOKE] Verify NFT preview window can be opened', () => { nfts.openActiveNFT(0) nfts.verifyNameInNFTModal(nftsTokenID) nfts.verifySelectedNetwrokSepolia() nfts.closeNFTModal() }) - it('Verify NFT open does not open if no NFT exits', () => { + it('[SMOKE] Verify NFT open does not open if no NFT exits', () => { nfts.clickOnInactiveNFT() nfts.verifyNFTModalDoesNotExist() }) - - it('Verify multipls NFTs can be selected and reviewed', () => { - nfts.verifyInitialNFTData() - nfts.selectNFTs(3) - nfts.deselectNFTs([2], 3) - nfts.sendNFT() - nfts.verifyNFTModalData() - nfts.typeRecipientAddress(constants.SEPOLIA_TEST_SAFE_4) - nfts.clikOnNextBtn() - nfts.verifyReviewModalData(2) - }) }) diff --git a/cypress/e2e/smoke/remove_owner.cy.js b/cypress/e2e/smoke/remove_owner.cy.js index 4643b91e1a..d6127941ce 100644 --- a/cypress/e2e/smoke/remove_owner.cy.js +++ b/cypress/e2e/smoke/remove_owner.cy.js @@ -1,8 +1,9 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' import * as owner from '../pages/owners.pages' +import * as createwallet from '../pages/create_wallet.pages' -describe('Remove Owners tests', () => { +describe('[SMOKE] Remove Owners tests', () => { beforeEach(() => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) cy.clearLocalStorage() @@ -10,44 +11,12 @@ describe('Remove Owners tests', () => { cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) }) - it('Verify that "Remove" icon is visible', () => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) - owner.verifyRemoveBtnIsEnabled().should('have.length', 2) - }) - - it('Verify Tooltip displays correct message for Non-Owner', () => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_4) - owner.waitForConnectionStatus() - owner.verifyRemoveBtnIsDisabled() - }) - - it('Verify Tooltip displays correct message for disconnected user', () => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) - owner.waitForConnectionStatus() - owner.clickOnWalletExpandMoreIcon() - owner.clickOnDisconnectBtn() - owner.verifyRemoveBtnIsDisabled() - }) - - it('Verify owner removal form can be opened', () => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) - owner.waitForConnectionStatus() - owner.openRemoveOwnerWindow(1) - }) - - it('Verify threshold input displays the upper limit as the current safe number of owners minus one', () => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) - owner.waitForConnectionStatus() - owner.openRemoveOwnerWindow(1) - owner.verifyThresholdLimit(1, 1) - owner.getThresholdOptions().should('have.length', 1) - }) - - it('Verify owner deletion confirmation is displayed', () => { + // TODO: Add Sign action. Check there is no error before sign action on UI when nonce not loaded + it('[SMOKE] Verify owner deletion confirmation is displayed', () => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) owner.waitForConnectionStatus() owner.openRemoveOwnerWindow(1) - owner.clickOnNextBtn() + createwallet.clickOnNextBtn() owner.verifyOwnerDeletionWindowDisplayed() }) }) diff --git a/cypress/e2e/smoke/replace_owner.cy.js b/cypress/e2e/smoke/replace_owner.cy.js index ed48056206..6bccf10e28 100644 --- a/cypress/e2e/smoke/replace_owner.cy.js +++ b/cypress/e2e/smoke/replace_owner.cy.js @@ -1,9 +1,8 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' import * as owner from '../pages/owners.pages' -import * as addressBook from '../pages/address_book.page' -describe('Replace Owners tests', () => { +describe('[SMOKE] Replace Owners tests', () => { beforeEach(() => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) cy.clearLocalStorage() @@ -11,74 +10,19 @@ describe('Replace Owners tests', () => { cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) }) - it('Verify that "Replace" icon is visible', () => { + it('[SMOKE] Verify that "Replace" icon is visible', () => { owner.verifyReplaceBtnIsEnabled() }) - it('Verify Tooltip displays correct message for Non-Owner', () => { + // TODO: Remove "tooltip" from title + it('[SMOKE] Verify Tooltip displays correct message for Non-Owner', () => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_2) owner.waitForConnectionStatus() owner.verifyReplaceBtnIsDisabled() }) - it('Verify Tooltip displays correct message for disconnected user', () => { - owner.waitForConnectionStatus() - owner.clickOnWalletExpandMoreIcon() - owner.clickOnDisconnectBtn() - owner.verifyReplaceBtnIsDisabled() - }) - - it('Verify that the owner replacement form is opened', () => { - owner.waitForConnectionStatus() - owner.openReplaceOwnerWindow() - }) - - it('Verify max characters in name field', () => { - owner.waitForConnectionStatus() - owner.openReplaceOwnerWindow() - owner.typeOwnerName(main.generateRandomString(51)) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.exceedChars) - }) - - it('Verify that Address input auto-fills with related value', () => { - cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) - addressBook.clickOnCreateEntryBtn() - addressBook.typeInName(constants.addresBookContacts.user1.name) - addressBook.typeInAddress(constants.addresBookContacts.user1.address) - addressBook.clickOnSaveEntryBtn() - addressBook.verifyNewEntryAdded(constants.addresBookContacts.user1.name, constants.addresBookContacts.user1.address) - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) + it('[SMOKE] Verify that the owner replacement form is opened', () => { owner.waitForConnectionStatus() owner.openReplaceOwnerWindow() - owner.typeOwnerAddress(constants.addresBookContacts.user1.address) - owner.selectNewOwner(constants.addresBookContacts.user1.name) - owner.verifyNewOwnerName(constants.addresBookContacts.user1.name) - }) - - it('Verify that Name field not mandatory. Verify confirmation for owner replacement is displayed', () => { - owner.waitForConnectionStatus() - owner.openReplaceOwnerWindow() - owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) - owner.clickOnNextBtn() - owner.verifyConfirmTransactionWindowDisplayed() - }) - - it('Verify relevant error messages are displayed in Address input', () => { - owner.waitForConnectionStatus() - owner.openReplaceOwnerWindow() - owner.typeOwnerAddress(main.generateRandomString(10)) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidFormat) - - owner.typeOwnerAddress(constants.addresBookContacts.user1.address.toUpperCase()) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidChecksum) - - owner.typeOwnerAddress(constants.SEPOLIA_TEST_SAFE_1) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.ownSafe) - - owner.typeOwnerAddress(constants.addresBookContacts.user1.address.replace('F', 'f')) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.invalidChecksum) - - owner.typeOwnerAddress(constants.DEFAULT_OWNER_ADDRESS) - owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.alreadyAdded) }) }) diff --git a/cypress/e2e/smoke/tx_history.cy.js b/cypress/e2e/smoke/tx_history.cy.js index a9b8309c3f..af41ef7d45 100644 --- a/cypress/e2e/smoke/tx_history.cy.js +++ b/cypress/e2e/smoke/tx_history.cy.js @@ -8,7 +8,7 @@ const str1 = 'Received' const str2 = 'Executed' const str3 = 'Transaction hash' -describe('Transaction history tests', () => { +describe('[SMOKE] Transaction history tests', () => { beforeEach(() => { cy.clearLocalStorage() // Go to the test Safe transaction history @@ -20,7 +20,7 @@ describe('Transaction history tests', () => { main.acceptCookies() }) - it('Verify October 29th transactions are displayed', () => { + it('[SMOKE] Verify October 29th transactions are displayed', () => { const DATE = 'Oct 29, 2023' const NEXT_DATE_LABEL = 'Oct 20, 2023' const amount = '0.00001 ETH' @@ -44,13 +44,4 @@ describe('Transaction history tests', () => { createTx.verifyTransactionStrExists(success) }) }) - - it('Verify transaction can be expanded/collapsed', () => { - createTx.clickOnTransactionExpandableItem('Oct 20, 2023', () => { - createTx.verifyTransactionStrExists(str1) - createTx.verifyTransactionStrExists(str2) - createTx.verifyTransactionStrExists(str3) - createTx.clickOnExpandIcon() - }) - }) }) diff --git a/cypress/e2e/spending_limit.cy.js b/cypress/e2e/spending_limit.cy.js deleted file mode 100644 index 403d1b93ce..0000000000 --- a/cypress/e2e/spending_limit.cy.js +++ /dev/null @@ -1,54 +0,0 @@ -const HW_WALLET = '0xff6E053fBf4E5895eDec09146Bc10f705E8c4b3D' -const SPENDING_LIMIT_SAFE = 'gor:0x28F95E682D1dd632b54Dc61740575f49DB39Eb7F' - -describe('Check spending limit modal', () => { - before(() => { - cy.visit(`/${SPENDING_LIMIT_SAFE}/home`, { failOnStatusCode: false }) - - cy.contains('Accept selection').click() - }) - - it('should open the spending limit modal', () => { - // Assert that "New transaction" button is visible - cy.contains('New transaction', { - timeout: 60_000, // `lastWallet` takes a while initialize in CI - }).should('be.visible') - - // Open the new transaction modal - cy.contains('New transaction').click() - }) - - it('should draft a spending limit transaction', () => { - // Modal is open - cy.contains('h2', 'New transaction').should('be.visible') - - cy.contains('Send tokens').click() - - // Fill transaction data - cy.get('input[name="recipient"]').type(SPENDING_LIMIT_SAFE) - - // Click on the Token selector - cy.get('input[name="tokenAddress"]').prev().click() - cy.get('ul[role="listbox"]').contains('Görli Ether').click() - - // Insert max amount - cy.contains('Spending Limit Transaction (0.1 GOR)').click() - - // Insert max amount - cy.contains('Max').click() - - cy.contains('Next').click() - }) - - it('should review the spending limit transaction', () => { - cy.contains( - 'Spending limit transactions only appear in the interface once they are successfully processed and indexed. Pending transactions can only be viewed in your signer wallet application or under your wallet address on a Blockchain Explorer.', - ) - - // Alias for New transaction modal - cy.contains('h2', 'Review transaction').parents('div').as('modal') - - // Estimation is loaded - cy.get('button[type="submit"]').should('not.be.disabled') - }) -}) diff --git a/cypress/e2e/tx_modal.cy.js b/cypress/e2e/tx_modal.cy.js deleted file mode 100644 index f416144b47..0000000000 --- a/cypress/e2e/tx_modal.cy.js +++ /dev/null @@ -1,164 +0,0 @@ -import * as constants from '../support/constants' - -const TEST_SAFE = 'rin:0x11Df0fa87b30080d59eba632570f620e37f2a8f7' -const RECIPIENT_ENS = 'diogo.eth' -const SAFE_NONCE = '6' - -describe('Tx Modal', () => { - before(() => { - // Open the Safe used for testing - cy.visit(`/${TEST_SAFE}`) - cy.contains('a', 'Accept selection').click() - }) - - describe('Send funds modal', () => { - describe('Send Funds form', () => { - before(() => { - // Open Send Funds Modal - cy.contains('New Transaction').click() - cy.contains('Send funds').click() - }) - - it('should display Send Funds modal with all the form elements', () => { - // Modal header - cy.contains('Send funds') - .should('be.visible') - .next() - .contains('Step 1 of 2') - .should('be.visible') - .next() - // Current network is same as Safe - .contains('Rinkeby') - .should('be.visible') - - // It contains the form elements - cy.get('form').within(() => { - // Sending from the current Safe address - const [chainPrefix, safeAddress] = TEST_SAFE.split(':') - cy.contains(chainPrefix) - cy.contains(safeAddress) - - // Recipient field - cy.get('#address-book-input').should('be.visible') - - // Token selector - cy.contains('Select an asset*').should('be.visible') - - // Amount field - cy.contains('Amount').should('be.visible') - }) - - // Review button is disabled - cy.get('button[type="submit"]').should('be.disabled') - }) - - it('should resolve the ENS name', () => { - // Fills recipient with ens - cy.get('label[for="address-book-input"]').next().type(RECIPIENT_ENS) - - // Waits for resolving the ENS - cy.contains(constants.RECIPIENT_ADDRESS).should('be.visible') - }) - - it('should have all tokens available in the token selector', () => { - // Click on the Token selector - cy.contains('Select an asset*').click() - - const ownedTokens = ['Dai', 'Wrapped Ether', 'Ether', 'Uniswap', 'Gnosis', '0x', 'USD Coin'] - ownedTokens.forEach((token) => { - cy.get('ul[role="listbox"]').contains(token) - }) - }) - - it('should validate token amount', () => { - // Select a token - cy.get('ul[role="listbox"]').contains('Gnosis').click() - - // Insert an incorrect amount - cy.get('input[placeholder="Amount*"]').type('0.4') - - // Selecting more than the balance is not allowed - cy.contains('Maximum value is 0.000004') - - // Form field contains an error class - cy.get('input[placeholder="Amount*"]') - // Parent div is MuiInputBase-root - .parent('div') - .should(($div) => { - // Turn the classList into an array - const classList = Array.from($div[0].classList) - expect(classList).to.include('MuiInputBase-root').and.to.include('Mui-error') - }) - - // Insert a correct amount - cy.get('input[placeholder="Amount*"]').clear().type('0.000002') - - // Form field does not contain an error class - cy.get('input[placeholder="Amount*"]') - // Parent div is MuiInputBase-root - .parent('div') - .should(($div) => { - // Turn the classList into an array - const classList = Array.from($div[0].classList) - // Check if it contains the error class - expect(classList).to.include('MuiInputBase-root').and.not.to.include('Mui-error') - }) - - // Click Send max fills the input with token total amount - cy.contains('Send max').click() - cy.get('input[placeholder="Amount*"]').should('have.value', '0.000004') - }) - - it('should advance to the Review step', () => { - // Clicks Review - cy.contains('Review').click() - - // Modal step 2 - cy.contains('Step 2 of 2').should('be.visible') - }) - }) - - describe('Review modal', () => { - before(() => { - // Wait max 10s for estimate to finish - cy.contains('Submit', { timeout: 10000 }) - }) - - it('should have the same parameters as the previous step', () => { - // Sender - cy.contains('Sending from').parent().next().contains(TEST_SAFE) - // Recipient - cy.contains('Recipient').parent().next().contains(constants.RECIPIENT_ADDRESS) - - // Token value - cy.contains('0.000004 GNO') - }) - - it('should contain a correctly estimated gas limit value', () => { - const GAS_LIMIT = '79804' // gas limit is deterministic - - // Estimated gas price is loaded - cy.contains('Estimated fee price').next().should('not.have.text', '> 0.001 ETH') - - // Click Advanced parameters - cy.contains('Estimated fee price').click() - - // Find Gas limit - cy.contains('Gas limit').next().contains(GAS_LIMIT).should('be.visible') - - // Close info again - cy.contains('Estimated fee price').click() - }) - - it('should contain the Safe nonce upon clicking Advanced parameters', () => { - // Click Advanced parameters - cy.contains('Advanced parameters').click() - // Find Safe nonce - cy.contains('Safe nonce').next().contains(SAFE_NONCE).should('be.visible') - - // Close dialog again - cy.contains('Advanced parameters').click() - }) - }) - }) -}) diff --git a/cypress/e2e/tx_simulation.cy.js b/cypress/e2e/tx_simulation.cy.js deleted file mode 100644 index 4f219d41c3..0000000000 --- a/cypress/e2e/tx_simulation.cy.js +++ /dev/null @@ -1,62 +0,0 @@ -import * as constants from '../support/constants' - -const TEST_SAFE = 'rin:0x11Df0fa87b30080d59eba632570f620e37f2a8f7' - -describe('Tx Simulation', () => { - before(() => { - // Open the Safe used for testing - cy.visit(`/${TEST_SAFE}/home`, { failOnStatusCode: false }) - cy.contains('button', 'Accept selection').click() - - // Open Send Funds Modal - cy.contains('New transaction').click() - cy.contains('Send tokens').click() - - // Choose recipient - cy.get('input[name="recipient"]').should('be.visible') - cy.get('input[name="recipient"]').type(constants.RECIPIENT_ADDRESS, { force: true }) - - // Select asset and amount - cy.get('input[name="tokenAddress"]').parent().click() - cy.get('ul[role="listbox"]').contains('Gnosis').click() - cy.contains('Max').click() - - // go to review step - cy.contains('Next').click() - }) - it('should initially have a successful simulation', () => { - // Simulate - cy.contains('Simulate').click() - - // result exists after max 10 seconds - cy.contains('The transaction was successfully simulated', { timeout: 10000 }) - }) - - it('should show unexpected error for a very low gas limit', () => { - // Set Gas Limit to too low - cy.contains('Estimated fee').click() - cy.contains('Edit').click() - cy.get('input[name="gasLimit"]').clear().type('21000') - cy.contains('Confirm').click() - - // Simulate - cy.contains('Simulate').click() - - // error exists after max 10 seconds - cy.contains('An unexpected error occurred during simulation:', { timeout: 10000 }) - }) - - it('should simulate with failed transaction for a slightly too low gas limit', () => { - // Set Gas Limit to too low - cy.contains('Estimated fee').click() - cy.contains('Edit').click() - cy.get('input[name="gasLimit"]').clear().type('75000') - cy.contains('Confirm').click() - - // Simulate - cy.contains('Simulate').click() - - // failed tx exists after max 10 seconds - cy.contains('out of gas', { timeout: 10000 }) - }) -}) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index e9dce33964..a518c1c918 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -61,6 +61,8 @@ export const validAppUrl = 'https://my-valid-custom-app.com' export const proposeEndpoint = '/**/propose' export const appsEndpoint = '/**/safe-apps' +export const transactionHistoryEndpoint = '**/history' +export const safeListEndpoint = '**/safes' export const mainSideMenuOptions = { home: 'Home', diff --git a/src/components/new-safe/create/steps/OwnerPolicyStep/index.tsx b/src/components/new-safe/create/steps/OwnerPolicyStep/index.tsx index 81ee517a65..f791ff5ffd 100644 --- a/src/components/new-safe/create/steps/OwnerPolicyStep/index.tsx +++ b/src/components/new-safe/create/steps/OwnerPolicyStep/index.tsx @@ -170,10 +170,16 @@ const OwnerPolicyStep = ({ - - diff --git a/src/components/new-safe/create/steps/ReviewStep/index.tsx b/src/components/new-safe/create/steps/ReviewStep/index.tsx index febd0bbdbd..e9b8afc372 100644 --- a/src/components/new-safe/create/steps/ReviewStep/index.tsx +++ b/src/components/new-safe/create/steps/ReviewStep/index.tsx @@ -250,7 +250,7 @@ const ReviewStep = ({ data, onSubmit, onBack, setStep }: StepRenderProps - diff --git a/src/components/settings/owner/OwnerList/index.tsx b/src/components/settings/owner/OwnerList/index.tsx index 7c484cc7be..49b6149404 100644 --- a/src/components/settings/owner/OwnerList/index.tsx +++ b/src/components/settings/owner/OwnerList/index.tsx @@ -115,6 +115,7 @@ export const OwnerList = () => { {(isOk) => ( diff --git a/src/components/tx-flow/flows/RemoveOwner/SetThreshold.tsx b/src/components/tx-flow/flows/RemoveOwner/SetThreshold.tsx index 942fbdc0ae..71b125efef 100644 --- a/src/components/tx-flow/flows/RemoveOwner/SetThreshold.tsx +++ b/src/components/tx-flow/flows/RemoveOwner/SetThreshold.tsx @@ -82,7 +82,7 @@ export const SetThreshold = ({ - diff --git a/src/pages/settings/setup.tsx b/src/pages/settings/setup.tsx index a551c4a177..3229762803 100644 --- a/src/pages/settings/setup.tsx +++ b/src/pages/settings/setup.tsx @@ -24,7 +24,7 @@ const Setup: NextPage = () => {
- +