diff --git a/.dockerignore b/.dockerignore index 355611305d..d659b8076c 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,10 +2,14 @@ Dockerfile .dockerignore node_modules npm-debug.log -README.md .next .git coverage .DS_Store .idea dist + +build/ +coverage/ +cypress/ +out/ diff --git a/.env.example b/.env.example index 6642c2c52c..4ee43536d7 100644 --- a/.env.example +++ b/.env.example @@ -40,4 +40,9 @@ NEXT_PUBLIC_FIREBASE_OPTIONS_STAGING= NEXT_PUBLIC_FIREBASE_VAPID_KEY_STAGING= # Redefine -NEXT_PUBLIC_REDEFINE_API= \ No newline at end of file +NEXT_PUBLIC_REDEFINE_API= + +# Social Login +NEXT_PUBLIC_SOCIAL_WALLET_OPTIONS_STAGING= +NEXT_PUBLIC_SOCIAL_WALLET_OPTIONS_PRODUCTION= + diff --git a/.github/workflows/build/action.yml b/.github/workflows/build/action.yml index fd57aada4f..8a8c32e0c1 100644 --- a/.github/workflows/build/action.yml +++ b/.github/workflows/build/action.yml @@ -43,6 +43,8 @@ runs: NEXT_PUBLIC_SAFE_RELAY_SERVICE_URL_STAGING: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_SAFE_GELATO_RELAY_SERVICE_URL_STAGING }} NEXT_PUBLIC_IS_OFFICIAL_HOST: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_IS_OFFICIAL_HOST }} NEXT_PUBLIC_REDEFINE_API: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_REDEFINE_API }} + NEXT_PUBLIC_SOCIAL_WALLET_OPTIONS_STAGING: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_SOCIAL_WALLET_OPTIONS_STAGING }} + NEXT_PUBLIC_SOCIAL_WALLET_OPTIONS_PRODUCTION: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_SOCIAL_WALLET_OPTIONS_PRODUCTION }} NEXT_PUBLIC_FIREBASE_OPTIONS_PRODUCTION: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_FIREBASE_OPTIONS_PRODUCTION }} NEXT_PUBLIC_FIREBASE_OPTIONS_STAGING: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_FIREBASE_OPTIONS_STAGING }} NEXT_PUBLIC_FIREBASE_VAPID_KEY_PRODUCTION: ${{ fromJSON(inputs.secrets).NEXT_PUBLIC_FIREBASE_VAPID_KEY_PRODUCTION }} diff --git a/Dockerfile b/Dockerfile index 82391964d5..9e8cba5245 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,34 @@ -FROM node:18-alpine +FROM node:18-alpine AS base +ENV NEXT_TELEMETRY_DISABLED 1 + +FROM base AS builder + RUN apk add --no-cache libc6-compat git python3 py3-pip make g++ libusb-dev eudev-dev linux-headers WORKDIR /app + +# Install dependencies +COPY package.json yarn.lock* ./ +RUN yarn --frozen-lockfile COPY . . +RUN yarn run after-install -# install deps -RUN yarn install --frozen-lockfile -RUN yarn after-install +RUN yarn build + +# Production image +FROM base AS runner +WORKDIR /app ENV NODE_ENV production +ENV REVERSE_PROXY_UI_PORT 8080 -# Next.js collects completely anonymous telemetry data about general usage. -# Learn more here: https://nextjs.org/telemetry -# Uncomment the following line in case you want to disable telemetry during the build. -ENV NEXT_TELEMETRY_DISABLED 1 +RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs +COPY --from=builder /app/out ./out + +# Set the correct permission for prerender cache +RUN mkdir .next && chown nextjs:nodejs .next -EXPOSE 3000 +USER nextjs -ENV PORT 3000 +EXPOSE ${REVERSE_PROXY_UI_PORT} -CMD ["yarn", "static-serve"] +CMD npx -y serve out -p ${REVERSE_PROXY_UI_PORT} diff --git a/cypress/e2e/pages/create_wallet.pages.js b/cypress/e2e/pages/create_wallet.pages.js index 706f7e4da1..ba4f9f8599 100644 --- a/cypress/e2e/pages/create_wallet.pages.js +++ b/cypress/e2e/pages/create_wallet.pages.js @@ -1,5 +1,7 @@ import * as constants from '../../support/constants' +const welcomeLoginScreen = '[data-testid="welcome-login"]' +const expandMoreIcon = 'svg[data-testid="ExpandMoreIcon"]' const nameInput = 'input[name="name"]' const selectNetworkBtn = '[data-cy="create-safe-select-network"]' const ownerInput = 'input[name^="owners"][name$="name"]' @@ -7,16 +9,17 @@ const ownerAddress = 'input[name^="owners"][name$="address"]' const thresholdInput = 'input[name="threshold"]' export const removeOwnerBtn = 'button[aria-label="Remove owner"]' const connectingContainer = 'div[class*="connecting-container"]' -const createNewSafeBtn = 'span[data-track="create-safe: Open stepper"]' +const createNewSafeBtn = 'span[data-track="create-safe: Continue to creation"]' +const connectWalletBtn = 'Connect wallet' const changeNetworkWarningStr = 'Change your wallet network' const safeAccountSetupStr = 'Safe Account setup' -const policy1_1 = '1/1 policy' +const policy1_2 = '1/1 policy' export const walletName = 'test1-sepolia-safe' -export const defaltSepoliaPlaceholder = 'sepolia-safe' +export const defaltSepoliaPlaceholder = 'Sepolia Safe' export function verifyPolicy1_1() { - cy.contains(policy1_1).should('exist') + cy.contains(policy1_2).should('exist') // TOD: Need data-cy for containers } @@ -49,13 +52,23 @@ export function clickOnCreateNewSafeBtn() { cy.get(createNewSafeBtn).click().wait(1000) } +export function clickOnConnectWalletBtn() { + cy.get(welcomeLoginScreen).within(() => { + cy.get('button').contains(connectWalletBtn).should('be.visible').should('be.enabled').click().wait(1000) + }) +} + export function typeWalletName(name) { cy.get(nameInput).type(name).should('have.value', name) } +export function clearWalletName() { + cy.get(nameInput).clear() +} + export function selectNetwork(network, regex = false) { cy.wait(1000) - cy.get(selectNetworkBtn).should('be.visible').click() + cy.get(expandMoreIcon).eq(1).parents('div').eq(1).click() cy.wait(1000) cy.get('li').contains(network).click() cy.get('body').click() @@ -91,7 +104,7 @@ export function typeOwnerAddress(address, index, clearOnly = false) { } export function clickOnAddNewOwnerBtn() { - cy.contains('button', 'Add new owner').click() + cy.contains('button', 'Add new owner').click().wait(700) } export function addNewOwner(name, address, index) { diff --git a/cypress/e2e/pages/dashboard.pages.js b/cypress/e2e/pages/dashboard.pages.js index 44b0bb4743..02dcdec201 100644 --- a/cypress/e2e/pages/dashboard.pages.js +++ b/cypress/e2e/pages/dashboard.pages.js @@ -5,6 +5,8 @@ const transactionQueueStr = 'Pending transactions' const noTransactionStr = 'This Safe has no queued transactions' const overviewStr = 'Overview' const viewAssetsStr = 'View assets' +const sendStr = 'Send' +const receiveStr = 'Receive' const tokensStr = 'Tokens' const nftStr = 'NFTs' const viewAllStr = 'View all' @@ -27,11 +29,10 @@ export function verifyOverviewWidgetData() { cy.get('@overviewSection').within(() => { // Prefix is separated across elements in EthHashInfo - cy.contains(constants.SEPOLIA_TEST_SAFE_5).should('exist') cy.get('h2').contains('Overview') - cy.get(`a[href="${constants.BALANCE_URL}${encodeURIComponent(constants.SEPOLIA_TEST_SAFE_5)}"]`).contains( - viewAssetsStr, - ) + cy.get(`a[href="${constants.BALANCE_URL}${encodeURIComponent(constants.SEPOLIA_TEST_SAFE_5)}"]`).contains('Tokens') + cy.get('button').contains(sendStr) + cy.get('button').contains(receiveStr) }) } diff --git a/cypress/e2e/pages/import_export.pages.js b/cypress/e2e/pages/import_export.pages.js index e5b49348ba..d2bf9b050b 100644 --- a/cypress/e2e/pages/import_export.pages.js +++ b/cypress/e2e/pages/import_export.pages.js @@ -25,7 +25,7 @@ export function clickOnImportBtn() { } export function clickOnImportBtnDataImportModal() { - cy.contains(dataImportModalStr).parent().contains('button', 'Import').click() + cy.contains('button', 'Import').click() } export function uploadFile(filePath) { @@ -44,6 +44,10 @@ export function clickOnImportedSafe(safe) { cy.contains(safe).click() } +export function clickOnOpenSafeListSidebar() { + cy.contains('My Safe Accounts').click() +} + export function clickOnClosePushNotificationsBanner() { cy.waitForSelector(() => { return cy.get('h6').contains(enablePushNotificationsStr).siblings('.MuiButtonBase-root').click({ force: true }) diff --git a/cypress/e2e/pages/load_safe.pages.js b/cypress/e2e/pages/load_safe.pages.js index bc0fa83daa..488b343bc6 100644 --- a/cypress/e2e/pages/load_safe.pages.js +++ b/cypress/e2e/pages/load_safe.pages.js @@ -1,6 +1,6 @@ import * as constants from '../../support/constants' -const addExistingAccountBtnStr = 'Add existing Account' +const addExistingAccountBtnStr = 'Add existing one' const contactStr = 'Name, address & network' const invalidAddressFormatErrorMsg = 'Invalid address format' @@ -16,7 +16,7 @@ const ownersConfirmationsStr = 'Owners and confirmations' const transactionStr = 'Transactions' export function openLoadSafeForm() { - cy.contains('button', addExistingAccountBtnStr).click() + cy.contains('a', addExistingAccountBtnStr).click() cy.contains(contactStr) } diff --git a/cypress/e2e/pages/owners.pages.js b/cypress/e2e/pages/owners.pages.js index 51d0c658f6..6e2e30aef3 100644 --- a/cypress/e2e/pages/owners.pages.js +++ b/cypress/e2e/pages/owners.pages.js @@ -19,6 +19,7 @@ const thresholdDropdown = 'div[aria-haspopup="listbox"]' const thresholdOption = 'li[role="option"]' const existingOwnerAddressInput = (index) => `input[name="owners.${index}.address"]` const existingOwnerNameInput = (index) => `input[name="owners.${index}.name"]` +const singleOwnerNameInput = 'input[name="name"]' const disconnectBtnStr = 'Disconnect' const notConnectedStatus = 'Connect' @@ -57,9 +58,9 @@ export function verifyExistingOwnerName(index, name) { cy.get(existingOwnerNameInput(index)).should('have.value', name) } -export function typeExistingOwnerName(index, name) { - cy.get(existingOwnerNameInput(index)).clear().type(name) - main.verifyInputValue(existingOwnerNameInput(index), name) +export function typeExistingOwnerName(name) { + cy.get(singleOwnerNameInput).clear().type(name) + main.verifyInputValue(singleOwnerNameInput, name) } export function verifyOwnerDeletionWindowDisplayed() { diff --git a/cypress/e2e/safe-apps/apps_list.cy.js b/cypress/e2e/safe-apps/apps_list.cy.js index 4081b813cf..0ef421f1e7 100644 --- a/cypress/e2e/safe-apps/apps_list.cy.js +++ b/cypress/e2e/safe-apps/apps_list.cy.js @@ -5,14 +5,14 @@ import * as safeapps from '../pages/safeapps.pages' const myCustomAppTitle = 'Cypress Test App' const myCustomAppDescrAdded = 'Cypress Test App Description' -describe('Safe Apps tests', () => { +describe('Safe Apps list tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.SEPOLIA_TEST_SAFE_4 + constants.appsUrl, { failOnStatusCode: false }) - main.acceptCookies(1) + main.acceptCookies() }) - it('Verify app list can be filtered by app name [C56130]', () => { + it('Verify app list can be filtered by app name', () => { // Wait for /safe-apps response cy.intercept('GET', constants.appsEndpoint).then(() => { safeapps.typeAppName(constants.appNames.walletConnect) @@ -20,29 +20,29 @@ describe('Safe Apps tests', () => { }) }) - it('Verify app list can be filtered by app description [C56131]', () => { + it('Verify app list can be filtered by app description', () => { safeapps.typeAppName(constants.appNames.customContract) safeapps.verifyLinkName(safeapps.linkNames.logo) }) - it('Verify error message is displayed when no app found [C56132]', () => { + it('Verify error message is displayed when no app found', () => { safeapps.typeAppName(constants.appNames.noResults) safeapps.verifyNoAppsTextPresent() }) - it('Verify apps can be pinned [C56133]', () => { + it('Verify apps can be pinned', () => { safeapps.clearSearchAppInput() safeapps.pinApp(safeapps.transactionBuilderStr) safeapps.verifyPinnedAppCount(1) }) - it('Verify apps can be unpinned [C56134]', () => { + it('Verify apps can be unpinned', () => { safeapps.pinApp(safeapps.transactionBuilderStr) safeapps.pinApp(safeapps.transactionBuilderStr, false) safeapps.verifyPinnedAppCount(0) }) - it('Verify there is an error when the app manifest is invalid [C56135]', () => { + it('Verify there is an error when the app manifest is invalid', () => { cy.intercept('GET', constants.invalidAppUrl, { name: constants.testAppData.name, }) @@ -52,7 +52,7 @@ describe('Safe Apps tests', () => { safeapps.verifyAppNotSupportedMsg() }) - it('Verify an app can be added to the list within the custom apps section [C56136]', () => { + it('Verify an app can be added to the list within the custom apps section', () => { cy.intercept('GET', constants.validAppUrlJson, { name: constants.testAppData.name, description: constants.testAppData.descr, diff --git a/cypress/e2e/safe-apps/browser_permissions.cy.js b/cypress/e2e/safe-apps/browser_permissions.cy.js index da34de03ab..59cbceffed 100644 --- a/cypress/e2e/safe-apps/browser_permissions.cy.js +++ b/cypress/e2e/safe-apps/browser_permissions.cy.js @@ -15,16 +15,16 @@ describe('Browser permissions tests', () => { }) }) cy.visitSafeApp(`${constants.testAppUrl}/app`) - main.acceptCookies(1) + main.acceptCookies() }) - it('Verify a permissions slide to the user is displayed [C56137]', () => { + it('Verify a permissions slide to the user is displayed', () => { safeapps.clickOnContinueBtn() safeapps.verifyCameraCheckBoxExists() safeapps.verifyMicrofoneCheckBoxExists() }) - it('Verify the selection can be changed, accepted and stored [C56138]', () => { + it('Verify the selection can be changed, accepted and stored', () => { safeapps.verifyMicrofoneCheckBoxExists().click() safeapps.clickOnContinueBtn() safeapps.verifyWarningDefaultAppMsgIsDisplayed() diff --git a/cypress/e2e/safe-apps/drain_account.spec.cy.js b/cypress/e2e/safe-apps/drain_account.spec.cy.js index 87d878f307..773559d748 100644 --- a/cypress/e2e/safe-apps/drain_account.spec.cy.js +++ b/cypress/e2e/safe-apps/drain_account.spec.cy.js @@ -3,7 +3,7 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as safeapps from '../pages/safeapps.pages' -describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => { +describe('Drain Account tests', { defaultCommandTimeout: 12000 }, () => { const appUrl = constants.drainAccount_url const iframeSelector = `iframe[id="iframe-${appUrl}"]` const visitUrl = `/apps/open?safe=${constants.GOERLI_SAFE_APPS_SAFE}&appUrl=${encodeURIComponent(appUrl)}` @@ -15,11 +15,11 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => cy.clearLocalStorage() cy.visit(visitUrl) - main.acceptCookies(1) + main.acceptCookies() safeapps.clickOnContinueBtn() }) - it('Verify drain can be created [C56627]', () => { + it('Verify drain can be created', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.recipientStr).type(constants.SAFE_APP_ADDRESS_2) getBody().findAllByText(safeapps.transferEverythingStr).click() @@ -28,7 +28,7 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => cy.findByRole('button', { name: safeapps.testNativeTransfer2 }) }) - it('Verify partial drain can be created [C56628]', () => { + it('Verify partial drain can be created', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.selectAllRowsChbxStr).click() getBody().findAllByLabelText(safeapps.selectRowChbxStr).eq(1).click() @@ -40,7 +40,7 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => cy.findByRole('button', { name: safeapps.testNativeTransfer1 }) }) - it('Verify a drain can be created when a ENS is specified [C56629]', () => { + it('Verify a drain can be created when a ENS is specified', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.recipientStr).type('goerli-test-safe.eth').wait(2000) getBody().findAllByText(safeapps.transferEverythingStr).click() @@ -50,7 +50,7 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => }) // TODO: Adjust safe - owner - it.skip('Verify when cancelling a drain, previous data is preserved [C56630]', () => { + it.skip('Verify when cancelling a drain, previous data is preserved', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.recipientStr).type(constants.SAFE_APP_ADDRESS_2) getBody().findAllByText(safeapps.transferEverythingStr).click() @@ -61,14 +61,14 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => }) }) - it('Verify a drain cannot be created with no recipient selected [C56631]', () => { + it('Verify a drain cannot be created with no recipient selected', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findAllByText(safeapps.transferEverythingStr).click() getBody().findByText(safeapps.validRecipientAddressStr) }) }) - it('Verify a drain cannot be created with invalid recipient selected [C56632]', () => { + it('Verify a drain cannot be created with invalid recipient selected', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.recipientStr).type(constants.SAFE_APP_ADDRESS_2.substring(1)) getBody().findAllByText(safeapps.transferEverythingStr).click() @@ -76,7 +76,7 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => }) }) - it('Verify a drain cannot be created when no assets are selected [C56633]', () => { + it('Verify a drain cannot be created when no assets are selected', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.selectAllRowsChbxStr).click() getBody().findByLabelText(safeapps.recipientStr).type(constants.SAFE_APP_ADDRESS_2) @@ -84,7 +84,7 @@ describe('Drain Account Safe App tests', { defaultCommandTimeout: 12000 }, () => }) }) - it('should not allow to perform a drain when no assets and recipient are selected', () => { + it('Verify a drain cannot be created when no assets and recipient are selected', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.selectAllRowsChbxStr).click() getBody().findAllByText(safeapps.noTokensSelectedStr).should('be.visible') diff --git a/cypress/e2e/safe-apps/info_modal.cy.js b/cypress/e2e/safe-apps/info_modal.cy.js index 650fe1b041..7aeab75e2e 100644 --- a/cypress/e2e/safe-apps/info_modal.cy.js +++ b/cypress/e2e/safe-apps/info_modal.cy.js @@ -2,27 +2,27 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as safeapps from '../pages/safeapps.pages' -describe('Safe Apps info modal tests', () => { +describe('Info modal tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.SEPOLIA_TEST_SAFE_5 + constants.appsUrl, { failOnStatusCode: false }) main.acceptCookies() }) - it('Verify the disclaimer is displayed when a Safe App is opened [C56139]', () => { + it('Verify the disclaimer is displayed when a Safe App is opened', () => { safeapps.clickOnApp(safeapps.transactionBuilderStr) safeapps.clickOnOpenSafeAppBtn() }) // Skip tests due to changed logic // TODO: Discuss furthers - it.skip('Verify the permissions slide is shown if the app require permissions [C56140]', () => { + it.skip('Verify the permissions slide is shown if the app require permissions', () => { safeapps.clickOnContinueBtn() cy.wait(500) // wait for the animation to finish safeapps.verifyCameraCheckBoxExists() }) - it.skip('Verify the permissions and consents decision are stored when accepted [C56141]', () => { + it.skip('Verify the permissions and consents decision are stored when accepted', () => { safeapps.storeAndVerifyPermissions() }) }) diff --git a/cypress/e2e/safe-apps/permissions_settings.cy.js b/cypress/e2e/safe-apps/permissions_settings.cy.js index ca88523a74..41484f9638 100644 --- a/cypress/e2e/safe-apps/permissions_settings.cy.js +++ b/cypress/e2e/safe-apps/permissions_settings.cy.js @@ -6,7 +6,7 @@ let $dapps = [] const app1 = 'https://app1.com' const app3 = 'https://app3.com' -describe('Safe Apps permissions settings tests', () => { +describe('Permissions settings tests', () => { before(() => { cy.clearLocalStorage() cy.on('window:before:load', (window) => { @@ -49,18 +49,18 @@ describe('Safe Apps permissions settings tests', () => { main.acceptCookies() }) - it('Verify for each stored app the permissions configuration is shown [C56142]', () => { + it('Verify for each stored app the permissions configuration is shown', () => { cy.findAllByRole('heading', { level: 5 }).should('have.length', 4) }) - describe('Permissions for each app', () => { + describe('Permissions for each Safe app', () => { before(() => { cy.get(safeapps.gridItem).then((items) => { $dapps = items }) }) - it('Verify that app1 has camera, full screen and geo permissions [C56143]', () => { + it('Verify that app1 has camera, full screen and geo permissions', () => { const app1Data = [ 'app1', safeapps.permissionCheckboxNames.camera, @@ -74,7 +74,7 @@ describe('Safe Apps permissions settings tests', () => { main.verifyCheckboxeState(safeapps.permissionCheckboxes.fullscreen, 0, constants.checkboxStates.checked) }) - it('Verify that app2 has address book and microphone permissions [C56144]', () => { + it('Verify that app2 has address book and microphone permissions', () => { const app2Data = [ 'app2', safeapps.permissionCheckboxNames.addressbook, @@ -86,21 +86,21 @@ describe('Safe Apps permissions settings tests', () => { main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 0, constants.checkboxStates.checked) }) - it('Verify that app3 has camera permissions [C56145]', () => { + it('Verify that app3 has camera permissions', () => { const app3Data = ['app3', safeapps.permissionCheckboxNames.camera] main.checkTextsExistWithinElement($dapps[2], app3Data) main.verifyCheckboxeState(safeapps.permissionCheckboxes.camera, 1, constants.checkboxStates.unchecked) }) - it('Verify that app4 has address book permissions [C56146]', () => { + it('Verify that app4 has address book permissions', () => { const app4Data = ['app4', safeapps.permissionCheckboxNames.addressbook] main.checkTextsExistWithinElement($dapps[3], app4Data) main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 1, constants.checkboxStates.checked) }) - it('Verify Allow all or Clear all the checkboxes at once is permitted [C56147]', () => { + it('Verify Allow all or Clear all the checkboxes at once is permitted', () => { safeapps.uncheckAllPermissions($dapps[1]) main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 0, constants.checkboxStates.unchecked) main.verifyCheckboxeState(safeapps.permissionCheckboxes.microphone, 0, constants.checkboxStates.unchecked) @@ -110,7 +110,7 @@ describe('Safe Apps permissions settings tests', () => { main.verifyCheckboxeState(safeapps.permissionCheckboxes.microphone, 0, constants.checkboxStates.checked) }) - it('Verify it is permitted to remove apps and reflect it in the localStorage [C56148]', () => { + it('Verify it is permitted to remove apps and reflect it in the localStorage', () => { cy.wrap($dapps[0]).find('svg').last().click() cy.wrap($dapps[2]) .find('svg') diff --git a/cypress/e2e/safe-apps/preview_drawer.cy.js b/cypress/e2e/safe-apps/preview_drawer.cy.js index 985a1bcad6..d9d3b35cf4 100644 --- a/cypress/e2e/safe-apps/preview_drawer.cy.js +++ b/cypress/e2e/safe-apps/preview_drawer.cy.js @@ -2,14 +2,14 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as safeapps from '../pages/safeapps.pages' -describe('Safe Apps info modal tests', () => { +describe('Preview drawer tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(`/${constants.SEPOLIA_TEST_SAFE_5}/apps`, { failOnStatusCode: false }) main.acceptCookies() }) - it('Verify the preview drawer is displayed when opening a Safe App from the app list [C56149]', () => { + it('Verify the preview drawer is displayed when opening a Safe App from the app list', () => { safeapps.clickOnApp(safeapps.transactionBuilderStr) cy.findByRole('presentation').within(() => { diff --git a/cypress/e2e/safe-apps/safe_permissions.cy.js b/cypress/e2e/safe-apps/safe_permissions.cy.js index 771beee30d..0b64e41ad9 100644 --- a/cypress/e2e/safe-apps/safe_permissions.cy.js +++ b/cypress/e2e/safe-apps/safe_permissions.cy.js @@ -15,9 +15,9 @@ describe('Safe permissions system tests', () => { }) }) - it('Verify that requesting permissions with wallet_requestPermissions shows the permissions prompt and return the permissions on accept [C56150]', () => { + it('Verify that requesting permissions with wallet_requestPermissions shows the permissions prompt and return the permissions on accept', () => { cy.visitSafeApp(constants.testAppUrl + constants.requestPermissionsUrl) - main.acceptCookies(1) + main.acceptCookies() safeapps.clickOnContinueBtn() safeapps.verifyWarningDefaultAppMsgIsDisplayed() safeapps.clickOnContinueBtn() @@ -38,7 +38,7 @@ describe('Safe permissions system tests', () => { }) }) - it('Verify that trying to get the current permissions with wallet_getPermissions returns the current permissions [C56151]', () => { + it('Verify that trying to get the current permissions with wallet_getPermissions returns the current permissions', () => { cy.on('window:before:load', (window) => { window.localStorage.setItem( constants.SAFE_PERMISSIONS_KEY, @@ -56,7 +56,7 @@ describe('Safe permissions system tests', () => { }) cy.visitSafeApp(constants.testAppUrl + constants.getPermissionsUrl) - main.acceptCookies(1) + main.acceptCookies() safeapps.clickOnContinueBtn() safeapps.verifyWarningDefaultAppMsgIsDisplayed() safeapps.clickOnContinueBtn() diff --git a/cypress/e2e/safe-apps/tx-builder.spec.cy.js b/cypress/e2e/safe-apps/tx-builder.spec.cy.js index d3444df3a2..057d12a217 100644 --- a/cypress/e2e/safe-apps/tx-builder.spec.cy.js +++ b/cypress/e2e/safe-apps/tx-builder.spec.cy.js @@ -3,18 +3,18 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as safeapps from '../pages/safeapps.pages' -describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { +describe('Transaction Builder tests', { defaultCommandTimeout: 20000 }, () => { const appUrl = constants.TX_Builder_url const iframeSelector = `iframe[id="iframe-${appUrl}"]` const visitUrl = `/apps/open?safe=${constants.GOERLI_SAFE_APPS_SAFE}&appUrl=${encodeURIComponent(appUrl)}` beforeEach(() => { cy.clearLocalStorage() cy.visit(visitUrl) - main.acceptCookies(1) + main.acceptCookies() safeapps.clickOnContinueBtn() }) - it('Verify a simple batch can be created [C56609]', () => { + it('Verify a simple batch can be created', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS) getBody().find(safeapps.contractMethodIndex).parent().click() @@ -32,7 +32,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.findAllByText(constants.SAFE_APP_ADDRESS_2_SHORT).should('have.length', 1) }) - it('Verify a complex batch can be created [C56610]', () => { + it('Verify a complex batch can be created', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS) getBody().find(safeapps.contractMethodIndex).parent().click() @@ -54,7 +54,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.findAllByText('True').should('have.length', 3) }) - it('Verify a batch can be created using ENS name [C56611]', () => { + it('Verify a batch can be created using ENS name', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.ENS_TEST_GOERLI) getBody().findByRole('button', { name: safeapps.useImplementationABI }).click() @@ -72,7 +72,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.findByText(safeapps.thresholdStr2).should('exist') }) - it('Verify a batch can be created from an ABI [C56612]', () => { + it('Verify a batch can be created from an ABI', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterABIStr).type(safeapps.abi) getBody().findByLabelText(safeapps.toAddressStr).type(constants.SAFE_APP_ADDRESS_2) @@ -85,7 +85,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.findByText(constants.SAFE_APP_ADDRESS_2).should('be.visible') }) - it('Verify a batch with custom data can be created [C56613]', () => { + it('Verify a batch with custom data can be created', () => { cy.enter(iframeSelector).then((getBody) => { getBody().find('.MuiSwitch-root').click() getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS_3) @@ -99,7 +99,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.findByText(constants.SAFE_APP_ADDRESS_3).should('be.visible') }) - it('Verify a batch can be cancelled [C56614]', () => { + it('Verify a batch can be cancelled', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS) getBody().find(safeapps.contractMethodIndex).parent().click() @@ -114,7 +114,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify cancel operation can be reverted [C56615]', () => { + it('Verify cancel operation can be reverted', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS) getBody().find(safeapps.contractMethodIndex).parent().click() @@ -129,7 +129,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify it is allowed to go back without removing data and add more transactions to the batch [C56616]', () => { + it('Verify it is allowed to go back without removing data and add more transactions to the batch', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS) getBody().find(safeapps.contractMethodIndex).parent().click() @@ -150,14 +150,14 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.get('p').contains('2').should('exist') }) - it('Verify a batch cannot be created with invalid address [C56617]', () => { + it('Verify a batch cannot be created with invalid address', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS_3) getBody().findAllByText(safeapps.addressNotValidStr).should('have.css', 'color', 'rgb(244, 67, 54)') }) }) - it('Verify a batch cannot be created without asset amount [C56618]', () => { + it('Verify a batch cannot be created without asset amount', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS_3) getBody().findByText(safeapps.addTransactionStr).click() @@ -165,7 +165,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify a batch cannot be created without method data [C56619]', () => { + it('Verify a batch cannot be created without method data', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS_2) getBody().findByText(safeapps.addTransactionStr).click() @@ -173,7 +173,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify a batch can be uploaded, saved, downloaded and removed [C56620]', () => { + it('Verify a batch can be uploaded, saved, downloaded and removed', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findAllByText('choose a file').attachFile('test-working-batch.json', { subjectType: 'drag-n-drop' }) getBody().findAllByText('uploaded').wait(300) @@ -190,7 +190,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { cy.readFile('cypress/downloads/E2E test.json').should('exist') }) - it('Verify there is notification if uploaded batch is from a different chain [C56621]', () => { + it('Verify there is notification if uploaded batch is from a different chain', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findAllByText('choose a file').attachFile('test-mainnet-batch.json', { subjectType: 'drag-n-drop' }) getBody().findAllByText(safeapps.warningStr).should('be.visible') @@ -198,7 +198,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify there is error message when a modified batch is uploaded [C56622]', () => { + it('Verify there is error message when a modified batch is uploaded', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findAllByText('choose a file').attachFile('test-modified-batch.json', { subjectType: 'drag-n-drop' }) getBody().findAllByText(safeapps.changedPropertiesStr) @@ -206,7 +206,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify an invalid batch cannot be uploaded [C56623]', () => { + it('Verify an invalid batch cannot be uploaded', () => { cy.enter(iframeSelector).then((getBody) => { getBody() .findAllByText('choose a file') @@ -216,7 +216,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify an empty batch cannot be uploaded [C56624]', () => { + it('Verify an empty batch cannot be uploaded', () => { cy.enter(iframeSelector).then((getBody) => { getBody() .findAllByText('choose a file') @@ -226,7 +226,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify a valid batch as successful can be simulated [C56625]', () => { + it('Verify a valid batch as successful can be simulated', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.SAFE_APP_ADDRESS_3) getBody().findByLabelText(safeapps.gorValue).type('0') @@ -238,7 +238,7 @@ describe('Tx-builder Safe App tests', { defaultCommandTimeout: 20000 }, () => { }) }) - it('Verify an invalid batch as failed can be simulated [C56626]', () => { + it('Verify an invalid batch as failed can be simulated', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.enterAddressStr).type(constants.TEST_SAFE_2) getBody().findByText(safeapps.keepProxiABIStr).click() diff --git a/cypress/e2e/safe-apps/tx_modal.cy.js b/cypress/e2e/safe-apps/tx_modal.cy.js index b2961038a1..38e46c2d03 100644 --- a/cypress/e2e/safe-apps/tx_modal.cy.js +++ b/cypress/e2e/safe-apps/tx_modal.cy.js @@ -19,7 +19,7 @@ describe('Transaction modal tests', () => { }) it( - 'Verify that the transaction popup is displayed when sending a transaction from an app [C56152]', + 'Verify that the transaction popup is displayed when sending a transaction from an app', { defaultCommandTimeout: 12000 }, () => { cy.visitSafeApp(`${constants.testAppUrl}/dummy`) diff --git a/cypress/e2e/smoke/add_owner.cy.js b/cypress/e2e/smoke/add_owner.cy.js index a36635750c..2397881b65 100644 --- a/cypress/e2e/smoke/add_owner.cy.js +++ b/cypress/e2e/smoke/add_owner.cy.js @@ -7,39 +7,39 @@ describe('Add Owners tests', () => { beforeEach(() => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) cy.clearLocalStorage() - main.acceptCookies(1) + main.acceptCookies() cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) }) - it('Verify the presence of "Add Owner" button [C56017]', () => { + it('Verify the presence of "Add Owner" button', () => { owner.verifyAddOwnerBtnIsEnabled() }) - it('Verify “Add new owner” button tooltip displays correct message for Non-Owner [C56018]', () => { + it('Verify “Add new owner” button tooltip displays correct message for Non-Owner', () => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_2) owner.verifyAddOwnerBtnIsDisabled() }) - it('Verify Tooltip displays correct message for disconnected user [C56019]', () => { + 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 [C56020]', () => { + 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 [C56022]', () => { + 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 [C56023]', () => { + 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) @@ -54,7 +54,7 @@ describe('Add Owners tests', () => { owner.verifyNewOwnerName(constants.addresBookContacts.user1.name) }) - it('Verify that Name field not mandatory [C56024]', () => { + it('Verify that Name field not mandatory', () => { owner.waitForConnectionStatus() owner.openAddOwnerWindow() owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) @@ -62,7 +62,7 @@ describe('Add Owners tests', () => { owner.verifyConfirmTransactionWindowDisplayed() }) - it('Verify relevant error messages are displayed in Address input [C56025]', () => { + it('Verify relevant error messages are displayed in Address input', () => { owner.waitForConnectionStatus() owner.openAddOwnerWindow() owner.typeOwnerAddress(main.generateRandomString(10)) @@ -81,14 +81,14 @@ describe('Add Owners tests', () => { owner.verifyErrorMsgInvalidAddress(constants.addressBookErrrMsg.alreadyAdded) }) - it('Verify default threshold value. Verify correct threshold calculation [C56028]', () => { + it('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 [C56027]', () => { + it('Verify valid Address validation', () => { owner.waitForConnectionStatus() owner.openAddOwnerWindow() owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) diff --git a/cypress/e2e/smoke/address_book.cy.js b/cypress/e2e/smoke/address_book.cy.js index 3a5af18932..b0af66eecd 100644 --- a/cypress/e2e/smoke/address_book.cy.js +++ b/cypress/e2e/smoke/address_book.cy.js @@ -12,15 +12,15 @@ describe('Address book tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.addressBookUrl + constants.SEPOLIA_TEST_SAFE_1) - main.acceptCookies(1) + main.acceptCookies() }) - it('Verify entry can be added [C56061]', () => { + it('Verify entry can be added', () => { addressBook.clickOnCreateEntryBtn() addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) }) - it('Verify entered entry in Name input can be saved [C56063]', () => { + it('Verify entered entry in Name input can be saved', () => { addressBook.clickOnCreateEntryBtn() addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) addressBook.clickOnEditEntryBtn() @@ -29,7 +29,7 @@ describe('Address book tests', () => { addressBook.verifyNameWasChanged(NAME, EDITED_NAME) }) - it('Verify entry can be deleted [C56062]', () => { + it('Verify entry can be deleted', () => { addressBook.clickOnCreateEntryBtn() addressBook.addEntry(NAME, constants.RECIPIENT_ADDRESS) // Click the delete button in the first entry @@ -38,14 +38,14 @@ describe('Address book tests', () => { addressBook.verifyEditedNameNotExists(EDITED_NAME) }) - it('Verify csv file can be imported (Goerli) [C56064]', () => { + it('Verify csv file can be imported (Goerli)', () => { 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 [C56066]', () => { + it.skip('Verify Gnosis Chain imported address can be found', () => { // Go to a Safe on Gnosis Chain cy.get('header') .contains(/^G(ö|oe)rli$/) @@ -63,7 +63,7 @@ describe('Address book tests', () => { cy.contains(constants.GNO_CSV_ENTRY.address).should('exist') }) - it('Verify the address book file can be downloaded [C56065]', () => { + it('Verify the address book file can be downloaded', () => { addressBook.clickOnImportFileBtn() addressBook.importFile() // Download the export file diff --git a/cypress/e2e/smoke/assets.cy.js b/cypress/e2e/smoke/assets.cy.js index aae5c455ad..ebdf8cad3a 100644 --- a/cypress/e2e/smoke/assets.cy.js +++ b/cypress/e2e/smoke/assets.cy.js @@ -13,18 +13,18 @@ describe('Assets tests', () => { beforeEach(() => { cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) cy.clearLocalStorage() - main.acceptCookies(2) + main.acceptCookies() }) - it('Verify that the token tab is selected by default and the table is visible [C56039]', () => { + it('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 [C56040]', () => { + it('Verify that the native token is visible', () => { balances.verifyTokenIsPresent(constants.tokenNames.sepoliaEther) }) - it('Verify that non-native tokens are present and have balance [C56041]', () => { + 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( @@ -81,7 +81,7 @@ describe('Assets tests', () => { ) }) - it('Verify that every token except the native token has a "go to blockexplorer link" [C56042]', () => { + 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) @@ -93,7 +93,7 @@ describe('Assets tests', () => { balances.verifyAssetExplorerLinkNotAvailable(constants.tokenNames.sepoliaEther, ASSET_NAME_COLUMN) }) - it('Verify the default Fiat currency and the effects after changing it [C56043]', () => { + 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) @@ -103,12 +103,12 @@ describe('Assets tests', () => { balances.verifyFirstRowContainsCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) }) - it('Verify that a tool tip is shown pointing to "Token list" dropdown [C56044]', () => { + 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 [C56045]', () => { + it('Verify that Token list dropdown down options show/hide spam tokens', () => { let spamTokens = [ balances.currencyAave, balances.currencyTestTokenA, @@ -124,20 +124,20 @@ describe('Assets tests', () => { main.verifyValuesExist(balances.tokenListTable, spamTokens) }) - it('Verify that "Hide token" button is present and opens the "Hide tokens menu" [C56046]', () => { + it('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 [C56047]', () => { + 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 [C56048]', () => { + it('Verify that selecting tokens and saving hides them from the table', () => { balances.selectTokenList(balances.tokenListOptions.allTokens) balances.openHideTokenMenu() balances.clickOnTokenCheckbox(balances.currencyLink) @@ -145,7 +145,7 @@ describe('Assets tests', () => { main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink]) }) - it('Verify that Cancel closes the menu and does not change the table status [C56049]', () => { + 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) @@ -159,7 +159,7 @@ describe('Assets tests', () => { main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) }) - it('Verify that Deselect All unchecks all tokens from the list [C56050]', () => { + it('Verify that Deselect All unchecks all tokens from the list', () => { balances.selectTokenList(balances.tokenListOptions.allTokens) balances.openHideTokenMenu() balances.clickOnTokenCheckbox(balances.currencyLink) @@ -168,7 +168,7 @@ describe('Assets tests', () => { balances.verifyEachRowHasCheckbox(constants.checkboxStates.unchecked) }) - it('Verify the Hidden tokens counter works for spam tokens [C56051]', () => { + it('Verify the Hidden tokens counter works for spam tokens', () => { balances.selectTokenList(balances.tokenListOptions.allTokens) balances.openHideTokenMenu() balances.clickOnTokenCheckbox(balances.currencyLink) @@ -176,19 +176,19 @@ describe('Assets tests', () => { balances.checkHiddenTokenBtnCounter(1) }) - it('Verify the Hidden tokens counter works for native tokens [C56056]', () => { + 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 [C56053]', () => { + 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 [C56052]', () => { + it('Verify the sorting of "Assets" and "Balance" in the table', () => { balances.selectTokenList(balances.tokenListOptions.allTokens) balances.verifyTableRows(7) balances.clickOnTokenNameSortBtn() @@ -201,20 +201,18 @@ describe('Assets tests', () => { balances.verifyTokenBalanceOrder('descending') }) - it('Verify that clicking the button with an owner opens the Send funds form [C56055]', () => { + it('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 [C56054]', () => { + 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) - cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_4) - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.showSendBtn(0) - owner.verifyTooltiptext(owner.nonOwnerErrorMsg) + // 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/smoke/balances.cy.js index df8e1c3681..9638173827 100644 --- a/cypress/e2e/smoke/balances.cy.js +++ b/cypress/e2e/smoke/balances.cy.js @@ -14,32 +14,33 @@ describe('Balance tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - main.acceptCookies(2) + main.acceptCookies() cy.contains('Assets') + balances.selectTokenList(balances.tokenListOptions.default) cy.get(balances.balanceSingleRow).should('have.length.lessThan', ASSETS_LENGTH) balances.selectTokenList(balances.tokenListOptions.allTokens) cy.get(balances.balanceSingleRow).should('have.length', ASSETS_LENGTH) }) - it('Verify that token is present: Dai [C56074]', () => { + it('Verify that token is present: Dai', () => { balances.verityTokenAltImageIsVisible(balances.currencyDaiCap, balances.currencyDaiAlttext) balances.verifyAssetNameHasExplorerLink(balances.currencyDaiCap, ASSET_NAME_COLUMN) balances.verifyBalance(balances.currencyDaiCap, TOKEN_AMOUNT_COLUMN, balances.currencyDaiAlttext) }) - it('Verify that token is present: AAVE [C56075]', () => { + it('Verify that token is present: AAVE', () => { balances.verityTokenAltImageIsVisible(balances.currencyAave, balances.currencyAaveAlttext) balances.verifyAssetNameHasExplorerLink(balances.currencyAave, ASSET_NAME_COLUMN) balances.verifyBalance(balances.currencyAave, TOKEN_AMOUNT_COLUMN, balances.currencyAaveAlttext) }) - it('Verify that token is present: LINK [C56076]', () => { + it('Verify that token is present: LINK', () => { balances.verityTokenAltImageIsVisible(balances.currencyLink, balances.currencyLinkAlttext) balances.verifyAssetNameHasExplorerLink(balances.currencyLink, ASSET_NAME_COLUMN) balances.verifyBalance(balances.currencyLink, TOKEN_AMOUNT_COLUMN, balances.currencyLinkAlttext) }) - it('Verify Token and Fiat balances formatted as per specification [C56077]', () => { + it('Verify Token and Fiat balances formatted as per specification', () => { balances.verifyTokenBalanceFormat( balances.currencyDaiCap, balances.currentcyDaiFormat, @@ -97,23 +98,23 @@ describe('Balance tests', () => { ) }) - it('Verify USD is default currency [C56078]', () => { + it('Verify USD is default currency', () => { balances.verifyFirstRowDoesNotContainCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) balances.verifyFirstRowContainsCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) }) - it('Verify currency can be changed to EUR [C56079]', () => { + it('Verify currency can be changed to EUR', () => { balances.clickOnCurrencyDropdown() balances.selectCurrency(balances.currencyEUR) balances.verifyFirstRowDoesNotContainCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) balances.verifyFirstRowContainsCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) }) - it('Verify a token can be hidden [C56080]', () => { + it('Verify a token can be hidden', () => { balances.hideAsset(balances.currencyDaiCap) }) - it('Verify a token can be unhidden [C56081]', () => { + it('Verify a token can be unhidden', () => { balances.hideAsset(balances.currencyDaiCap) balances.openHideTokenMenu() balances.clickOnTokenCheckbox(balances.currencyDaiCap) diff --git a/cypress/e2e/smoke/balances_pagination.cy.js b/cypress/e2e/smoke/balances_pagination.cy.js index b1033b9242..767780c633 100644 --- a/cypress/e2e/smoke/balances_pagination.cy.js +++ b/cypress/e2e/smoke/balances_pagination.cy.js @@ -4,17 +4,17 @@ import * as main from '../../e2e/pages/main.page' const ASSETS_LENGTH = 8 -describe('Balance tests', () => { +describe('Balance pagination tests', () => { before(() => { cy.clearLocalStorage() // Open the Safe used for testing cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_6) - main.acceptCookies(2) + main.acceptCookies() balances.selectTokenList(balances.tokenListOptions.allTokens) }) - it('Verify a user can change rows per page and navigate to next and previous page [C56073]', () => { + it('Verify a user can change rows per page and navigate to next and previous page', () => { balances.verifyInitialTableState() balances.changeTo10RowsPerPage() balances.verifyTableHas10Rows() diff --git a/cypress/e2e/smoke/batch_tx.cy.js b/cypress/e2e/smoke/batch_tx.cy.js index 507b2b3ecd..c3e93d08fc 100644 --- a/cypress/e2e/smoke/batch_tx.cy.js +++ b/cypress/e2e/smoke/batch_tx.cy.js @@ -1,6 +1,7 @@ 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' @@ -10,20 +11,21 @@ describe('Batch transaction tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - main.acceptCookies(2) + owner.waitForConnectionStatus() + main.acceptCookies() }) - it('Verify empty batch list can be opened [C56082]', () => { + it('Verify empty batch list can be opened', () => { batch.openBatchtransactionsModal() batch.openNewTransactionModal() }) - it('Verify the Add batch button is present in a transaction form [C56084]', () => { + 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 [C56085]', () => { + it('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 @@ -32,7 +34,7 @@ describe('Batch transaction tests', () => { batch.verifyAmountTransactionsInBatch(1) }) - it('Verify a second transaction can be added to the batch [C56086]', () => { + 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) @@ -41,7 +43,7 @@ describe('Batch transaction tests', () => { batch.verifyAmountTransactionsInBatch(2) }) - it('Verify the batch can be confirmed and related transactions exist in the form [C56088]', () => { + it('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) @@ -56,7 +58,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 [C56089]', () => { + it('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/beamer.cy.js b/cypress/e2e/smoke/beamer.cy.js index ab0ee152b9..4280e7f15a 100644 --- a/cypress/e2e/smoke/beamer.cy.js +++ b/cypress/e2e/smoke/beamer.cy.js @@ -9,7 +9,7 @@ describe('Beamer tests', () => { main.acceptCookies() }) - it.skip('Verify "Updates" cookie acceptance is required before displaying Beamer [C56090]', () => { + it.skip('Verify "Updates" cookie acceptance is required before displaying Beamer', () => { addressbook.clickOnWhatsNewBtn() addressbook.acceptBeamerCookies() addressbook.verifyBeamerIsChecked() diff --git a/cypress/e2e/smoke/create_safe_simple.cy.js b/cypress/e2e/smoke/create_safe_simple.cy.js index 9dc90f4723..1b924cd743 100644 --- a/cypress/e2e/smoke/create_safe_simple.cy.js +++ b/cypress/e2e/smoke/create_safe_simple.cy.js @@ -1,74 +1,77 @@ 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.createNewSafeSepoliaUrl) + cy.visit(constants.welcomeUrl + '?chain=sep') cy.clearLocalStorage() main.acceptCookies() }) - - it('Verify a Wallet can be connected [C56101]', () => { - owner.waitForConnectionStatus() - cy.visit(constants.welcomeUrl) + it('Verify a Wallet can be connected', () => { + createwallet.clickOnCreateNewSafeBtn() owner.clickOnWalletExpandMoreIcon() owner.clickOnDisconnectBtn() - createwallet.clickOnCreateNewSafeBtn() - owner.clickOnConnectBtn() + createwallet.clickOnConnectWalletBtn() createwallet.connectWallet() }) - it('Verify Next button is disabled until switching to network is done [C56102]', () => { + 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 [C56099]', () => { + it('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 [C56098]', () => { + 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() }) - it('Verify there is no error message is displayed if wallet name input contains less than 50 characters [C56100]', () => { + 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 [C56091]', () => { + 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 [C56092]', () => { + it('Verify error message is displayed if owner name input exceeds 50 characters', () => { owner.waitForConnectionStatus() - owner.clickOnNextBtn() - owner.typeExistingOwnerName(0, main.generateRandomString(51)) + 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 [C56093]', () => { + it('Verify there is no error message is displayed if owner name input contains less than 50 characters', () => { owner.waitForConnectionStatus() - owner.clickOnNextBtn() - owner.typeExistingOwnerName(0, main.generateRandomString(50)) + createwallet.clickOnCreateNewSafeBtn() + owner.typeExistingOwnerName(main.generateRandomString(50)) owner.verifyValidWalletName(constants.addressBookErrrMsg.exceedChars) }) - it('Verify Add and Remove Owner Row works as expected [C56094]', () => { + it('Verify Add and Remove Owner Row works as expected', () => { owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() owner.clickOnNextBtn() createwallet.clickOnAddNewOwnerBtn() owner.verifyNumberOfOwners(2) @@ -80,28 +83,36 @@ describe('Safe creation tests', () => { owner.verifyNumberOfOwners(2) }) - it('Verify Threshold Setup [C56096]', () => { + it('Verify Threshold Setup', () => { owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() owner.clickOnNextBtn() createwallet.clickOnAddNewOwnerBtn() - owner.verifyNumberOfOwners(2) createwallet.clickOnAddNewOwnerBtn() owner.verifyNumberOfOwners(3) - owner.verifyThresholdLimit(1, 3) + createwallet.clickOnAddNewOwnerBtn() + owner.verifyNumberOfOwners(4) + owner.verifyThresholdLimit(1, 4) createwallet.updateThreshold(3) createwallet.removeOwner(1) + owner.verifyThresholdLimit(1, 3) + createwallet.removeOwner(1) owner.verifyThresholdLimit(1, 2) + createwallet.updateThreshold(1) }) - it('Verify data persistence [C56103]', () => { + it('Verify data persistence', () => { const ownerName = 'David' owner.waitForConnectionStatus() - createwallet.typeWalletName(createwallet.walletName) + createwallet.clickOnCreateNewSafeBtn() owner.clickOnNextBtn() createwallet.clickOnAddNewOwnerBtn() createwallet.typeOwnerName(ownerName, 1) createwallet.typeOwnerAddress(constants.SEPOLIA_OWNER_2, 1) - owner.verifyThresholdLimit(1, 2) + owner.clickOnBackBtn() + createwallet.clearWalletName() + createwallet.typeWalletName(createwallet.walletName) + owner.clickOnNextBtn() owner.clickOnNextBtn() createwallet.verifySafeNameInSummaryStep(createwallet.walletName) createwallet.verifyOwnerNameInSummaryStep(ownerName) @@ -123,14 +134,16 @@ describe('Safe creation tests', () => { createwallet.verifyEstimatedFeeInSummaryStep() }) - it('Verify tip is displayed on right side for threshold 1/1 [C56097]', () => { + 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 [C56095]', () => { + it('Verify address input validation rules', () => { owner.waitForConnectionStatus() + createwallet.clickOnCreateNewSafeBtn() owner.clickOnNextBtn() createwallet.clickOnAddNewOwnerBtn() createwallet.typeOwnerAddress(main.generateRandomString(10), 1) diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index b1a5e1ac1a..74718d6980 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -9,10 +9,10 @@ describe('Create transactions tests', () => { before(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - main.acceptCookies(2) + main.acceptCookies() }) - it('Verify a new send token transaction can be created [C56104]', () => { + it('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 [C56105]', () => { + it('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 [C56106]', () => { + it('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 ad0ca6d0ee..d3712a5b07 100644 --- a/cypress/e2e/smoke/dashboard.cy.js +++ b/cypress/e2e/smoke/dashboard.cy.js @@ -6,24 +6,24 @@ describe('Dashboard tests', () => { beforeEach(() => { cy.clearLocalStorage() cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - main.acceptCookies(2) + main.acceptCookies() main.clickOnSideMenuItem(constants.mainSideMenuOptions.home) dashboard.verifyConnectTransactStrIsVisible() }) - it('Verify the overview widget is displayed [C56107]', () => { + it('Verify the overview widget is displayed', () => { dashboard.verifyOverviewWidgetData() }) - it('Verify the transaction queue widget is displayed [C56108]', () => { + it('Verify the transaction queue widget is displayed', () => { dashboard.verifyTxQueueWidget() }) - it('Verify the featured Safe Apps are displayed [C56109]', () => { + it('Verify the featured Safe Apps are displayed', () => { dashboard.verifyFeaturedAppsSection() }) - it('Verify the Safe Apps Section is displayed [C56110]', () => { + it('Verify the Safe Apps Section is displayed', () => { dashboard.verifySafeAppsSection() }) }) diff --git a/cypress/e2e/smoke/import_export_data.cy.js b/cypress/e2e/smoke/import_export_data.cy.js index de7e8f3881..2d99d5d9b7 100644 --- a/cypress/e2e/smoke/import_export_data.cy.js +++ b/cypress/e2e/smoke/import_export_data.cy.js @@ -6,37 +6,37 @@ import * as constants from '../../support/constants' describe('Import Export Data tests', () => { before(() => { cy.clearLocalStorage() - cy.visit(constants.welcomeUrl) + cy.visit(constants.dataSettingsUrl) main.acceptCookies() - file.verifyImportBtnIsVisible() }) - it('Verify Safe can be accessed after test file upload [C56111]', () => { + it('Verify Safe can be accessed after test file upload', () => { const filePath = '../fixtures/data_import.json' const safe = constants.SEPOLIA_CSV_ENTRY.name - file.clickOnImportBtn() file.uploadFile(filePath) file.verifyImportModalData() file.clickOnImportBtnDataImportModal() + cy.visit(constants.welcomeUrl) + file.clickOnOpenSafeListSidebar() file.clickOnImportedSafe(safe) file.clickOnClosePushNotificationsBanner() }) - it('Verify address book imported data [C56112]', () => { - main.acceptCookies(1) + it('Verify address book imported data', () => { + main.acceptCookies() file.clickOnAddressBookBtn() file.verifyImportedAddressBookData() }) - it('Verify pinned apps [C56113]', () => { + it('Verify pinned apps', () => { const appNames = ['Transaction Builder'] file.clickOnAppsBtn() file.verifyAppsAreVisible(appNames) }) - it('Verify imported data in settings [C56114]', () => { + it('Verify imported data in settings', () => { const unchecked = [file.prependChainPrefixStr, file.copyAddressStr] const checked = [file.darkModeStr] file.clickOnSettingsBtn() @@ -45,7 +45,7 @@ describe('Import Export Data tests', () => { file.verifyCheckboxes(checked, true) }) - it('Verifies data for export in Data tab [C56115]', () => { + it('Verifies data for export in Data tab', () => { file.clickOnShowMoreTabsBtn() file.verifDataTabBtnIsVisible() file.clickOnDataTab() diff --git a/cypress/e2e/smoke/landing.cy.js b/cypress/e2e/smoke/landing.cy.js index 5482541966..206732446f 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 [C56116]', () => { + it('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 8aaa66bede..edce4b7f8b 100644 --- a/cypress/e2e/smoke/load_safe.cy.js +++ b/cypress/e2e/smoke/load_safe.cy.js @@ -26,7 +26,7 @@ describe('Load Safe tests', () => { cy.wait(2000) }) - it('Verify a network can be selected in the Safe [C56117]', () => { + it('Verify a network can be selected in the Safe', () => { safe.clickNetworkSelector(constants.networks.sepolia) safe.selectPolygon() cy.wait(2000) @@ -34,7 +34,7 @@ describe('Load Safe tests', () => { safe.selectSepolia() }) - it('Verify only valid Safe name can be accepted [C56118]', () => { + it('Verify only valid Safe name can be accepted', () => { // alias the address input label cy.get('input[name="address"]').parent().prev('label').as('addressLabel') @@ -62,14 +62,14 @@ describe('Load Safe tests', () => { safe.clickOnNextBtn() }) - it('Verify custom name in the first owner an be set [C56120]', () => { + 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 [C56121]', () => { + 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) @@ -78,7 +78,7 @@ describe('Load Safe tests', () => { safe.clickOnAddBtn() }) - it('Verify the custom Safe name is successfully loaded [C56122]', () => { + it('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 e86f1d671a..01680d3f72 100644 --- a/cypress/e2e/smoke/nfts.cy.js +++ b/cypress/e2e/smoke/nfts.cy.js @@ -9,32 +9,32 @@ const nftsTokenID = 'CF' describe('NFTs tests', () => { beforeEach(() => { cy.clearLocalStorage() - cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - main.acceptCookies(2) + cy.visit(constants.balanceNftsUrl + constants.SEPOLIA_TEST_SAFE_5) + main.acceptCookies() nfts.clickOnNftsTab() }) - it('Verify that NFTs exist in the table [C56123]', () => { + it('Verify that NFTs exist in the table', () => { nfts.verifyNFTNumber(20) }) - it('Verify NFT row contains data [C56124]', () => { + it('Verify NFT row contains data', () => { nfts.verifyDataInTable(nftsName, nftsAddress, nftsTokenID) }) - it('Verify NFT preview window can be opened [C56125]', () => { - nfts.openNFT(1) + it('Verify NFT preview window can be opened', () => { + nfts.openNFT(0) nfts.verifyNameInNFTModal(nftsTokenID) nfts.verifySelectedNetwrokSepolia() nfts.closeNFTModal() }) - it('Verify NFT open does not open if no NFT exits [C56126]', () => { + it('Verify NFT open does not open if no NFT exits', () => { nfts.clickOn6thNFT() nfts.verifyNFTModalDoesNotExist() }) - it('Verify multipls NFTs can be selected and reviewed [C56127]', () => { + it('Verify multipls NFTs can be selected and reviewed', () => { nfts.verifyInitialNFTData() nfts.selectNFTs(3) nfts.deselectNFTs([2], 3) diff --git a/cypress/e2e/smoke/pending_actions.cy.js b/cypress/e2e/smoke/pending_actions.cy.js index 6f8a9edc81..c7f2399954 100644 --- a/cypress/e2e/smoke/pending_actions.cy.js +++ b/cypress/e2e/smoke/pending_actions.cy.js @@ -1,7 +1,7 @@ import * as constants from '../../support/constants' import * as safe from '../pages/load_safe.pages' -describe('Pending actions', () => { +describe('Pending actions tests', () => { before(() => { cy.visit(constants.welcomeUrl) // main.acceptCookies() diff --git a/cypress/e2e/smoke/remove_owner.cy.js b/cypress/e2e/smoke/remove_owner.cy.js index b22848c26e..4643b91e1a 100644 --- a/cypress/e2e/smoke/remove_owner.cy.js +++ b/cypress/e2e/smoke/remove_owner.cy.js @@ -10,18 +10,18 @@ describe('Remove Owners tests', () => { cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) }) - it('Verify that "Remove" icon is visible [C56030]', () => { + 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 [C56037]', () => { + 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 [C56031]', () => { + it('Verify Tooltip displays correct message for disconnected user', () => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) owner.waitForConnectionStatus() owner.clickOnWalletExpandMoreIcon() @@ -29,13 +29,13 @@ describe('Remove Owners tests', () => { owner.verifyRemoveBtnIsDisabled() }) - it('Verify owner removal form can be opened [C56032]', () => { + 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 [C56033]', () => { + 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) @@ -43,7 +43,7 @@ describe('Remove Owners tests', () => { owner.getThresholdOptions().should('have.length', 1) }) - it('Verify owner deletion confirmation is displayed [C56034]', () => { + it('Verify owner deletion confirmation is displayed', () => { cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) owner.waitForConnectionStatus() owner.openRemoveOwnerWindow(1) diff --git a/cypress/e2e/smoke/replace_owner.cy.js b/cypress/e2e/smoke/replace_owner.cy.js index b43ec2a8ce..ed48056206 100644 --- a/cypress/e2e/smoke/replace_owner.cy.js +++ b/cypress/e2e/smoke/replace_owner.cy.js @@ -11,36 +11,36 @@ describe('Replace Owners tests', () => { cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) }) - it('Verify that "Replace" icon is visible [C55998]', () => { + it('Verify that "Replace" icon is visible', () => { owner.verifyReplaceBtnIsEnabled() }) - it('Verify Tooltip displays correct message for Non-Owner [C56016]', () => { + it('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 [C56006]', () => { + 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 [C56007]', () => { + it('Verify that the owner replacement form is opened', () => { owner.waitForConnectionStatus() owner.openReplaceOwnerWindow() }) - it('Verify max characters in name field [C56008]', () => { + 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 [C56009]', () => { + 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) @@ -55,7 +55,7 @@ describe('Replace Owners tests', () => { owner.verifyNewOwnerName(constants.addresBookContacts.user1.name) }) - it('Verify that Name field not mandatory. Verify confirmation for owner replacement is displayed [C56011]', () => { + it('Verify that Name field not mandatory. Verify confirmation for owner replacement is displayed', () => { owner.waitForConnectionStatus() owner.openReplaceOwnerWindow() owner.typeOwnerAddress(constants.SEPOLIA_OWNER_2) @@ -63,7 +63,7 @@ describe('Replace Owners tests', () => { owner.verifyConfirmTransactionWindowDisplayed() }) - it('Verify relevant error messages are displayed in Address input [C56012]', () => { + it('Verify relevant error messages are displayed in Address input', () => { owner.waitForConnectionStatus() owner.openReplaceOwnerWindow() owner.typeOwnerAddress(main.generateRandomString(10)) diff --git a/cypress/e2e/smoke/tx_history.cy.js b/cypress/e2e/smoke/tx_history.cy.js index 42fdbeeb44..a9b8309c3f 100644 --- a/cypress/e2e/smoke/tx_history.cy.js +++ b/cypress/e2e/smoke/tx_history.cy.js @@ -17,10 +17,10 @@ describe('Transaction history tests', () => { // 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(1) + main.acceptCookies() }) - it('Verify October 29th transactions are displayed [C56128]', () => { + it('Verify October 29th transactions are displayed', () => { const DATE = 'Oct 29, 2023' const NEXT_DATE_LABEL = 'Oct 20, 2023' const amount = '0.00001 ETH' @@ -45,7 +45,7 @@ describe('Transaction history tests', () => { }) }) - it('Verify transaction can be expanded/collapsed [C56129]', () => { + it('Verify transaction can be expanded/collapsed', () => { createTx.clickOnTransactionExpandableItem('Oct 20, 2023', () => { createTx.verifyTransactionStrExists(str1) createTx.verifyTransactionStrExists(str2) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index 6daeacfc92..e9dce33964 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -54,6 +54,7 @@ export const requestPermissionsUrl = '/request-permissions' export const getPermissionsUrl = '/get-permissions' export const appSettingsUrl = '/settings/safe-apps' export const setupUrl = '/settings/setup?safe=' +export const dataSettingsUrl = '/settings/data' export const invalidAppUrl = 'https://my-invalid-custom-app.com/manifest.json' export const validAppUrlJson = 'https://my-valid-custom-app.com/manifest.json' export const validAppUrl = 'https://my-valid-custom-app.com' diff --git a/jest.config.cjs b/jest.config.cjs index 2ba19a6f88..1b06adda63 100644 --- a/jest.config.cjs +++ b/jest.config.cjs @@ -8,13 +8,13 @@ const createJestConfig = nextJest({ // Add any custom config to be passed to Jest const customJestConfig = { setupFilesAfterEnv: ['/jest.setup.js'], + moduleNameMapper: { // Handle module aliases (this will be automatically configured for you soon) '^@/(.*)$': '/src/$1', '^.+\\.(svg)$': '/mocks/svg.js', isows: '/node_modules/isows/_cjs/index.js', }, - transformIgnorePatterns: ['node_modules/(?!isows/)'], testEnvironment: 'jest-environment-jsdom', testEnvironmentOptions: { url: 'http://localhost/balances?safe=rin:0xb3b83bf204C458B461de9B0CD2739DB152b4fa5A' }, globals: { @@ -25,5 +25,5 @@ const customJestConfig = { // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async module.exports = async () => ({ ...(await createJestConfig(customJestConfig)()), - transformIgnorePatterns: ['node_modules/(?!(uint8arrays|multiformats)/)'], + transformIgnorePatterns: ['node_modules/(?!(uint8arrays|multiformats|@web3-onboard/common)/)'], }) diff --git a/next.config.mjs b/next.config.mjs index 1470c8d524..07d1f2a35d 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -32,7 +32,7 @@ const nextConfig = { dirs: ['src'], }, experimental: { - optimizePackageImports: ['@mui/material', '@mui/icons-material', 'lodash', 'date-fns'] + optimizePackageImports: ['@mui/material', '@mui/icons-material', 'lodash', 'date-fns'], }, webpack(config) { config.module.rules.push({ diff --git a/package.json b/package.json index d58949d6b9..1150b1d199 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "homepage": "https://github.com/safe-global/safe-wallet-web", "license": "GPL-3.0", "type": "module", - "version": "1.21.0", + "version": "1.22.2", "scripts": { "dev": "next dev", "start": "next dev", @@ -57,6 +57,7 @@ "@safe-global/safe-react-components": "^2.0.6", "@sentry/react": "^7.74.0", "@sentry/tracing": "^7.74.0", + "@tkey-mpc/common-types": "^8.2.2", "@truffle/hdwallet-provider": "^2.1.4", "@walletconnect/utils": "^2.10.2", "@walletconnect/web3wallet": "^1.9.2", @@ -67,7 +68,9 @@ "@web3-onboard/ledger": "2.3.2", "@web3-onboard/trezor": "^2.4.2", "@web3-onboard/walletconnect": "^2.4.7", + "@web3auth/mpc-core-kit": "^1.1.3", "blo": "^1.1.1", + "bn.js": "^5.2.1", "classnames": "^2.3.1", "date-fns": "^2.29.2", "ethers": "5.7.2", diff --git a/public/images/common/arrow-se.svg b/public/images/common/arrow-se.svg new file mode 100644 index 0000000000..994e469595 --- /dev/null +++ b/public/images/common/arrow-se.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/common/arrow-top-right.svg b/public/images/common/arrow-top-right.svg new file mode 100644 index 0000000000..14866a77da --- /dev/null +++ b/public/images/common/arrow-top-right.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/common/bar-chart.svg b/public/images/common/bar-chart.svg new file mode 100644 index 0000000000..f6beb44899 --- /dev/null +++ b/public/images/common/bar-chart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/images/common/check-filled.svg b/public/images/common/check-filled.svg new file mode 100644 index 0000000000..284624fc19 --- /dev/null +++ b/public/images/common/check-filled.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/common/lock-small.svg b/public/images/common/lock-small.svg new file mode 100644 index 0000000000..2620c043c8 --- /dev/null +++ b/public/images/common/lock-small.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/images/common/lock-warning.svg b/public/images/common/lock-warning.svg new file mode 100644 index 0000000000..6972e1bb15 --- /dev/null +++ b/public/images/common/lock-warning.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/images/common/lock.svg b/public/images/common/lock.svg new file mode 100644 index 0000000000..9535a642e0 --- /dev/null +++ b/public/images/common/lock.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/common/shield-off.svg b/public/images/common/shield-off.svg new file mode 100644 index 0000000000..f36482d5ca --- /dev/null +++ b/public/images/common/shield-off.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/images/common/shield.svg b/public/images/common/shield.svg new file mode 100644 index 0000000000..f7d12a900f --- /dev/null +++ b/public/images/common/shield.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/images/logo-round.svg b/public/images/logo-round.svg index 7782f914a7..dd8af7d0ca 100644 --- a/public/images/logo-round.svg +++ b/public/images/logo-round.svg @@ -1,4 +1,4 @@ - + diff --git a/public/images/logo-text.svg b/public/images/logo-text.svg new file mode 100644 index 0000000000..b71ae734a4 --- /dev/null +++ b/public/images/logo-text.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/images/welcome/logo-google.svg b/public/images/welcome/logo-google.svg new file mode 100644 index 0000000000..65781d4881 --- /dev/null +++ b/public/images/welcome/logo-google.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/balances/AssetsTable/NoAssets.tsx b/src/components/balances/AssetsTable/NoAssets.tsx new file mode 100644 index 0000000000..ebf337c6f6 --- /dev/null +++ b/src/components/balances/AssetsTable/NoAssets.tsx @@ -0,0 +1,76 @@ +import Link from 'next/link' +import { useRouter } from 'next/router' +import { Box, Button, FormControlLabel, Grid, Paper, Switch, Typography } from '@mui/material' +import EthHashInfo from '@/components/common/EthHashInfo' +import QRCode from '@/components/common/QRCode' +import { AppRoutes } from '@/config/routes' +import { useRemoteSafeApps } from '@/hooks/safe-apps/useRemoteSafeApps' +import { useCurrentChain } from '@/hooks/useChains' +import useSafeAddress from '@/hooks/useSafeAddress' +import { useAppDispatch, useAppSelector } from '@/store' +import { selectSettings, setQrShortName } from '@/store/settingsSlice' +import AddIcon from '@mui/icons-material/Add' + +const NoAssets = () => { + const router = useRouter() + const safeAddress = useSafeAddress() + const chain = useCurrentChain() + const dispatch = useAppDispatch() + const settings = useAppSelector(selectSettings) + const qrPrefix = settings.shortName.qr ? `${chain?.shortName}:` : '' + const qrCode = `${qrPrefix}${safeAddress}` + const [apps] = useRemoteSafeApps() + + // @FIXME: use tags instead of name + const rampSafeApp = apps?.find((app) => app.name === 'Ramp Network') + + return ( + + + +
+ + + +
+ + dispatch(setQrShortName(e.target.checked))} /> + } + label={<>QR code with chain prefix} + /> + + + + + Add funds to get started + + + + Add funds directly from your bank account or copy your address to send tokens from a different account. + + + + + + + {rampSafeApp && ( + + + + + + )} + + + + ) +} + +export default NoAssets diff --git a/src/components/balances/AssetsTable/index.tsx b/src/components/balances/AssetsTable/index.tsx index b673142d13..57c0a28c16 100644 --- a/src/components/balances/AssetsTable/index.tsx +++ b/src/components/balances/AssetsTable/index.tsx @@ -20,6 +20,7 @@ import CheckWallet from '@/components/common/CheckWallet' import useSpendingLimit from '@/hooks/useSpendingLimit' import { TxModalContext } from '@/components/tx-flow' import TokenTransferFlow from '@/components/tx-flow/flows/TokenTransfer' +import NoAssets from './NoAssets' const skeletonCells: EnhancedTableProps['rows'][0]['cells'] = { asset: { @@ -137,6 +138,8 @@ const AssetsTable = ({ [hiddenAssets, balances.items, showHiddenAssets], ) + const hasNoAssets = balances.items.length === 1 && balances.items[0].balance === '0' + const selectedAssetCount = visibleAssets?.filter((item) => isAssetSelected(item.tokenInfo.address)).length || 0 const onSendClick = (tokenAddress: string) => { @@ -247,9 +250,13 @@ const AssetsTable = ({ showHiddenAssets={showHiddenAssets} /> -
- -
+ {hasNoAssets ? ( + + ) : ( +
+ +
+ )} ) } diff --git a/src/components/common/ConnectWallet/AccountCenter.tsx b/src/components/common/ConnectWallet/AccountCenter.tsx index 161e9e1bb2..c28d607e09 100644 --- a/src/components/common/ConnectWallet/AccountCenter.tsx +++ b/src/components/common/ConnectWallet/AccountCenter.tsx @@ -1,48 +1,21 @@ import type { MouseEvent } from 'react' import { useState } from 'react' -import { Box, Button, ButtonBase, Paper, Popover, Typography } from '@mui/material' +import { Box, ButtonBase, Paper, Popover } from '@mui/material' import css from '@/components/common/ConnectWallet/styles.module.css' -import EthHashInfo from '@/components/common/EthHashInfo' import ExpandLessIcon from '@mui/icons-material/ExpandLess' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' -import useOnboard, { switchWallet } from '@/hooks/wallets/useOnboard' -import { useAppSelector } from '@/store' -import { selectChainById } from '@/store/chainsSlice' -import Identicon from '@/components/common/Identicon' -import ChainSwitcher from '../ChainSwitcher' -import useAddressBook from '@/hooks/useAddressBook' import { type ConnectedWallet } from '@/hooks/wallets/useOnboard' -import WalletInfo, { UNKNOWN_CHAIN_NAME } from '../WalletInfo' +import WalletOverview from '../WalletOverview' +import WalletInfo from '@/components/common/WalletInfo' -const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => { +export const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => { const [anchorEl, setAnchorEl] = useState(null) - const onboard = useOnboard() - const chainInfo = useAppSelector((state) => selectChainById(state, wallet.chainId)) - const addressBook = useAddressBook() - const prefix = chainInfo?.shortName - const handleSwitchWallet = () => { - if (onboard) { - handleClose() - switchWallet(onboard) - } - } - - const handleDisconnect = () => { - if (!wallet) return - - onboard?.disconnectWallet({ - label: wallet.label, - }) - - handleClose() - } - - const handleClick = (event: MouseEvent) => { + const openWalletInfo = (event: MouseEvent) => { setAnchorEl(event.currentTarget) } - const handleClose = () => { + const closeWalletInfo = () => { setAnchorEl(null) } @@ -51,9 +24,15 @@ const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => { return ( <> - + - + {open ? : } @@ -65,7 +44,7 @@ const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => { id={id} open={open} anchorEl={anchorEl} - onClose={handleClose} + onClose={closeWalletInfo} anchorOrigin={{ vertical: 'bottom', horizontal: 'center', @@ -81,43 +60,7 @@ const AccountCenter = ({ wallet }: { wallet: ConnectedWallet }) => { }} > - - - - {addressBook[wallet.address] || wallet.ens} - - - - - - - - - Wallet - {wallet.label} - - - Connected network - {chainInfo?.chainName || UNKNOWN_CHAIN_NAME} - - - - - - - - + diff --git a/src/components/common/ConnectWallet/ConnectionCenter.tsx b/src/components/common/ConnectWallet/ConnectionCenter.tsx new file mode 100644 index 0000000000..1e04b377d2 --- /dev/null +++ b/src/components/common/ConnectWallet/ConnectionCenter.tsx @@ -0,0 +1,79 @@ +import ConnectWalletButton from '@/components/common/ConnectWallet/ConnectWalletButton' +import { useHasFeature } from '@/hooks/useChains' +import { FEATURES } from '@/utils/chains' +import madProps from '@/utils/mad-props' +import { Popover, ButtonBase, Typography, Paper, Box } from '@mui/material' +import ExpandMoreIcon from '@mui/icons-material/ExpandMore' +import ExpandLessIcon from '@mui/icons-material/ExpandLess' +import classnames from 'classnames' +import { useState, type MouseEvent, type ReactElement } from 'react' + +import KeyholeIcon from '@/components/common/icons/KeyholeIcon' +import WalletDetails from '@/components/common/ConnectWallet/WalletDetails' + +import css from '@/components/common/ConnectWallet/styles.module.css' + +export const ConnectionCenter = ({ isSocialLoginEnabled }: { isSocialLoginEnabled: boolean }): ReactElement => { + const [anchorEl, setAnchorEl] = useState(null) + const open = !!anchorEl + + const handleClick = (event: MouseEvent) => { + setAnchorEl(event.currentTarget) + } + + const handleClose = () => { + setAnchorEl(null) + } + + const ExpandIcon = open ? ExpandLessIcon : ExpandMoreIcon + + if (!isSocialLoginEnabled) { + return ( + + + + ) + } + + return ( + <> + + + + + Not connected + palette.error.main }}> + Connect wallet + + + + + + + + + + + + + ) +} + +const useIsSocialLoginEnabled = () => useHasFeature(FEATURES.SOCIAL_LOGIN) + +export default madProps(ConnectionCenter, { + isSocialLoginEnabled: useIsSocialLoginEnabled, +}) diff --git a/src/components/common/ConnectWallet/WalletDetails.tsx b/src/components/common/ConnectWallet/WalletDetails.tsx new file mode 100644 index 0000000000..5f0442f50a --- /dev/null +++ b/src/components/common/ConnectWallet/WalletDetails.tsx @@ -0,0 +1,30 @@ +import { Box, Divider, SvgIcon, Typography } from '@mui/material' +import type { ReactElement } from 'react' + +import LockIcon from '@/public/images/common/lock.svg' +import SocialSigner from '@/components/common/SocialSigner' +import WalletLogin from '@/components/welcome/WelcomeLogin/WalletLogin' + +const WalletDetails = ({ onConnect }: { onConnect: () => void }): ReactElement => { + return ( + <> + + + + + + + + + + + or + + + + + + ) +} + +export default WalletDetails diff --git a/src/components/common/ConnectWallet/__tests__/AccountCenter.test.tsx b/src/components/common/ConnectWallet/__tests__/AccountCenter.test.tsx new file mode 100644 index 0000000000..382b7acb1d --- /dev/null +++ b/src/components/common/ConnectWallet/__tests__/AccountCenter.test.tsx @@ -0,0 +1,35 @@ +import { render } from '@/tests/test-utils' +import { AccountCenter } from '@/components/common/ConnectWallet/AccountCenter' +import { type EIP1193Provider } from '@web3-onboard/core' +import { act, waitFor } from '@testing-library/react' + +const mockWallet = { + address: '0x1234567890123456789012345678901234567890', + chainId: '5', + label: '', + provider: null as unknown as EIP1193Provider, +} + +describe('AccountCenter', () => { + it('should open and close the account center on click', async () => { + const { getByText, getByTestId } = render() + + const openButton = getByTestId('open-account-center') + + act(() => { + openButton.click() + }) + + const disconnectButton = getByText('Disconnect') + + expect(disconnectButton).toBeInTheDocument() + + act(() => { + disconnectButton.click() + }) + + await waitFor(() => { + expect(disconnectButton).not.toBeInTheDocument() + }) + }) +}) diff --git a/src/components/common/ConnectWallet/__tests__/ConnectionCenter.test.tsx b/src/components/common/ConnectWallet/__tests__/ConnectionCenter.test.tsx new file mode 100644 index 0000000000..5ca16f4512 --- /dev/null +++ b/src/components/common/ConnectWallet/__tests__/ConnectionCenter.test.tsx @@ -0,0 +1,18 @@ +import { ConnectionCenter } from '@/components/common/ConnectWallet/ConnectionCenter' +import { render } from '@/tests/test-utils' + +describe('ConnectionCenter', () => { + it('displays a Connect wallet button if the social login feature is enabled', () => { + const { getByText, queryByText } = render() + + expect(getByText('Connect wallet')).toBeInTheDocument() + expect(queryByText('Connect')).not.toBeInTheDocument() + }) + + it('displays the ConnectWalletButton if the social login feature is disabled', () => { + const { getByText, queryByText } = render() + + expect(queryByText('Connect wallet')).not.toBeInTheDocument() + expect(getByText('Connect')).toBeInTheDocument() + }) +}) diff --git a/src/components/common/ConnectWallet/index.tsx b/src/components/common/ConnectWallet/index.tsx index aaf6812b1a..45a9f33a9f 100644 --- a/src/components/common/ConnectWallet/index.tsx +++ b/src/components/common/ConnectWallet/index.tsx @@ -1,13 +1,12 @@ import type { ReactElement } from 'react' import useWallet from '@/hooks/wallets/useWallet' import AccountCenter from '@/components/common/ConnectWallet/AccountCenter' -import ConnectWalletButton from './ConnectWalletButton' -import css from './styles.module.css' +import ConnectionCenter from './ConnectionCenter' const ConnectWallet = (): ReactElement => { const wallet = useWallet() - return
{wallet ? : }
+ return wallet ? : } export default ConnectWallet diff --git a/src/components/common/ConnectWallet/styles.module.css b/src/components/common/ConnectWallet/styles.module.css index 24057834f2..b41bbc8399 100644 --- a/src/components/common/ConnectWallet/styles.module.css +++ b/src/components/common/ConnectWallet/styles.module.css @@ -1,5 +1,6 @@ -.container { - padding: 0 var(--space-2); +.connectedContainer { + display: flex; + align-items: center; } .buttonContainer { @@ -7,18 +8,23 @@ align-items: center; text-align: left; gap: var(--space-1); + padding: 0 var(--space-2); } .popoverContainer { padding: var(--space-2); - width: 250px; + width: 300px; display: flex; flex-direction: column; align-items: center; - gap: var(--space-2); + gap: var(--space-1); border: 1px solid var(--color-border-light); } +.largeGap { + gap: var(--space-2); +} + .addressName { text-align: center; overflow: hidden; @@ -27,6 +33,18 @@ width: 100%; } +.profileImg { + border-radius: var(--space-2); + width: 32px; + height: 32px; +} + +.profileData { + display: flex; + flex-direction: column; + align-items: flex-start; +} + .rowContainer { align-self: stretch; margin-left: calc(var(--space-2) * -1); @@ -45,3 +63,29 @@ .row:last-of-type { border-bottom: 1px solid var(--color-border-light); } + +.pairingDetails { + display: flex; + flex-direction: column; + align-items: center; + gap: var(--space-2); +} + +.loginButton { + min-height: 42px; +} + +.loginError { + width: 100%; + margin: 0; +} + +@media (max-width: 599.95px) { + .socialLoginInfo > div > div { + display: none; + } + + .notConnected { + display: none; + } +} diff --git a/src/components/common/ConnectWallet/useConnectWallet.ts b/src/components/common/ConnectWallet/useConnectWallet.ts index 9dc10e7930..b3f832229e 100644 --- a/src/components/common/ConnectWallet/useConnectWallet.ts +++ b/src/components/common/ConnectWallet/useConnectWallet.ts @@ -1,19 +1,17 @@ -import { useMemo } from 'react' +import { useCallback } from 'react' import useOnboard, { connectWallet } from '@/hooks/wallets/useOnboard' import { OVERVIEW_EVENTS, trackEvent } from '@/services/analytics' -const useConnectWallet = (): (() => void) => { +const useConnectWallet = () => { const onboard = useOnboard() - return useMemo(() => { + return useCallback(() => { if (!onboard) { - return () => {} + return Promise.resolve(undefined) } - return () => { - trackEvent(OVERVIEW_EVENTS.OPEN_ONBOARD) - connectWallet(onboard) - } + trackEvent(OVERVIEW_EVENTS.OPEN_ONBOARD) + return connectWallet(onboard) }, [onboard]) } diff --git a/src/components/common/ErrorBoundary/index.tsx b/src/components/common/ErrorBoundary/index.tsx index e277db308a..f25c1bbcfa 100644 --- a/src/components/common/ErrorBoundary/index.tsx +++ b/src/components/common/ErrorBoundary/index.tsx @@ -33,7 +33,7 @@ const ErrorBoundary: FallbackRender = ({ error, componentStack }) => { {componentStack} )} - + Go home diff --git a/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx b/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx index 1fbbd76d89..34041dbdfb 100644 --- a/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx +++ b/src/components/common/EthHashInfo/SrcEthHashInfo/index.tsx @@ -71,7 +71,7 @@ const SrcEthHashInfo = ({ {name && ( - + {name} )} diff --git a/src/components/common/Footer/index.tsx b/src/components/common/Footer/index.tsx index afd19922e0..545b1f43aa 100644 --- a/src/components/common/Footer/index.tsx +++ b/src/components/common/Footer/index.tsx @@ -11,7 +11,7 @@ import MUILink from '@mui/material/Link' import { IS_DEV, IS_OFFICIAL_HOST } from '@/config/constants' const footerPages = [ - AppRoutes.welcome, + AppRoutes.welcome.index, AppRoutes.settings.index, AppRoutes.imprint, AppRoutes.privacy, diff --git a/src/components/common/Header/index.tsx b/src/components/common/Header/index.tsx index c18ebcd039..6919ca1526 100644 --- a/src/components/common/Header/index.tsx +++ b/src/components/common/Header/index.tsx @@ -34,7 +34,7 @@ const Header = ({ onMenuToggle, onBatchToggle }: HeaderProps): ReactElement => { const enableWc = !!chain && hasFeature(chain, FEATURES.NATIVE_WALLETCONNECT) // Logo link: if on Dashboard, link to Welcome, otherwise to the root (which redirects to either Dashboard or Welcome) - const logoHref = router.pathname === AppRoutes.home ? AppRoutes.welcome : AppRoutes.index + const logoHref = router.pathname === AppRoutes.home ? AppRoutes.welcome.index : AppRoutes.index const handleMenuToggle = () => { if (onMenuToggle) { diff --git a/src/components/common/NetworkSelector/index.tsx b/src/components/common/NetworkSelector/index.tsx index efe261b122..5e3ac1e676 100644 --- a/src/components/common/NetworkSelector/index.tsx +++ b/src/components/common/NetworkSelector/index.tsx @@ -1,20 +1,34 @@ import Link from 'next/link' import type { SelectChangeEvent } from '@mui/material' -import { MenuItem, Select, Skeleton } from '@mui/material' +import { MenuItem, Select, Skeleton, Tooltip } from '@mui/material' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import useChains from '@/hooks/useChains' import { useRouter } from 'next/router' import ChainIndicator from '../ChainIndicator' import css from './styles.module.css' import { useChainId } from '@/hooks/useChainId' -import type { ReactElement } from 'react' +import { type ReactElement, forwardRef } from 'react' import { useCallback } from 'react' import { AppRoutes } from '@/config/routes' import { trackEvent, OVERVIEW_EVENTS } from '@/services/analytics' +import useWallet from '@/hooks/wallets/useWallet' +import { isSocialWalletEnabled } from '@/hooks/wallets/wallets' +import { isSocialLoginWallet } from '@/services/mpc/SocialLoginModule' -const keepPathRoutes = [AppRoutes.welcome, AppRoutes.newSafe.create, AppRoutes.newSafe.load] +const keepPathRoutes = [AppRoutes.welcome.index, AppRoutes.newSafe.create, AppRoutes.newSafe.load] + +const MenuWithTooltip = forwardRef(function MenuWithTooltip(props: any, ref) { + return ( + +
    + {props.children} +
+
+ ) +}) const NetworkSelector = (props: { onChainSelect?: () => void }): ReactElement => { + const wallet = useWallet() const { configs } = useChains() const chainId = useChainId() const router = useRouter() @@ -53,6 +67,8 @@ const NetworkSelector = (props: { onChainSelect?: () => void }): ReactElement => } } + const isSocialLogin = isSocialLoginWallet(wallet?.label) + return configs.length ? (