Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Commit

Permalink
Chore: add cypress CI (#3900)
Browse files Browse the repository at this point in the history
* Chore: add cypress CI

* Move Cypress after the deployment

* Run Cypress on localhost

* Run in background

* Run after deployment

* Fix address book

* Fix intercom

* Use public RPC url

* e2e wallet

* Rerun

* Headless chrome

* Smoke suite

* Video false

* Spec smoke
  • Loading branch information
katspaugh authored May 24, 2022
1 parent f56e0f6 commit 20fc5ca
Show file tree
Hide file tree
Showing 17 changed files with 59 additions and 92 deletions.
33 changes: 8 additions & 25 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ jobs:
with:
message: |
## Deployment links
:orange_circle: [Rinkeby](${{ env.REVIEW_FEATURE_URL }}/rin) | :white_circle: [Mainnet](${{ env.REVIEW_FEATURE_URL }}/eth) | :purple_circle: [Polygon](${{ env.REVIEW_FEATURE_URL }}/matic) | :yellow_circle: [BSC](${{ env.REVIEW_FEATURE_URL }}/bnb) | :black_circle: [Arbitrum](${{ env.REVIEW_FEATURE_URL }}/arb1) | :green_circle: [Gnosis Chain](${{ env.REVIEW_FEATURE_URL }}/gno)
-|-|-|-|-|-
:white_circle: [Mainnet](${{ env.REVIEW_FEATURE_URL }}/eth)     :purple_circle: [Polygon](${{ env.REVIEW_FEATURE_URL }}/matic)     :orange_circle: [Rinkeby](${{ env.REVIEW_FEATURE_URL }}/rin)
repo-token: ${{ secrets.GITHUB_TOKEN }}
repo-token-user-login: 'github-actions[bot]'
if: success() && github.event.number
Expand Down Expand Up @@ -163,27 +162,11 @@ jobs:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
e2e:
name: Trigger e2e tests
if: success() && github.ref == 'refs/heads/dev'
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v1
with:
token: ${{ secrets.E2E_RUN_HOOK_ACCESS_TOKEN }}
repository: gnosis/safe-react-e2e-tests
event-type: run-e2e-from-safe-react
e2ePR:
name: Trigger e2e tests on pr
if: github.event.number
needs: deploy
runs-on: ubuntu-latest
steps:
- name: Repository Dispatch
uses: peter-evans/repository-dispatch@v1

# Cypress E2E tests
- name: Cypress run
uses: cypress-io/github-action@v2
with:
token: ${{ secrets.E2E_RUN_HOOK_ACCESS_TOKEN }}
repository: gnosis/safe-react-e2e-tests
event-type: run-e2e-tests-from-safe-react-on-pr
client-payload: '{"pr_number": "${{ github.event.number }}"}'
spec: cypress/integration/smoke/*.spec.js
video: false
config: baseUrl=https://pr${{ github.event.number }}--safereact.review-safe.gnosisdev.com/app/
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ jest.results.json
*.#*
cypress/videos
cypress/screenshots
cypress/downloads
Empty file added cypress/ci.json
Empty file.
5 changes: 0 additions & 5 deletions cypress/downloads/gnosis-safe-address-book-2022-04-27.csv

This file was deleted.

5 changes: 0 additions & 5 deletions cypress/fixtures/example.json

This file was deleted.

18 changes: 11 additions & 7 deletions cypress/integration/address_book.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'cypress-file-upload'
import { deleteDownloadsFolder } from '../utils/deleteDownloadsFolder'
const path = require('path')

const NAME = 'Owner1'
Expand All @@ -18,11 +17,10 @@ const GNO_CSV_ENTRY = {
}

describe('Address book', () => {
beforeEach(deleteDownloadsFolder)

it('should add and remove Address Book entries', () => {
cy.visit(`/${RINKEBY_TEST_SAFE}/address-book`)

// Add a new entry manually
cy.findByText('Create entry', { timeout: 6000 }).click()
cy.findByTestId('create-entry-input-name').type(NAME)
cy.findByTestId('create-entry-input-address').type(ENS_NAME)
Expand All @@ -31,31 +29,37 @@ describe('Address book', () => {
cy.findByText(NAME).should('exist')
cy.findByText(ADDRESS).should('exist')

// Edit the entry
cy.get('[title="Edit entry"]').click({ force: true }) //This is because the button is hidden
cy.findByTestId('create-entry-input-name').clear().type(EDITED_NAME)
cy.findByTestId('save-new-entry-btn-id').click()
cy.findByText(NAME).should('not.exist')
cy.findByText(EDITED_NAME).should('exist')

// Delete the entry
cy.get('[title="Delete entry"]').click({ force: true }) //This is because the button is hidden
cy.findByText('Delete').should('exist').click()
cy.findByText(EDITED_NAME).should('not.exist')
cy.get('.MuiSnackbarContent-action').click({ multiple: true }) // close the snackbar

cy.wait(4000) // Waiting for notifications to dissapear before clicking "Import"
// Import CSV
cy.get('[data-track="address-book: Import"]').click()
cy.get('[type="file"]').attachFile('../utils/files/address_book_test.csv')
cy.get('[type="file"]').attachFile('../fixtures/address_book_test.csv')
cy.get('.modal-footer').findByText('Import').click()
cy.findByText(RINKEBY_CSV_ENTRY.name).should('exist')
cy.findByText(RINKEBY_CSV_ENTRY.address).should('exist')

// Go to a Safe on Gnosis Chain
cy.get('nav').findByText('Rinkeby').click()
cy.findByText('Gnosis Chain').click()
cy.visit(`/${GNO_TEST_SAFE}/address-book`)
cy.findByText(GNO_CSV_ENTRY.name).should('exist')
cy.findByText(GNO_CSV_ENTRY.address).should('exist')

// Download the export file
const date = new Date()
const day = date.getUTCDate() < 10 ? `0${date.getUTCDate()}` : date.getUTCMonth()
const month = date.getUTCMonth() + 1 < 10 ? `0${date.getUTCMonth() + 1}` : date.getUTCMonth() + 1
const day = `00${date.getUTCDate()}`.slice(-2)
const month = `00${date.getUTCMonth() + 1}`.slice(-2)
const year = date.getUTCFullYear()
const fileName = `gnosis-safe-address-book-${year}-${month}-${day}.csv`

Expand Down
1 change: 0 additions & 1 deletion cypress/integration/create_safe.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ describe('Create Safe', () => {
cy.contains('button', 'Continue').click({ force: true })

cy.wait(500) // Not sure why without this ends with "Transaction underpriced"

cy.contains('button', 'Create').click()
cy.contains('Your Safe was created successfully', { timeout: 60000 })
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,7 @@ describe('Intercom and cookie prefs', () => {
cy.get('#intercom-container').should('exist')

// Intercom should be disabled on a Safe App page
cy.visit(`/${RINKEBY_TEST_SAFE}/apps`)

// Click on first app link
cy.get('[data-testid="safe_apps__all-apps-container"] a').first().click()
cy.visit(`/${RINKEBY_TEST_SAFE}/apps?appUrl=https://safe-apps.dev.gnosisdev.com/drain-safe`)
cy.get(intercomButton).should('not.exist')

// Go to Settings and change the cookie settings
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ describe('Load Safe', () => {

cy.get('[data-track="load-safe: Open stepper"]').click()
cy.findByText('Add existing Safe').should('exist')
cy.wait(1000) //Have to wait because clicking the switch network fails sometimes if not
cy.wait(1000) // Have to wait because clicking the switch network fails sometimes if not

//Step 1
// Network selection
cy.findByTestId('select-network-step').find('button').click()
cy.get('[aria-labelledby="select-network"]').should('exist')
cy.findByText('Ethereum').click()
Expand All @@ -29,7 +29,7 @@ describe('Load Safe', () => {
cy.findByText('Rinkeby').click()
cy.get('[data-track="load-safe: Continue"]').click()

//Step2
// Name
cy.findByTestId('load-safe-name-field').should('have.attr', 'placeholder').should('contain', 'rinkeby-safe')
cy.findByTestId('load-safe-name-field').type('Test safe name').should('have.value', 'Test safe name')

Expand All @@ -43,23 +43,23 @@ describe('Load Safe', () => {
.should('have.value', SAFE_ENS_NAME_TRANSLATED)
cy.findByTestId('qr-icon').click()
cy.get('[class="paper"]').find('button').contains('Upload an image').click()
cy.get('[type="file"]').attachFile('../utils/files/rinkeby_safe_QR.png')
cy.get('[type="file"]').attachFile('../fixtures/rinkeby_safe_QR.png')
cy.findByTestId('load-safe-address-field').should('have.value', SAFE_QR_CODE_ADDRESS)
cy.findByTestId('safeAddress-valid-address-adornment').should('exist')
cy.wait(3000) //have to wait or after clicking next what loads is the owners of the previous valids safe, not the one from the QR code
cy.get('[type="submit"]').click()

//Step3
// Owners
cy.findByPlaceholderText(OWNER_ENS_DEFAULT_NAME).parents('div[data-testid="owner-row"]').findByText(OWNER_ADDRESS)
cy.findByPlaceholderText(OWNER_ENS_DEFAULT_NAME).type('Test Owner Name').should('have.value', 'Test Owner Name')
cy.get('[data-track="load-safe: Continue"]').click()

//Review Step
// Review Step
cy.findByText('Test safe name').should('exist')
cy.findByText('Test Owner Name').should('exist')
cy.get('[data-track="load-safe: Add"]').click()

//Safe Loaded
// Safe loaded
cy.findByTestId('sidebar').findByText('Test safe name')
cy.findByTestId('sidebar').find('nav').findByText('Settings').click()
cy.findByTestId('sidebar').find('nav').findByText('Owners').click()
Expand Down
7 changes: 0 additions & 7 deletions cypress/utils/deleteDownloadsFolder.js

This file was deleted.

30 changes: 2 additions & 28 deletions src/logic/wallets/onboard.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import Onboard from 'bnc-onboard'
import { WalletModule } from 'bnc-onboard/dist/src/interfaces'
import { API, Initialization } from 'bnc-onboard/dist/src/interfaces'
import { FEATURES } from '@gnosis.pm/safe-react-gateway-sdk'

import { _getChainId, getChainName } from 'src/config'
import transactionDataCheck from 'src/logic/wallets/transactionDataCheck'
import { getSupportedWallets } from 'src/logic/wallets/utils/walletList'
import { getSupportedWallets, isCypressAskingForConnectedState } from 'src/logic/wallets/utils/walletList'
import { ChainId, CHAIN_ID } from 'src/config/chain.d'
import { loadFromStorageWithExpiry, removeFromStorageWithExpiry, saveToStorageWithExpiry } from 'src/utils/storage'
import { store } from 'src/store'
Expand All @@ -18,8 +17,6 @@ import { getChains } from 'src/config/cache/chains'
import { shouldSwitchNetwork, switchNetwork } from 'src/logic/wallets/utils/network'
import { isPairingModule } from 'src/logic/wallets/pairing/utils'
import { checksumAddress } from 'src/utils/checksumAddress'
import HDWalletProvider from '@truffle/hdwallet-provider'
import { E2E_MNEMONIC, E2E_PROVIDER_URL } from 'src/utils/constants'

const LAST_USED_PROVIDER_KEY = 'SAFE__lastUsedProvider'

Expand All @@ -29,10 +26,6 @@ export const saveLastUsedProvider = (name: string): void => {
saveToStorageWithExpiry(LAST_USED_PROVIDER_KEY, name, expiry)
}

const isCypressAskingForConnectedState = (): boolean => {
return window.Cypress && window.cypressConfig?.connected
}

export const loadLastUsedProvider = (): string | undefined => {
if (isCypressAskingForConnectedState()) {
return 'e2e-wallet'
Expand Down Expand Up @@ -61,25 +54,6 @@ const hasENSSupport = (chainId: ChainId): boolean => {

export const BLOCK_POLLING_INTERVAL = 1000 * 60 * 60 // 1 hour

const customSDKWallet: WalletModule = {
name: 'e2e-wallet',
type: 'injected',
wallet: async (helpers) => {
const { createModernProviderInterface } = helpers
const provider = new HDWalletProvider({
mnemonic: E2E_MNEMONIC,
providerOrUrl: E2E_PROVIDER_URL,
})

return {
provider,
interface: createModernProviderInterface(provider),
}
},
desktop: true,
mobile: true,
}

const getOnboard = (chainId: ChainId): API => {
const config: Initialization = {
networkId: parseInt(chainId, 10),
Expand All @@ -104,7 +78,7 @@ const getOnboard = (chainId: ChainId): API => {
},
walletSelect: {
description: 'Please select a wallet to connect to Gnosis Safe',
wallets: isCypressAskingForConnectedState() ? [customSDKWallet] : getSupportedWallets(chainId),
wallets: getSupportedWallets(chainId),
},
walletCheck: [
{ checkName: 'derivationPath' },
Expand Down
30 changes: 29 additions & 1 deletion src/logic/wallets/utils/walletList.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { WalletInitOptions, WalletModule, WalletSelectModuleOptions } from 'bnc-onboard/dist/src/interfaces'

import { getRpcServiceUrl, getDisabledWallets, getChainById } from 'src/config'
import { getRpcServiceUrl, getDisabledWallets, getChainById, getPublicRpcUrl } from 'src/config'
import { ChainId, WALLETS } from 'src/config/chain.d'
import { FORTMATIC_KEY, PORTIS_ID, WC_BRIDGE } from 'src/utils/constants'
import getPairingModule from 'src/logic/wallets/pairing/module'
import { isPairingSupported } from 'src/logic/wallets/pairing/utils'
import { getChains } from 'src/config/cache/chains'
import HDWalletProvider from '@truffle/hdwallet-provider'
import { E2E_MNEMONIC } from 'src/utils/constants'

type Wallet = (WalletInitOptions | WalletModule) & {
desktop: boolean // Whether wallet supports desktop app
Expand Down Expand Up @@ -78,6 +80,27 @@ const wallets = (chainId: ChainId): Wallet[] => {
{ walletName: WALLETS.OPERA_TOUCH, desktop: false },
]
}
const getTestWallet = (): WalletModule => ({
name: 'e2e-wallet',
type: 'injected',
wallet: async (helpers) => {
const { createModernProviderInterface } = helpers
const provider = new HDWalletProvider({
mnemonic: E2E_MNEMONIC,
providerOrUrl: getPublicRpcUrl(),
})
return {
provider,
interface: createModernProviderInterface(provider),
}
},
desktop: true,
mobile: true,
})

export const isCypressAskingForConnectedState = (): boolean => {
return typeof window !== 'undefined' && window.Cypress && window.cypressConfig?.connected
}

export const isSupportedWallet = (name: WALLETS | string): boolean => {
return !getDisabledWallets().some((walletName) => {
Expand All @@ -87,6 +110,11 @@ export const isSupportedWallet = (name: WALLETS | string): boolean => {
}

export const getSupportedWallets = (chainId: ChainId): WalletSelectModuleOptions['wallets'] => {
// E2E test wallet
if (isCypressAskingForConnectedState()) {
return [getTestWallet()]
}

const supportedWallets: WalletSelectModuleOptions['wallets'] = wallets(chainId)
.filter(({ walletName, desktop }) => {
if (!isSupportedWallet(walletName)) {
Expand Down
2 changes: 0 additions & 2 deletions src/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const PUBLIC_URL = process.env.PUBLIC_URL
export const TX_SERVICE_VERSION = '1'
export const INTERCOM_ID = IS_PRODUCTION ? process.env.REACT_APP_INTERCOM_ID : 'plssl1fl'
export const BEAMER_ID = IS_PRODUCTION ? process.env.REACT_APP_BEAMER_ID : 'ehlRMhQi41258'
export const GOOGLE_ANALYTICS_ID = process.env.REACT_APP_GOOGLE_ANALYTICS || ''
export const SENTRY_DSN = process.env.REACT_APP_SENTRY_DSN || ''
export const PORTIS_ID = process.env.REACT_APP_PORTIS_ID ?? '852b763d-f28b-4463-80cb-846d7ec5806b'
export const FORTMATIC_KEY = process.env.REACT_APP_FORTMATIC_KEY ?? 'pk_test_CAD437AA29BE0A40'
Expand All @@ -34,7 +33,6 @@ export const GOOGLE_TAG_MANAGER_DEVELOPMENT_AUTH = process.env.REACT_APP_GOOGLE_

// Cypress tests
export const E2E_MNEMONIC = process.env.REACT_APP_E2E_MNEMONIC || ''
export const E2E_PROVIDER_URL = process.env.REACT_APP_E2E_PROVIDER_URL || ''

// localStorage-related constants
export const LS_NAMESPACE = 'SAFE'
Expand Down

0 comments on commit 20fc5ca

Please sign in to comment.