diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index d978694270..6af7316dbc 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -9,7 +9,7 @@ concurrency: jobs: e2e: - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 name: Smoke E2E tests steps: - uses: actions/checkout@v3 diff --git a/cypress.config.js b/cypress.config.js index 16a5083407..8c736ea572 100644 --- a/cypress.config.js +++ b/cypress.config.js @@ -11,6 +11,7 @@ export default defineConfig({ e2e: { baseUrl: 'http://localhost:3000', + testIsolation: false, }, chromeWebSecurity: false, diff --git a/cypress/e2e/pages/address_book.page.js b/cypress/e2e/pages/address_book.page.js index 0114cdd13c..c02b046a9f 100644 --- a/cypress/e2e/pages/address_book.page.js +++ b/cypress/e2e/pages/address_book.page.js @@ -2,6 +2,8 @@ export const acceptSelection = 'Accept selection' export const addressBook = 'Address book' const createEntryBtn = 'Create entry' +const beameriFrameContainer = '#beamerOverlay .iframeCointaner' +const beamerInput = 'input[id="beamer"]' const nameInput = 'input[name="name"]' const addressInput = 'input[name="address"]' const saveBtn = 'Save' @@ -13,6 +15,8 @@ const exportFileModalBtnSection = '.MuiDialogActions-root' const exportFileModalExportBtn = 'Export' const importBtn = 'Import' const exportBtn = 'Export' +const whatsNewBtnStr = "What's new" +const beamrCookiesStr = 'accept the "Beamer" cookies' export function clickOnImportFileBtn() { cy.contains(importBtn).click() @@ -91,3 +95,20 @@ export function clickDeleteEntryModalDeleteButton() { export function verifyEditedNameNotExists(name) { cy.get(name).should('not.exist') } + +export function clickOnWhatsNewBtn(force = false) { + cy.contains(whatsNewBtnStr).click({ force: force }) +} + +export function acceptBeamerCookies() { + cy.contains(beamrCookiesStr) +} + +export function verifyBeamerIsChecked() { + cy.get(beamerInput).should('be.checked') +} + +export function verifyBeameriFrameExists() { + cy.wait(1000) + cy.get(beameriFrameContainer).should('exist') +} diff --git a/cypress/e2e/pages/balances.pages.js b/cypress/e2e/pages/balances.pages.js index f648706209..4c640272b2 100644 --- a/cypress/e2e/pages/balances.pages.js +++ b/cypress/e2e/pages/balances.pages.js @@ -7,6 +7,7 @@ const hideAssetBtn = 'button[aria-label="Hide asset"]' const hiddeTokensBtn = '[data-testid="toggle-hidden-assets"]' const hiddenTokenCheckbox = 'input[type="checkbox"]' const paginationPageList = 'ul[role="listbox"]' +const currencyDropDown = 'div[id="currency"]' const hiddenTokenSaveBtn = 'Save' const hideTokenDefaultString = 'Hide tokens' diff --git a/cypress/e2e/pages/batches.pages.js b/cypress/e2e/pages/batches.pages.js index 77829aeaf5..1b5631c6fb 100644 --- a/cypress/e2e/pages/batches.pages.js +++ b/cypress/e2e/pages/batches.pages.js @@ -23,6 +23,7 @@ const tokenAddressInput = 'input[name="tokenAddress"]' const listBox = 'ul[role="listbox"]' const amountInput = '[name="amount"]' const nonceInput = 'input[name="nonce"]' +const executeOptionsContainer = 'div[role="radiogroup"]' export function addToBatch(EOA, currentNonce, amount, verify = false) { fillTransactionData(EOA, amount) @@ -49,8 +50,12 @@ function setNonceAndProceed(currentNonce) { } function executeTransaction() { - cy.contains(yesExecuteString, { timeout: 4000 }).click() - cy.contains(addToBatchBtn).should('not.exist') + cy.waitForSelector(() => { + return cy.get(executeOptionsContainer).then(() => { + cy.contains(yesExecuteString, { timeout: 4000 }).click() + cy.contains(addToBatchBtn).should('not.exist') + }) + }) } function addToBatchButton() { diff --git a/cypress/e2e/pages/create_tx.pages.js b/cypress/e2e/pages/create_tx.pages.js index b657939bac..11b7bb78e7 100644 --- a/cypress/e2e/pages/create_tx.pages.js +++ b/cypress/e2e/pages/create_tx.pages.js @@ -8,6 +8,7 @@ const amountInput = 'input[name="amount"]' const nonceInput = 'input[name="nonce"]' const gasLimitInput = '[name="gasLimit"]' const rotateLeftIcon = '[data-testid="RotateLeftIcon"]' +const transactionItemExpandable = 'div[id^="transfer"]' const viewTransactionBtn = 'View transaction' const transactionDetailsTitle = 'Transaction details' @@ -26,6 +27,8 @@ const editBtnStr = 'Edit' const executionParamsStr = 'Execution parameters' const noLaterStr = 'No, later' const signBtnStr = 'Sign' +const expandAllBtnStr = 'Expand all' +const collapseAllBtnStr = 'Collapse all' export function clickOnNewtransactionBtn() { // Assert that "New transaction" button is visible @@ -90,7 +93,7 @@ export function changeNonce(value) { } export function verifyConfirmTransactionData() { - cy.contains(yesStr).should('exist') + cy.contains(yesStr).should('exist').click() cy.contains(estimatedFeeStr).should('exist') // Asserting the sponsored info is present @@ -130,7 +133,7 @@ export function clickOnSignTransactionBtn() { } export function waitForProposeRequest() { - cy.intercept('POST', constants.proposeEndPoint).as('ProposeTx') + cy.intercept('POST', constants.proposeEndpoint).as('ProposeTx') cy.wait('@ProposeTx') } @@ -149,3 +152,40 @@ export function verifyQueueLabel() { export function verifyTransactionSummary(sendValue) { cy.contains(TransactionSummary + `${sendValue} ${constants.tokenAbbreviation.gor}`).should('exist') } + +export function verifyDateExists(date) { + cy.contains('div', date).should('exist') +} + +export function verifyImageAltTxt(index, text) { + cy.get('img').eq(index).should('have.attr', 'alt', text).should('be.visible') +} + +export function verifyStatus(status) { + cy.contains('div', status).should('exist') +} + +export function verifyTransactionStrExists(str) { + cy.contains(str).should('exist') +} + +export function verifyTransactionStrNotVible(str) { + cy.contains(str).should('not.be.visible') +} + +export function clickOnTransactionExpandableItem(name, actions) { + cy.contains('div', name) + .next() + .click() + .within(() => { + actions() + }) +} + +export function clickOnExpandAllBtn() { + cy.contains(expandAllBtnStr).click() +} + +export function clickOnCollapseAllBtn() { + cy.contains(collapseAllBtnStr).click() +} diff --git a/cypress/e2e/pages/load_safe.pages.js b/cypress/e2e/pages/load_safe.pages.js index 2f5b22e618..3d36669e48 100644 --- a/cypress/e2e/pages/load_safe.pages.js +++ b/cypress/e2e/pages/load_safe.pages.js @@ -29,6 +29,11 @@ export function selectGoerli() { cy.contains('span', constants.networks.goerli) } +export function selectPolygon() { + cy.get('ul li').contains(constants.networks.polygon).click() + cy.contains('span', constants.networks.polygon) +} + export function verifyNameInputHasPlceholder() { cy.get(nameInput).should('have.attr', 'placeholder').should('match', constants.goerlySafeName) } diff --git a/cypress/e2e/pages/main.page.js b/cypress/e2e/pages/main.page.js index c4c628e053..aea2e04bd0 100644 --- a/cypress/e2e/pages/main.page.js +++ b/cypress/e2e/pages/main.page.js @@ -15,3 +15,13 @@ export function verifyGoerliWalletHeader() { export function verifyHomeSafeUrl(safe) { cy.location('href', { timeout: 10000 }).should('include', constants.homeUrl + safe) } + +export function checkTextsExistWithinElement(element, texts) { + texts.forEach((text) => { + cy.wrap(element).findByText(text).should('exist') + }) +} + +export function verifyCheckboxeState(element, index, state) { + cy.get(element).eq(index).should(state) +} diff --git a/cypress/e2e/pages/safeapps.pages.js b/cypress/e2e/pages/safeapps.pages.js new file mode 100644 index 0000000000..4700348f6b --- /dev/null +++ b/cypress/e2e/pages/safeapps.pages.js @@ -0,0 +1,193 @@ +import * as constants from '../../support/constants' + +const searchAppInput = 'input[id="search-by-name"]' +const appUrlInput = 'input[name="appUrl"]' +const closePreviewWindowBtn = 'button[aria-label*="Close"][aria-label*="preview"]' + +const addBtnStr = /add/i +const noAppsStr = /no Safe Apps found/i +const bookmarkedAppsStr = /bookmarked Apps/i +const customAppsStr = /my custom Apps/i +const addCustomAppBtnStr = /add custom Safe App/i +const openSafeAppBtnStr = /open Safe App/i +const disclaimerTtle = /disclaimer/i +const continueBtnStr = /continue/i +const cameraCheckBoxStr = /camera/i +const microfoneCheckBoxStr = /microphone/i +const permissionRequestStr = /permissions request/i +const accessToAddressBookStr = /access to your address book/i +const acceptBtnStr = /accept/i +const clearAllBtnStr = /clear all/i +const allowAllPermissions = /allow all/i + +const appNotSupportedMsg = "The app doesn't support Safe App functionality" + +export const pinWalletConnectStr = /pin walletconnect/i +export const transactionBuilderStr = /pin transaction builder/i +export const logoWalletConnect = /logo.*walletconnect/i +export const walletConnectHeadlinePreview = /walletconnect/i +export const availableNetworksPreview = /available networks/i +export const connecttextPreview = 'Connect your Safe to any dApp that supports WalletConnect' +export const localStorageItem = + '{"https://safe-test-app.com":[{"feature":"camera","status":"granted"},{"feature":"microphone","status":"denied"}]}' +export const gridItem = 'main .MuiPaper-root > .MuiGrid-item' +export const linkNames = { + logo: /logo/i, +} + +export const permissionCheckboxes = { + camera: 'input[name="camera"]', + addressbook: 'input[name="requestAddressBook"]', + microphone: 'input[name="microphone"]', + geolocation: 'input[name="geolocation"]', + fullscreen: 'input[name="fullscreen"]', +} + +export const permissionCheckboxNames = { + camera: 'Camera', + addressbook: 'Address Book', + microphone: 'Microphone', + geolocation: 'Geolocation', + fullscreen: 'Fullscreen', +} +export function typeAppName(name) { + cy.get(searchAppInput).clear().type(name) +} + +export function clearSearchAppInput() { + cy.get(searchAppInput).clear() +} + +export function verifyLinkName(name) { + cy.findAllByRole('link', { name: name }).should('have.length', 1) +} + +export function clickOnApp(app) { + cy.findByRole('link', { name: app }).click() +} + +export function verifyNoAppsTextPresent() { + cy.contains(noAppsStr).should('exist') +} + +export function pinApp(app, pin = true) { + let str = 'Unpin' + if (!pin) str = 'Pin' + cy.findByLabelText(app) + .click() + .should(($el) => { + const ariaLabel = $el.attr('aria-label') + expect(ariaLabel).to.include(str) + }) +} + +export function clickOnBookmarkedAppsTab() { + cy.findByText(bookmarkedAppsStr).click() +} + +export function verifyAppCount(count) { + cy.findByText(`ALL (${count})`).should('be.visible') +} + +export function clickOnCustomAppsTab() { + cy.findByText(customAppsStr).click() +} + +export function clickOnAddCustomApp() { + cy.findByText(addCustomAppBtnStr).click() +} + +export function typeCustomAppUrl(url) { + cy.get(appUrlInput).clear().type(url) +} + +export function verifyAppNotSupportedMsg() { + cy.contains(appNotSupportedMsg).should('be.visible') +} + +export function verifyAppTitle(title) { + cy.findByRole('heading', { name: title }).should('exist') +} + +export function acceptTC() { + cy.findByRole('checkbox').click() +} + +export function clickOnAddBtn() { + cy.findByRole('button', { name: addBtnStr }).click() +} + +export function verifyAppDescription(descr) { + cy.findByText(descr).should('exist') +} + +export function clickOnOpenSafeAppBtn() { + cy.findByRole('link', { name: openSafeAppBtnStr }).click() + cy.wait(500) + verifyDisclaimerIsVisible() + cy.wait(500) +} + +function verifyDisclaimerIsVisible() { + cy.findByRole('heading', { name: disclaimerTtle }).should('be.visible') +} + +export function clickOnContinueBtn() { + return cy.findByRole('button', { name: continueBtnStr }).click() +} + +export function verifyCameraCheckBoxExists() { + cy.findByRole('checkbox', { name: cameraCheckBoxStr }).should('exist') +} + +export function verifyMicrofoneCheckBoxExists() { + return cy.findByRole('checkbox', { name: microfoneCheckBoxStr }).should('exist') +} + +export function storeAndVerifyPermissions() { + cy.waitForSelector(() => { + return cy + .findByRole('button', { name: continueBtnStr }) + .click() + .wait(500) + .should(() => { + const storedBrowserPermissions = JSON.parse(localStorage.getItem(constants.BROWSER_PERMISSIONS_KEY)) + const browserPermissions = Object.values(storedBrowserPermissions)[0][0] + const storedInfoModal = JSON.parse(localStorage.getItem(constants.INFO_MODAL_KEY)) + + expect(browserPermissions.feature).to.eq('camera') + expect(browserPermissions.status).to.eq('granted') + expect(storedInfoModal['5'].consentsAccepted).to.eq(true) + }) + }) +} + +export function verifyPreviewWindow(str1, str2, str3) { + cy.findByRole('heading', { name: str1 }).should('exist') + cy.findByText(str2).should('exist') + cy.findByText(str3).should('exist') +} + +export function closePreviewWindow() { + cy.get(closePreviewWindowBtn).click() +} + +export function verifyPermissionsRequestExists() { + cy.findByRole('heading', { name: permissionRequestStr }).should('exist') +} + +export function verifyAccessToAddressBookExists() { + cy.findByText(accessToAddressBookStr).should('exist') +} + +export function clickOnAcceptBtn() { + cy.findByRole('button', { name: acceptBtnStr }).click() +} + +export function uncheckAllPermissions(element) { + cy.wrap(element).findByText(clearAllBtnStr).click() +} + +export function checkAllPermissions(element) { + cy.wrap(element).findByText(allowAllPermissions).click() +} diff --git a/cypress/e2e/safe-apps/apps_list.cy.js b/cypress/e2e/safe-apps/apps_list.cy.js index 7a73abe742..a6db25138e 100644 --- a/cypress/e2e/safe-apps/apps_list.cy.js +++ b/cypress/e2e/safe-apps/apps_list.cy.js @@ -1,79 +1,77 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as safeapps from '../pages/safeapps.pages' + +const myCustomAppTitle = 'Cypress Test App' +const myCustomAppDescrAdded = 'Cypress Test App Description' describe('The Safe Apps list', () => { before(() => { - cy.visit(`/${constants.TEST_SAFE_2}/apps`, { failOnStatusCode: false }) - cy.findByText(/accept selection/i).click() + cy.clearLocalStorage() + cy.visit(constants.TEST_SAFE_2 + constants.appsUrl, { failOnStatusCode: false }) + main.acceptCookies() }) describe('When searching apps', () => { it('should filter the list by app name', () => { // Wait for /safe-apps response - cy.intercept('GET', '/**/safe-apps').then(() => { - cy.findByRole('textbox').type('walletconnect') - cy.findAllByRole('link', { name: /logo/i }).should('have.length', 1) + cy.intercept('GET', constants.appsEndpoint).then(() => { + safeapps.typeAppName(constants.appNames.walletConnect) + safeapps.verifyLinkName(safeapps.linkNames.logo) }) }) it('should filter the list by app description', () => { - cy.findByRole('textbox').clear().type('compose custom contract') - cy.findAllByRole('link', { name: /logo/i }).should('have.length', 1) + safeapps.typeAppName(constants.appNames.customContract) + safeapps.verifyLinkName(safeapps.linkNames.logo) }) it('should show a not found text when no match', () => { - cy.findByRole('textbox').clear().type('atextwithoutresults') - cy.findByText(/no Safe Apps found/i).should('exist') + safeapps.typeAppName(constants.appNames.noResults) + safeapps.verifyNoAppsTextPresent() }) }) describe('When browsing the apps list', () => { it('should allow to pin apps', () => { - cy.findByRole('textbox').clear() - cy.findByLabelText(/pin walletconnect/i).click() - cy.findByLabelText(/pin transaction builder/i).click() - cy.findByText(/bookmarked Apps/i).click() - cy.findByText('ALL (2)').should('exist') + safeapps.clearSearchAppInput() + safeapps.pinApp(safeapps.pinWalletConnectStr) + safeapps.pinApp(safeapps.transactionBuilderStr) + safeapps.clickOnBookmarkedAppsTab() + safeapps.verifyAppCount(2) }) it('should allow to unpin apps', () => { - cy.findAllByLabelText(/unpin walletConnect/i) - .first() - .click() - cy.findAllByLabelText(/unpin transaction builder/i) - .first() - .click() - cy.findByText('ALL (0)').should('exist') + safeapps.pinApp(safeapps.pinWalletConnectStr) + safeapps.pinApp(safeapps.transactionBuilderStr) + safeapps.verifyAppCount(0) }) }) describe('When adding a custom app', () => { it('should show an error when the app manifest is invalid', () => { - cy.intercept('GET', 'https://my-invalid-custom-app.com/manifest.json', { - name: 'My Custom App', + cy.intercept('GET', constants.invalidAppUrl, { + name: constants.testAppData.name, }) - cy.findByText(/my custom Apps/i).click() - cy.findByText(/add custom Safe App/i).click({ force: true }) - cy.findByLabelText(/Safe App url/i) - .clear() - .type('https://my-invalid-custom-app.com') - cy.contains("The app doesn't support Safe App functionality").should('exist') + safeapps.clickOnCustomAppsTab() + safeapps.clickOnAddCustomApp() + safeapps.typeCustomAppUrl(constants.invalidAppUrl) + safeapps.verifyAppNotSupportedMsg() }) it('should be added to the list within the custom apps section', () => { - cy.intercept('GET', 'https://my-valid-custom-app.com/manifest.json', { - name: 'My Custom App', - description: 'My Custom App Description', + cy.intercept('GET', constants.validAppUrlJson, { + name: constants.testAppData.name, + description: constants.testAppData.descr, icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml' }], }) - cy.findByLabelText(/Safe App url/i) - .clear() - .type('https://my-valid-custom-app.com') - cy.findByRole('heading', { name: /my custom app/i }).should('exist') - cy.findByRole('checkbox').click() - cy.findByRole('button', { name: /add/i }).click() - cy.findByText('ALL (1)').should('exist') - cy.findByText(/my custom app description/i).should('exist') + safeapps.typeCustomAppUrl(constants.validAppUrl) + safeapps.verifyAppTitle(myCustomAppTitle) + safeapps.acceptTC() + safeapps.clickOnAddBtn() + safeapps.verifyAppCount(1) + safeapps.verifyAppDescription(myCustomAppDescrAdded) }) }) }) diff --git a/cypress/e2e/safe-apps/browser_permissions.cy.js b/cypress/e2e/safe-apps/browser_permissions.cy.js index 8e1d3d72a6..fc11c368c6 100644 --- a/cypress/e2e/safe-apps/browser_permissions.cy.js +++ b/cypress/e2e/safe-apps/browser_permissions.cy.js @@ -1,13 +1,16 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as safeapps from '../pages/safeapps.pages' describe('The Browser permissions system', () => { describe('When the safe app requires permissions', () => { beforeEach(() => { + cy.clearLocalStorage() cy.fixture('safe-app').then((html) => { - cy.intercept('GET', `${constants.appUrlProd}/*`, html) + cy.intercept('GET', `${constants.testAppUrl}/*`, html) cy.intercept('GET', `*/manifest.json`, { - name: 'Cypress Test App', - description: 'Cypress Test App Description', + name: constants.testAppData.name, + description: constants.testAppData.descr, icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml' }], safe_apps_permissions: ['camera', 'microphone'], }) @@ -15,23 +18,18 @@ describe('The Browser permissions system', () => { }) it('should show a permissions slide to the user', () => { - cy.visitSafeApp(`${constants.appUrlProd}/app`) - - cy.findByRole('checkbox', { name: /camera/i }).should('exist') - cy.findByRole('checkbox', { name: /microphone/i }).should('exist') + cy.visitSafeApp(`${constants.testAppUrl}/app`) + safeapps.verifyCameraCheckBoxExists() + safeapps.verifyMicrofoneCheckBoxExists() }) it('should allow to change, accept and store the selection', () => { - cy.findByText(/accept selection/i).click() + main.acceptCookies() + safeapps.verifyMicrofoneCheckBoxExists().click() - cy.findByRole('checkbox', { name: /microphone/i }).click() - cy.findByRole('button', { name: /continue/i }) - .click() - .should(() => { - expect(window.localStorage.getItem(constants.BROWSER_PERMISSIONS_KEY)).to.eq( - '{"https://safe-test-app.com":[{"feature":"camera","status":"granted"},{"feature":"microphone","status":"denied"}]}', - ) - }) + safeapps.clickOnContinueBtn().should(() => { + expect(window.localStorage.getItem(constants.BROWSER_PERMISSIONS_KEY)).to.eq(safeapps.localStorageItem) + }) }) }) }) diff --git a/cypress/e2e/safe-apps/info_modal.cy.js b/cypress/e2e/safe-apps/info_modal.cy.js index 1c73118b65..3ea79a148c 100644 --- a/cypress/e2e/safe-apps/info_modal.cy.js +++ b/cypress/e2e/safe-apps/info_modal.cy.js @@ -1,36 +1,28 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as safeapps from '../pages/safeapps.pages' describe('The Safe Apps info modal', () => { before(() => { - cy.visit(`/${constants.TEST_SAFE_2}/apps`, { failOnStatusCode: false }) - cy.findByText(/accept selection/i).click() + cy.clearLocalStorage() + cy.visit(constants.TEST_SAFE_2 + constants.appsUrl, { failOnStatusCode: false }) + main.acceptCookies() }) describe('when opening a Safe App', () => { it('should show the disclaimer', () => { - cy.findByRole('link', { name: /logo.*walletconnect/i }).click() - cy.findByRole('link', { name: /open Safe App/i }).click() - cy.findByRole('heading', { name: /disclaimer/i }).should('exist') + safeapps.clickOnApp(safeapps.logoWalletConnect) + safeapps.clickOnOpenSafeAppBtn() }) it('should show the permissions slide if the app require permissions', () => { - cy.findByRole('button', { name: /continue/i }).click() + safeapps.clickOnContinueBtn() cy.wait(500) // wait for the animation to finish - cy.findByRole('checkbox', { name: /camera/i }).should('exist') + safeapps.verifyCameraCheckBoxExists() }) it('should store the permissions and consents decision when accepted', () => { - cy.findByRole('button', { name: /continue/i }) - .click() - .should(() => { - const storedBrowserPermissions = JSON.parse(localStorage.getItem(constants.BROWSER_PERMISSIONS_KEY)) - const browserPermissions = Object.values(storedBrowserPermissions)[0][0] - const storedInfoModal = JSON.parse(localStorage.getItem(constants.INFO_MODAL_KEY)) - - expect(browserPermissions.feature).to.eq('camera') - expect(browserPermissions.status).to.eq('granted') - expect(storedInfoModal['5'].consentsAccepted).to.eq(true) - }) + safeapps.storeAndVerifyPermissions() }) }) }) diff --git a/cypress/e2e/safe-apps/permissions_settings.cy.js b/cypress/e2e/safe-apps/permissions_settings.cy.js index d1696f2931..629f117dc3 100644 --- a/cypress/e2e/safe-apps/permissions_settings.cy.js +++ b/cypress/e2e/safe-apps/permissions_settings.cy.js @@ -1,36 +1,41 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as safeapps from '../pages/safeapps.pages' let $dapps = [] +const app1 = 'https://app1.com' +const app3 = 'https://app3.com' describe('The Safe Apps permissions settings section', () => { before(() => { + cy.clearLocalStorage() cy.on('window:before:load', (window) => { window.localStorage.setItem( constants.BROWSER_PERMISSIONS_KEY, JSON.stringify({ - 'https://app1.com': [ + app1: [ { feature: 'camera', status: 'granted' }, { feature: 'fullscreen', status: 'granted' }, { feature: 'geolocation', status: 'granted' }, ], - 'https://app2.com': [{ feature: 'microphone', status: 'granted' }], - 'https://app3.com': [{ feature: 'camera', status: 'denied' }], + app2: [{ feature: 'microphone', status: 'granted' }], + app3: [{ feature: 'camera', status: 'denied' }], }), ) window.localStorage.setItem( constants.SAFE_PERMISSIONS_KEY, JSON.stringify({ - 'https://app2.com': [ + app2: [ { - invoker: 'https://app1.com', + invoker: app1, parentCapability: 'requestAddressBook', date: 1666103778276, caveats: [], }, ], - 'https://app4.com': [ + app4: [ { - invoker: 'https://app3.com', + invoker: app3, parentCapability: 'requestAddressBook', date: 1666103787026, caveats: [], @@ -40,8 +45,8 @@ describe('The Safe Apps permissions settings section', () => { ) }) - cy.visit(`${constants.TEST_SAFE_2}/settings/safe-apps`, { failOnStatusCode: false }) - cy.findByText(/accept selection/i).click() + cy.visit(constants.TEST_SAFE_2 + constants.appSettingsUrl, { failOnStatusCode: false }) + main.acceptCookies() }) it('should show the permissions configuration for each stored app', () => { @@ -50,78 +55,59 @@ describe('The Safe Apps permissions settings section', () => { describe('For each app', () => { before(() => { - cy.get('main .MuiPaper-root > .MuiGrid-item').then((items) => { + cy.get(safeapps.gridItem).then((items) => { $dapps = items }) }) it('app1 should have camera, full screen and geo permissions', () => { - cy.wrap($dapps[0]) - .findByText(/https:\/\/app1.com/i) - .should('exist') - cy.wrap($dapps[0]) - .findByText(/camera/i) - .should('exist') - cy.wrap($dapps[0]) - .findByText(/fullscreen/i) - .should('exist') - cy.wrap($dapps[0]) - .findByText(/geolocation/i) - .should('exist') - - cy.wrap($dapps[0]).findAllByRole('checkbox').should('have.checked') + const app1Data = [ + 'app1', + safeapps.permissionCheckboxNames.camera, + safeapps.permissionCheckboxNames.fullscreen, + safeapps.permissionCheckboxNames.geolocation, + ] + + main.checkTextsExistWithinElement($dapps[0], app1Data) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.camera, 0, constants.checkboxStates.checked) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.geolocation, 0, constants.checkboxStates.checked) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.fullscreen, 0, constants.checkboxStates.checked) }) it('app2 should have address book and microphone permissions', () => { - cy.wrap($dapps[1]) - .findByText(/https:\/\/app2.com/i) - .should('exist') - cy.wrap($dapps[1]) - .findByText(/address book/i) - .should('exist') - cy.wrap($dapps[1]) - .findByText(/microphone/i) - .should('exist') - - cy.wrap($dapps[1]).findAllByRole('checkbox').should('have.checked') + const app2Data = [ + 'app2', + safeapps.permissionCheckboxNames.addressbook, + safeapps.permissionCheckboxNames.microphone, + ] + + main.checkTextsExistWithinElement($dapps[1], app2Data) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.microphone, 0, constants.checkboxStates.checked) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 0, constants.checkboxStates.checked) }) it('app3 should have camera permissions', () => { - cy.wrap($dapps[2]) - .findByText(/https:\/\/app3.com/i) - .should('exist') - cy.wrap($dapps[2]) - .findByText(/camera/i) - .should('exist') + const app3Data = ['app3', safeapps.permissionCheckboxNames.camera] - cy.wrap($dapps[2]) - .findByLabelText(/camera/i) - .should('have.not.checked') + main.checkTextsExistWithinElement($dapps[2], app3Data) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.camera, 1, constants.checkboxStates.unchecked) }) it('app4 should have address book permissions', () => { - cy.wrap($dapps[3]) - .findByText(/https:\/\/app4.com/i) - .should('exist') - cy.wrap($dapps[3]) - .findByText(/address book/i) - .should('exist') - - cy.wrap($dapps[3]) - .findByLabelText(/address book/i) - .should('have.checked') + const app4Data = ['app4', safeapps.permissionCheckboxNames.addressbook] + + main.checkTextsExistWithinElement($dapps[3], app4Data) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 1, constants.checkboxStates.checked) }) it('should allow to allow all or clear all the checkboxes at once', () => { - cy.wrap($dapps[1]) - .findByText(/clear all/i) - .click() - cy.wrap($dapps[1]).findAllByRole('checkbox').should('have.not.checked') + safeapps.uncheckAllPermissions($dapps[1]) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 0, constants.checkboxStates.unchecked) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.microphone, 0, constants.checkboxStates.unchecked) - cy.wrap($dapps[1]) - .findByText(/allow all/i) - .click() - cy.wrap($dapps[1]).findAllByRole('checkbox').should('have.checked') + safeapps.checkAllPermissions($dapps[1]) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.addressbook, 0, constants.checkboxStates.checked) + main.verifyCheckboxeState(safeapps.permissionCheckboxes.microphone, 0, constants.checkboxStates.checked) }) it('should allow to remove apps and reflect it in the localStorage', () => { diff --git a/cypress/e2e/safe-apps/preview_drawer.cy.js b/cypress/e2e/safe-apps/preview_drawer.cy.js index 0514e8e49b..ed0b366935 100644 --- a/cypress/e2e/safe-apps/preview_drawer.cy.js +++ b/cypress/e2e/safe-apps/preview_drawer.cy.js @@ -1,24 +1,27 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as safeapps from '../pages/safeapps.pages' describe('The Safe Apps info modal', () => { before(() => { + cy.clearLocalStorage() cy.visit(`/${constants.TEST_SAFE_2}/apps`, { failOnStatusCode: false }) - cy.findByText(/accept selection/i).click() + main.acceptCookies() }) describe('when opening a Safe App from the app list', () => { it('should show the preview drawer', () => { - cy.findByRole('link', { name: /logo.*walletconnect/i }).click() - cy.findByRole('presentation').within((presentation) => { - cy.findByRole('heading', { name: /walletconnect/i }).should('exist') - cy.findByText('Connect your Safe to any dApp that supports WalletConnect').should('exist') - cy.findByText(/available networks/i).should('exist') - cy.findByLabelText(/pin walletconnect/i).click() - cy.findByLabelText(/unpin walletconnect/i) - .should('exist') - .click() - cy.findByLabelText(/pin walletconnect/i).should('exist') - cy.findByLabelText(/close walletconnect preview/i).click() + safeapps.clickOnApp(safeapps.logoWalletConnect) + + cy.findByRole('presentation').within(() => { + safeapps.verifyPreviewWindow( + safeapps.walletConnectHeadlinePreview, + safeapps.connecttextPreview, + safeapps.availableNetworksPreview, + ) + safeapps.pinApp(safeapps.pinWalletConnectStr) + safeapps.pinApp(safeapps.pinWalletConnectStr, false) + safeapps.closePreviewWindow() }) cy.findByRole('presentation').should('not.exist') }) diff --git a/cypress/e2e/safe-apps/safe_permissions.cy.js b/cypress/e2e/safe-apps/safe_permissions.cy.js index 26b775b669..33e4f74c49 100644 --- a/cypress/e2e/safe-apps/safe_permissions.cy.js +++ b/cypress/e2e/safe-apps/safe_permissions.cy.js @@ -1,12 +1,16 @@ import * as constants from '../../support/constants' +import * as safeapps from '../pages/safeapps.pages' describe('The Safe permissions system', () => { + before(() => { + cy.clearLocalStorage() + }) beforeEach(() => { cy.fixture('safe-app').then((html) => { - cy.intercept('GET', `${constants.appUrlProd}/*`, html) + cy.intercept('GET', `${constants.testAppUrl}/*`, html) cy.intercept('GET', `*/manifest.json`, { - name: 'Cypress Test App', - description: 'Cypress Test App Description', + name: constants.testAppData.name, + description: constants.testAppData.descr, icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml' }], }) }) @@ -14,17 +18,15 @@ describe('The Safe permissions system', () => { describe('When requesting permissions with wallet_requestPermissions', () => { it('should show the permissions prompt and return the permissions on accept', () => { - cy.visitSafeApp(`${constants.appUrlProd}/request-permissions`) - - cy.findByRole('heading', { name: /permissions request/i }).should('exist') - cy.findByText(/access to your address book/i).should('exist') - - cy.findByRole('button', { name: /accept/i }).click() + cy.visitSafeApp(constants.testAppUrl + constants.requestPermissionsUrl) + safeapps.verifyPermissionsRequestExists() + safeapps.verifyAccessToAddressBookExists() + safeapps.clickOnAcceptBtn() cy.get('@safeAppsMessage').should('have.been.calledWithMatch', { data: [ { - invoker: 'https://safe-test-app.com', + invoker: constants.testAppUrl, parentCapability: 'requestAddressBook', date: Cypress.sinon.match.number, caveats: [], @@ -40,9 +42,9 @@ describe('The Safe permissions system', () => { window.localStorage.setItem( constants.SAFE_PERMISSIONS_KEY, JSON.stringify({ - [constants.appUrlProd]: [ + [constants.testAppUrl]: [ { - invoker: constants.appUrlProd, + invoker: constants.testAppUrl, parentCapability: 'requestAddressBook', date: 1111111111111, caveats: [], @@ -52,12 +54,12 @@ describe('The Safe permissions system', () => { ) }) - cy.visitSafeApp(`${constants.appUrlProd}/get-permissions`) + cy.visitSafeApp(constants.testAppUrl + constants.getPermissionsUrl) cy.get('@safeAppsMessage').should('have.been.calledWithMatch', { data: [ { - invoker: constants.appUrlProd, + invoker: constants.testAppUrl, parentCapability: 'requestAddressBook', date: Cypress.sinon.match.number, caveats: [], diff --git a/cypress/e2e/safe-apps/tx_modal.cy.js b/cypress/e2e/safe-apps/tx_modal.cy.js index 9ddc34b2a2..abbaced9c2 100644 --- a/cypress/e2e/safe-apps/tx_modal.cy.js +++ b/cypress/e2e/safe-apps/tx_modal.cy.js @@ -1,12 +1,19 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' + +const testAppName = 'Cypress Test App' +const testAppDescr = 'Cypress Test App Description' describe('The transaction modal', () => { + before(() => { + cy.clearLocalStorage() + }) beforeEach(() => { cy.fixture('safe-app').then((html) => { - cy.intercept('GET', `${constants.appUrlProd}/*`, html) + cy.intercept('GET', `${constants.testAppUrl}/*`, html) cy.intercept('GET', `*/manifest.json`, { - name: 'Cypress Test App', - description: 'Cypress Test App Description', + name: testAppName, + description: testAppDescr, icons: [{ src: 'logo.svg', sizes: 'any', type: 'image/svg+xml' }], }) }) @@ -14,11 +21,11 @@ describe('The transaction modal', () => { describe('When sending a transaction from an app', () => { it('should show the transaction popup', { defaultCommandTimeout: 12000 }, () => { - cy.visitSafeApp(`${constants.appUrlProd}/dummy`) + cy.visitSafeApp(`${constants.testAppUrl}/dummy`) - cy.findByText(/accept selection/i).click() + main.acceptCookies() cy.findByRole('dialog').within(() => { - cy.findByText(/Cypress Test App/i) + cy.findByText(testAppName) }) }) }) diff --git a/cypress/e2e/smoke/address_book.cy.js b/cypress/e2e/smoke/address_book.cy.js index b64b9a72a7..92d12e606d 100644 --- a/cypress/e2e/smoke/address_book.cy.js +++ b/cypress/e2e/smoke/address_book.cy.js @@ -10,6 +10,7 @@ const EDITED_NAME = 'Edited Owner1' describe('Address book tests', () => { before(() => { + cy.clearLocalStorage() cy.visit(constants.addressBookUrl + constants.GOERLI_TEST_SAFE) main.acceptCookies() }) diff --git a/cypress/e2e/smoke/balances.cy.js b/cypress/e2e/smoke/balances.cy.js index 320aa52c6a..bc6d0468b7 100644 --- a/cypress/e2e/smoke/balances.cy.js +++ b/cypress/e2e/smoke/balances.cy.js @@ -1,5 +1,6 @@ import * as constants from '../../support/constants' import * as balances from '../pages/balances.pages' +import * as main from '../../e2e/pages/main.page' const ASSETS_LENGTH = 8 const ASSET_NAME_COLUMN = 0 @@ -11,9 +12,10 @@ describe('Assets > Coins', () => { const fiatRegex = new RegExp(`([0-9]{1,3},)*[0-9]{1,3}.[0-9]{2}`) before(() => { + cy.clearLocalStorage() // Open the Safe used for testing - cy.visit(`/balances?safe=${constants.GOERLI_TEST_SAFE}`) - cy.contains('button', 'Accept selection').click() + cy.visit(constants.BALANCE_URL + constants.GOERLI_TEST_SAFE) + main.acceptCookies() // Table is loaded cy.contains('Görli Ether') diff --git a/cypress/e2e/smoke/balances_pagination.cy.js b/cypress/e2e/smoke/balances_pagination.cy.js index ceaeca7652..63202a9bd4 100644 --- a/cypress/e2e/smoke/balances_pagination.cy.js +++ b/cypress/e2e/smoke/balances_pagination.cy.js @@ -5,6 +5,7 @@ const ASSETS_LENGTH = 8 describe('Balance pagination tests', () => { before(() => { + cy.clearLocalStorage() // Open the Safe used for testing cy.visit(constants.BALANCE_URL + constants.PAGINATION_TEST_SAFE) cy.contains('button', 'Accept selection').click() diff --git a/cypress/e2e/smoke/batch_tx.cy.js b/cypress/e2e/smoke/batch_tx.cy.js index a410ae6986..ef9baa461b 100644 --- a/cypress/e2e/smoke/batch_tx.cy.js +++ b/cypress/e2e/smoke/batch_tx.cy.js @@ -8,6 +8,7 @@ const funds_second_tx = '0.002' describe('Create batch transaction', () => { before(() => { + cy.clearLocalStorage() cy.visit(constants.homeUrl + constants.TEST_SAFE) main.acceptCookies() cy.contains(constants.goerlyE2EWallet, { timeout: 10000 }) diff --git a/cypress/e2e/smoke/beamer.cy.js b/cypress/e2e/smoke/beamer.cy.js index 7266dea955..412a8b5d8a 100644 --- a/cypress/e2e/smoke/beamer.cy.js +++ b/cypress/e2e/smoke/beamer.cy.js @@ -1,31 +1,22 @@ import * as constants from '../../support/constants' +import * as addressbook from '../pages/address_book.page' +import * as main from '../../e2e/pages/main.page' describe('Beamer', () => { - it('should require accept "Updates" cookies to display Beamer', () => { - // Disable PWA, otherwise it will throw a security error - cy.visit(`/address-book?safe=${constants.GOERLI_TEST_SAFE}`) - - // Way to select the cookies banner without an id - cy.contains('Accept selection').click() - - // Open What's new - cy.contains("What's new").click() - - // Tells that the user has to accept "Beamer" cookies - cy.contains('accept the "Beamer" cookies') - - // "Beamer" is checked when the banner opens - cy.get('input[id="beamer"]').should('be.checked') - // Accept "Updates & Feedback" cookies - cy.contains('Accept selection').click() - cy.contains('Accept selection').should('not.exist') + before(() => { + cy.clearLocalStorage() + cy.visit(constants.addressBookUrl + constants.GOERLI_TEST_SAFE) + main.acceptCookies() + }) + it.skip('should require accept "Updates" cookies to display Beamer', () => { + addressbook.clickOnWhatsNewBtn() + addressbook.acceptBeamerCookies() + addressbook.verifyBeamerIsChecked() + main.acceptCookies() // wait for Beamer cookies to be set - cy.wait(600) - - // Open What's new - cy.contains("What's new").click({ force: true }) // clicks through the "lastPostTitle" - - cy.get('#beamerOverlay .iframeCointaner').should('exist') + cy.wait(1000) + addressbook.clickOnWhatsNewBtn(true) // clicks through the "lastPostTitle" + addressbook.verifyBeameriFrameExists() }) }) diff --git a/cypress/e2e/smoke/create_safe_simple.cy.js b/cypress/e2e/smoke/create_safe_simple.cy.js index da63618b5e..7d7b78dfe4 100644 --- a/cypress/e2e/smoke/create_safe_simple.cy.js +++ b/cypress/e2e/smoke/create_safe_simple.cy.js @@ -8,6 +8,7 @@ const ownerName2 = 'Test Owner Name 2' describe('Create Safe form', () => { it('should navigate to the form', () => { + cy.clearLocalStorage() cy.visit(constants.welcomeUrl) main.acceptCookies() main.verifyGoerliWalletHeader() diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index ab7d6b3d29..0e1dab05cb 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -7,6 +7,7 @@ const currentNonce = 3 describe('Queue a transaction on 1/N', () => { before(() => { + cy.clearLocalStorage() cy.visit(constants.homeUrl + constants.TEST_SAFE) main.acceptCookies() }) diff --git a/cypress/e2e/smoke/dashboard.cy.js b/cypress/e2e/smoke/dashboard.cy.js index 88f4249aa5..f4ce1b6ec1 100644 --- a/cypress/e2e/smoke/dashboard.cy.js +++ b/cypress/e2e/smoke/dashboard.cy.js @@ -4,6 +4,7 @@ import * as main from '../pages/main.page' describe('Dashboard', () => { before(() => { + cy.clearLocalStorage() cy.visit(constants.homeUrl + constants.TEST_SAFE) main.acceptCookies() dashboard.verifyConnectTransactStrIsVisible() diff --git a/cypress/e2e/smoke/import_export_data.cy.js b/cypress/e2e/smoke/import_export_data.cy.js index 5cc2636ca9..165ee847e6 100644 --- a/cypress/e2e/smoke/import_export_data.cy.js +++ b/cypress/e2e/smoke/import_export_data.cy.js @@ -5,6 +5,7 @@ import * as constants from '../../support/constants' describe('Import Export Data', () => { before(() => { + cy.clearLocalStorage() cy.visit(constants.welcomeUrl) main.acceptCookies() file.verifyImportBtnIsVisible() diff --git a/cypress/e2e/smoke/landing.cy.js b/cypress/e2e/smoke/landing.cy.js index 18da5edffb..751f1e6cfa 100644 --- a/cypress/e2e/smoke/landing.cy.js +++ b/cypress/e2e/smoke/landing.cy.js @@ -1,6 +1,7 @@ import * as constants from '../../support/constants' describe('Landing page', () => { it('redirects 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 4456d60a6c..b346898ccb 100644 --- a/cypress/e2e/smoke/load_safe.cy.js +++ b/cypress/e2e/smoke/load_safe.cy.js @@ -20,12 +20,17 @@ const OWNER_ADDRESS = constants.EOA describe('Load existing Safe', () => { before(() => { - cy.visit(constants.chainMaticUrl) + cy.clearLocalStorage() + cy.visit(constants.welcomeUrl) main.acceptCookies() safe.openLoadSafeForm() + cy.wait(2000) }) it('should allow choosing the network where the Safe exists', () => { + safe.clickNetworkSelector(constants.networks.goerli) + safe.selectPolygon() + cy.wait(2000) safe.clickNetworkSelector(constants.networks.polygon) safe.selectGoerli() }) diff --git a/cypress/e2e/smoke/nfts.cy.js b/cypress/e2e/smoke/nfts.cy.js index b920c6c1ed..55205fadb6 100644 --- a/cypress/e2e/smoke/nfts.cy.js +++ b/cypress/e2e/smoke/nfts.cy.js @@ -9,6 +9,7 @@ const nftsLink = 'https://testnets.opensea.io/assets/0x000000000faE8c6069596c9C8 describe('Assets > NFTs', () => { before(() => { + cy.clearLocalStorage() cy.visit(constants.balanceNftsUrl + constants.GOERLI_TEST_SAFE) main.acceptCookies() cy.contains(constants.goerlyE2EWallet) diff --git a/cypress/e2e/smoke/pending_actions.cy.js b/cypress/e2e/smoke/pending_actions.cy.js index 73f7a6a9ef..6f8a9edc81 100644 --- a/cypress/e2e/smoke/pending_actions.cy.js +++ b/cypress/e2e/smoke/pending_actions.cy.js @@ -1,13 +1,14 @@ import * as constants from '../../support/constants' import * as safe from '../pages/load_safe.pages' -import * as main from '../pages/main.page' describe('Pending actions', () => { before(() => { cy.visit(constants.welcomeUrl) - main.acceptCookies() + // main.acceptCookies() }) + //TODO: Discuss test logic + beforeEach(() => { // Uses the previously saved local storage // to preserve the wallet connection between tests @@ -18,7 +19,7 @@ describe('Pending actions', () => { cy.saveLocalStorageCache() }) - it('should add the Safe with the pending actions', () => { + it.skip('should add the Safe with the pending actions', () => { safe.openLoadSafeForm() safe.inputAddress(constants.TEST_SAFE) safe.clickOnNextBtn() @@ -27,7 +28,7 @@ describe('Pending actions', () => { safe.clickOnAddBtn() }) - it('should display the pending actions in the Safe list sidebar', () => { + it.skip('should display the pending actions in the Safe list sidebar', () => { safe.openSidebar() safe.verifyAddressInsidebar(constants.SIDEBAR_ADDRESS) safe.verifySidebarIconNumber(1) @@ -35,7 +36,7 @@ describe('Pending actions', () => { //cy.get('img[alt="E2E Wallet logo"]').next().contains('2').should('exist') }) - it('should have the right number of queued and signable transactions', () => { + it.skip('should have the right number of queued and signable transactions', () => { safe.verifyTransactionSectionIsVisible() safe.verifyNumberOfTransactions(1, 1) }) diff --git a/cypress/e2e/smoke/tx_history.cy.js b/cypress/e2e/smoke/tx_history.cy.js index 8f61bcf5fc..482840fc1e 100644 --- a/cypress/e2e/smoke/tx_history.cy.js +++ b/cypress/e2e/smoke/tx_history.cy.js @@ -1,25 +1,37 @@ import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as createTx from '../pages/create_tx.pages' -const INCOMING = 'Receive' -const OUTGOING = 'Send' +const INCOMING = 'Received' +const OUTGOING = 'Sent' const CONTRACT_INTERACTION = 'Contract interaction' +const str1 = 'True' +const str2 = '1337' +const str3 = '5688' + describe('Transaction history', () => { before(() => { + cy.clearLocalStorage() // Go to the test Safe transaction history - cy.visit(`/transactions/history?safe=${constants.GOERLI_TEST_SAFE}`) - cy.contains('button', 'Accept selection').click() + cy.visit(constants.transactionsHistoryUrl + constants.GOERLI_TEST_SAFE) + main.acceptCookies() }) it('should display October 9th transactions', () => { const DATE = 'Oct 9, 2022' const NEXT_DATE_LABEL = 'Feb 8, 2022' - - // Date label - cy.contains('div', DATE).should('exist') - - // Next date label - cy.contains('div', NEXT_DATE_LABEL).scrollIntoView() + const amount = '0.25 GOR' + const amount2 = '0.11 WETH' + const amount3 = '120,497.61 DAI' + const time = '4:56 PM' + const time2 = '4:59 PM' + const time3 = '5:00 PM' + const time4 = '5:01 PM' + const success = 'Success' + + createTx.verifyDateExists(DATE) + createTx.verifyDateExists(NEXT_DATE_LABEL) // Transaction summaries from October 9th const rows = cy.contains('div', DATE).nextUntil(`div:contains(${NEXT_DATE_LABEL})`) @@ -31,130 +43,77 @@ describe('Transaction history', () => { .last() .within(() => { // Type - cy.get('img').should('have.attr', 'alt', 'Received') - cy.contains('div', INCOMING).should('exist') + createTx.verifyImageAltTxt(0, INCOMING) + createTx.verifyStatus(constants.transactionStatus.received) // Info - cy.get('img[alt="GOR"]').should('be.visible') - cy.contains('span', '0.25 GOR').should('exist') - - // Time - cy.contains('span', '4:56 PM').should('exist') - - // Status - cy.contains('span', 'Success').should('exist') + createTx.verifyImageAltTxt(1, constants.tokenAbbreviation.gor) + createTx.verifyTransactionStrExists(amount) + createTx.verifyTransactionStrExists(time) + createTx.verifyTransactionStrExists(success) }) // CowSwap deposit of Wrapped Ether .prev() .within(() => { - // Nonce - cy.contains('0') - - // Type + createTx.verifyTransactionStrExists('0') // TODO: update next line after fixing the logo // cy.find('img').should('have.attr', 'src').should('include', WRAPPED_ETH) - cy.contains('div', 'Wrapped Ether').should('exist') - - // Info - cy.contains('div', 'deposit').should('exist') - - // Time - cy.contains('span', '4:59 PM').should('exist') - - // Status - cy.contains('span', 'Success').should('exist') + createTx.verifyTransactionStrExists(constants.tokenNames.wrappedEther) + createTx.verifyTransactionStrExists(constants.transactionStatus.deposit) + createTx.verifyTransactionStrExists(time2) + createTx.verifyTransactionStrExists(constants.transactionStatus.success) }) // CowSwap approval of Wrapped Ether .prev() .within(() => { - // Nonce - cy.contains('1') - + createTx.verifyTransactionStrExists('1') // Type // TODO: update next line after fixing the logo // cy.find('img').should('have.attr', 'src').should('include', WRAPPED_ETH) - cy.contains('div', 'WETH').should('exist') - - cy.contains('div', 'unlimited').should('exist') - - // Info - cy.contains('div', 'Approve').should('exist') - - // Time - cy.contains('span', '5:00 PM').should('exist') - - // Status - cy.contains('span', 'Success').should('exist') + createTx.verifyTransactionStrExists(constants.transactionStatus.approve) + createTx.verifyTransactionStrExists(time3) + createTx.verifyTransactionStrExists(constants.transactionStatus.success) }) // Contract interaction .prev() .within(() => { - // Nonce - cy.contains('2') - - // Type - cy.contains('div', 'Contract interaction').should('exist') - - // Time - cy.contains('span', '5:01 PM').should('exist') - - // Status - cy.contains('span', 'Success').should('exist') + createTx.verifyTransactionStrExists('2') + createTx.verifyTransactionStrExists(constants.transactionStatus.interaction) + createTx.verifyTransactionStrExists(time4) + createTx.verifyTransactionStrExists(constants.transactionStatus.success) }) // Send 0.11 WETH .prev() .within(() => { - // Type - cy.get('img').should('have.attr', 'alt', 'Sent') - cy.contains('div', 'Send').should('exist') - - // Info - cy.contains('span', '0.11 WETH').should('exist') - - // Time - cy.contains('span', '5:01 PM').should('exist') - - // Status - cy.contains('span', 'Success').should('exist') + createTx.verifyImageAltTxt(0, OUTGOING) + createTx.verifyTransactionStrExists(constants.transactionStatus.sent) + createTx.verifyTransactionStrExists(amount2) + createTx.verifyTransactionStrExists(time4) + createTx.verifyTransactionStrExists(constants.transactionStatus.success) }) // Receive 120 DAI .prev() .within(() => { - // Type - cy.contains('div', INCOMING).should('exist') - - // Info - cy.contains('span', '120,497.61 DAI').should('exist') - - // Time - cy.contains('span', '5:01 PM').should('exist') - - // Status - cy.contains('span', 'Success').should('exist') + createTx.verifyTransactionStrExists(constants.transactionStatus.received) + createTx.verifyTransactionStrExists(amount3) + createTx.verifyTransactionStrExists(time4) + createTx.verifyTransactionStrExists(constants.transactionStatus.success) }) }) it('should expand/collapse all actions', () => { - // Open the tx details - cy.contains('div', 'Mar 24, 2023') - .next() - .click() - .within(() => { - cy.contains('True').should('not.be.visible') - cy.contains('1337').should('not.be.visible') - cy.contains('5688').should('not.be.visible') - cy.contains('Expand all').click() - - // All the values in the actions must be visible - cy.contains('True').should('exist') - cy.contains('1337').should('exist') - cy.contains('5688').should('exist') - - // After collapse all the same values should not be visible - cy.contains('Collapse all').click() - cy.contains('True').should('not.be.visible') - cy.contains('1337').should('not.be.visible') - cy.contains('5688').should('not.be.visible') - }) + createTx.clickOnTransactionExpandableItem('Mar 24, 2023', () => { + createTx.verifyTransactionStrNotVible(str1) + createTx.verifyTransactionStrNotVible(str2) + createTx.verifyTransactionStrNotVible(str3) + createTx.clickOnExpandAllBtn() + createTx.verifyTransactionStrExists(str1) + createTx.verifyTransactionStrExists(str2) + createTx.verifyTransactionStrExists(str3) + createTx.clickOnCollapseAllBtn() + createTx.verifyTransactionStrNotVible(str1) + createTx.verifyTransactionStrNotVible(str2) + createTx.verifyTransactionStrNotVible(str3) + }) }) }) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index feea0642a2..77e3994062 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -17,16 +17,26 @@ export const goerlyE2EWallet = /E2E Wallet @ G(ö|oe)rli/ export const goerlySafeName = /g(ö|oe)rli-safe/ export const goerliToken = /G(ö|oe)rli Ether/ -export const appUrlProd = 'https://safe-test-app.com' +export const testAppUrl = 'https://safe-test-app.com' export const addressBookUrl = '/address-book?safe=' export const BALANCE_URL = '/balances?safe=' export const balanceNftsUrl = '/balances/nfts?safe=' export const transactionQueueUrl = '/transactions/queue?safe=' +export const transactionsHistoryUrl = '/transactions/history?safe=' export const openAppsUrl = '/apps/open?safe=' export const homeUrl = '/home?safe=' export const welcomeUrl = '/welcome' export const chainMaticUrl = '/welcome?chain=matic' -export const proposeEndPoint = '/**/propose' +export const appsUrl = '/apps' +export const requestPermissionsUrl = '/request-permissions' +export const getPermissionsUrl = '/get-permissions' +export const appSettingsUrl = '/settings/safe-apps' +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' + +export const proposeEndpoint = '/**/propose' +export const appsEndpoint = '/**/safe-apps' export const GOERLI_CSV_ENTRY = { name: 'goerli user 1', @@ -47,3 +57,32 @@ export const networks = { export const tokenAbbreviation = { gor: 'GOR', } + +export const appNames = { + walletConnect: 'walletconnect', + customContract: 'compose custom contract', + noResults: 'atextwithoutresults', +} + +export const testAppData = { + name: 'Cypress Test App', + descr: 'Cypress Test App Description', +} + +export const checkboxStates = { + unchecked: 'not.be.checked', + checked: 'be.checked', +} + +export const transactionStatus = { + received: 'Receive', + sent: 'Send', + deposit: 'deposit', + approve: 'Approve', + success: 'Success', + interaction: 'Contract interaction', +} + +export const tokenNames = { + wrappedEther: 'Wrapped Ether', +} diff --git a/package.json b/package.json index 97bf64ec71..a4e108301b 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "@types/semver": "^7.3.10", "@typescript-eslint/eslint-plugin": "^5.47.1", "cross-env": "^7.0.3", - "cypress": "^11.1.0", + "cypress": "^12.15.0", "cypress-file-upload": "^5.0.8", "eslint": "8.31.0", "eslint-config-next": "13.1.1", diff --git a/yarn.lock b/yarn.lock index fc3e6dd9ea..c0a8816726 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1116,7 +1116,7 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@cypress/request@^2.88.10": +"@cypress/request@2.88.12": version "2.88.12" resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590" integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA== @@ -4238,10 +4238,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== -"@types/node@^14.14.31": - version "14.18.56" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.56.tgz#09e092d684cd8cfbdb3c5e5802672712242f2600" - integrity sha512-+k+57NVS9opgrEn5l9c0gvD1r6C+PtyhVE4BTnMMRwiEA8ZO8uFcs6Yy2sXIy0eC95ZurBtRSvhZiHXBysbl6w== +"@types/node@^16.18.39": + version "16.18.53" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.53.tgz#21820fe4d5968aaf8071dabd1ee13d24ada1350a" + integrity sha512-vVmHeo4tpF8zsknALU90Hh24VueYdu45ZlXzYWFbom61YR4avJqTFDC3QlWzjuTdAv6/3xHaxiO9NrtVZXrkmw== "@types/papaparse@^5.3.1": version "5.3.8" @@ -6394,11 +6394,6 @@ commander@^2.20.0, commander@^2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" - integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== - commander@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" @@ -6688,14 +6683,14 @@ cypress-file-upload@^5.0.8: resolved "https://registry.yarnpkg.com/cypress-file-upload/-/cypress-file-upload-5.0.8.tgz#d8824cbeaab798e44be8009769f9a6c9daa1b4a1" integrity sha512-+8VzNabRk3zG6x8f8BWArF/xA/W0VK4IZNx3MV0jFWrJS/qKn8eHfa5nU73P9fOQAgwHFJx7zjg4lwOnljMO8g== -cypress@^11.1.0: - version "11.2.0" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-11.2.0.tgz#63edef8c387b687066c5493f6f0ad7b9ced4b2b7" - integrity sha512-u61UGwtu7lpsNWLUma/FKNOsrjcI6wleNmda/TyKHe0dOBcVjbCPlp1N6uwFZ0doXev7f/91YDpU9bqDCFeBLA== +cypress@^12.15.0: + version "12.17.4" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.4.tgz#b4dadf41673058493fa0d2362faa3da1f6ae2e6c" + integrity sha512-gAN8Pmns9MA5eCDFSDJXWKUpaL3IDd89N9TtIupjYnzLSmlpVr+ZR+vb4U/qaMp+lB6tBvAmt7504c3Z4RU5KQ== dependencies: - "@cypress/request" "^2.88.10" + "@cypress/request" "2.88.12" "@cypress/xvfb" "^1.2.4" - "@types/node" "^14.14.31" + "@types/node" "^16.18.39" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -6707,10 +6702,10 @@ cypress@^11.1.0: check-more-types "^2.24.0" cli-cursor "^3.1.0" cli-table3 "~0.6.1" - commander "^5.1.0" + commander "^6.2.1" common-tags "^1.8.0" dayjs "^1.10.4" - debug "^4.3.2" + debug "^4.3.4" enquirer "^2.3.6" eventemitter2 "6.4.7" execa "4.1.0" @@ -6725,12 +6720,13 @@ cypress@^11.1.0: listr2 "^3.8.3" lodash "^4.17.21" log-symbols "^4.0.0" - minimist "^1.2.6" + minimist "^1.2.8" ospath "^1.2.2" pretty-bytes "^5.6.0" + process "^0.11.10" proxy-from-env "1.0.0" request-progress "^3.0.0" - semver "^7.3.2" + semver "^7.5.3" supports-color "^8.1.1" tmp "~0.2.1" untildify "^4.0.0" @@ -10880,7 +10876,7 @@ minimatch@^5.0.1: dependencies: brace-expansion "^2.0.1" -minimist@^1.2.0, minimist@^1.2.6, minimist@~1.2.5: +minimist@^1.2.0, minimist@^1.2.6, minimist@^1.2.8, minimist@~1.2.5: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -12765,7 +12761,7 @@ semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: +semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==