From c6efa327f6300d74b0bf97ccf43e8071eb4c3f28 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:49:15 +0100 Subject: [PATCH 01/38] Chore: update WalletConnect SDK (#3158) --- package.json | 9 ++-- yarn.lock | 122 +++++++++++++++++++++------------------------------ 2 files changed, 52 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index 8f18bcede2..862dee9c87 100644 --- a/package.json +++ b/package.json @@ -33,9 +33,6 @@ "node": ">=16" }, "resolutions": { - "@walletconnect/core": "^2.10.6", - "@walletconnect/ethereum-provider": "^2.10.6", - "@walletconnect/utils": "^2.10.6", "@web3-onboard/trezor/**/protobufjs": "^7.2.4", "@safe-global/safe-core-sdk-types/**/ethers": "^6.10.0", "@safe-global/protocol-kit/**/ethers": "^6.10.0", @@ -62,8 +59,8 @@ "@spindl-xyz/attribution-lite": "^1.4.0", "@tkey-mpc/common-types": "^8.2.2", "@truffle/hdwallet-provider": "^2.1.4", - "@walletconnect/utils": "^2.10.6", - "@walletconnect/web3wallet": "^1.9.5", + "@walletconnect/utils": "^2.11.0", + "@walletconnect/web3wallet": "^1.10.0", "@web3-onboard/coinbase": "^2.2.6", "@web3-onboard/core": "^2.21.2", "@web3-onboard/injected-wallets": "^2.10.7", @@ -120,7 +117,7 @@ "@types/react-qr-reader": "^2.1.5", "@types/semver": "^7.3.10", "@typescript-eslint/eslint-plugin": "^5.47.1", - "@walletconnect/types": "^2.10.1", + "@walletconnect/types": "^2.11.0", "cross-env": "^7.0.3", "cypress": "^12.15.0", "cypress-file-upload": "^5.0.8", diff --git a/yarn.lock b/yarn.lock index b43706ed6a..92ad7fb689 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5697,10 +5697,10 @@ events "^3.3.0" isomorphic-unfetch "^3.1.0" -"@walletconnect/core@2.10.6", "@walletconnect/core@^2.10.1", "@walletconnect/core@^2.10.6": - version "2.10.6" - resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.10.6.tgz#786b0d2e2045c210c917e29bfa0498bbc210be20" - integrity sha512-Z4vh4ZdfcoQjgPEOxeuF9HUZCVLtV3MgRbS/awLIj/omDrFnOwlBhxi5Syr4Y8muVGC0ocRetQYHae0/gX5crQ== +"@walletconnect/core@2.11.0", "@walletconnect/core@^2.10.1", "@walletconnect/core@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.11.0.tgz#3a4e301077b2f858fd916b7a20b5b984d1afce63" + integrity sha512-2Tjp5BCevI7dbmqo/OrCjX4tqgMqwJNQLlQAlphqPfvwlF9+tIu6pGcVbSN3U9zyXzWIZCeleqEaWUeSeET4Ew== dependencies: "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-provider" "1.0.13" @@ -5713,9 +5713,10 @@ "@walletconnect/relay-auth" "^1.0.4" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.10.6" - "@walletconnect/utils" "2.10.6" + "@walletconnect/types" "2.11.0" + "@walletconnect/utils" "2.11.0" events "^3.3.0" + isomorphic-unfetch "3.1.0" lodash.isequal "4.5.0" uint8arrays "^3.1.0" @@ -5726,20 +5727,20 @@ dependencies: tslib "1.14.1" -"@walletconnect/ethereum-provider@^2.10.6", "@walletconnect/ethereum-provider@^2.11.0": - version "2.10.6" - resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.10.6.tgz#53720771cc2d6accd452916a853ac927f26acbaa" - integrity sha512-bBQ+yUfxLv8VxNttgNKY7nED35gSVayO/BnLHbNKvyV1gpvSCla5mWB9MsXuQs70MK0g+/qtgRVSrOtdSubaNQ== +"@walletconnect/ethereum-provider@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@walletconnect/ethereum-provider/-/ethereum-provider-2.11.0.tgz#feb90368d8b2608d7d120ac8feeb3e26eac8c709" + integrity sha512-YrTeHVjuSuhlUw7SQ6xBJXDuJ6iAC+RwINm9nVhoKYJSHAy3EVSJZOofMKrnecL0iRMtD29nj57mxAInIBRuZA== dependencies: "@walletconnect/jsonrpc-http-connection" "^1.0.7" "@walletconnect/jsonrpc-provider" "^1.0.13" "@walletconnect/jsonrpc-types" "^1.0.3" "@walletconnect/jsonrpc-utils" "^1.0.8" - "@walletconnect/modal" "^2.4.3" - "@walletconnect/sign-client" "2.10.6" - "@walletconnect/types" "2.10.6" - "@walletconnect/universal-provider" "2.10.6" - "@walletconnect/utils" "2.10.6" + "@walletconnect/modal" "^2.6.2" + "@walletconnect/sign-client" "2.11.0" + "@walletconnect/types" "2.11.0" + "@walletconnect/universal-provider" "2.11.0" + "@walletconnect/utils" "2.11.0" events "^3.3.0" "@walletconnect/events@^1.0.1": @@ -5805,14 +5806,6 @@ events "^3.3.0" ws "^7.5.1" -"@walletconnect/keyvaluestorage@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.0.2.tgz#92f5ca0f54c1a88a093778842ce0c874d86369c8" - integrity sha512-U/nNG+VLWoPFdwwKx0oliT4ziKQCEoQ27L5Hhw8YOFGA2Po9A9pULUYNWhDgHkrb0gYDNt//X7wABcEWWBd3FQ== - dependencies: - safe-json-utils "^1.1.1" - tslib "1.14.1" - "@walletconnect/keyvaluestorage@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@walletconnect/keyvaluestorage/-/keyvaluestorage-1.1.1.tgz#dd2caddabfbaf80f6b8993a0704d8b83115a1842" @@ -5847,7 +5840,7 @@ motion "10.16.2" qrcode "1.5.3" -"@walletconnect/modal@^2.4.3": +"@walletconnect/modal@^2.6.2": version "2.6.2" resolved "https://registry.yarnpkg.com/@walletconnect/modal/-/modal-2.6.2.tgz#4b534a836f5039eeb3268b80be7217a94dd12651" integrity sha512-eFopgKi8AjKf/0U4SemvcYw9zlLpx9njVN8sf6DAkowC2Md0gPU/UNEbH1Wwj407pEKnEds98pKWib1NN1ACoA== @@ -5882,19 +5875,19 @@ dependencies: tslib "1.14.1" -"@walletconnect/sign-client@2.10.6": - version "2.10.6" - resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.10.6.tgz#722d2c2844565e2826dce6a6d3a36c9b3ca1ea91" - integrity sha512-EvUWjaZBQu2yKnH5/5F2qzbuiIuUN9ZgrNKgvXkw5z1Dq5RJCks0S9/MFlKH/ZSGqXnLl7uAzBXtoX4sMgbCMA== +"@walletconnect/sign-client@2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.11.0.tgz#de10f976cc1b8ab04b7f7c27f6a298e4e083ab25" + integrity sha512-H2ukscibBS+6WrzQWh+WyVBqO5z4F5et12JcwobdwgHnJSlqIoZxqnUYYWNCI5rUR5UKsKWaUyto4AE9N5dw4Q== dependencies: - "@walletconnect/core" "2.10.6" + "@walletconnect/core" "2.11.0" "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "^2.0.1" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.10.6" - "@walletconnect/utils" "2.10.6" + "@walletconnect/types" "2.11.0" + "@walletconnect/utils" "2.11.0" events "^3.3.0" "@walletconnect/time@^1.0.2": @@ -5904,10 +5897,10 @@ dependencies: tslib "1.14.1" -"@walletconnect/types@2.10.6": - version "2.10.6" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.10.6.tgz#d9920ed4fd0113e0addbda8e7e73a5176a3163fd" - integrity sha512-WgHfiTG1yakmxheaBRiXhUdEmgxwrvsAdOIWaMf/spvrzVKYh6sHI3oyEEky5qj5jjiMiyQBeB57QamzCotbcQ== +"@walletconnect/types@2.11.0", "@walletconnect/types@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.11.0.tgz#474a009c56faa9ef4063b76ed84415c801dc9f1e" + integrity sha512-AB5b1lrEbCGHxqS2vqfCkIoODieH+ZAUp9rA1O2ftrhnqDJiJK983Df87JhYhECsQUBHHfALphA8ydER0q+9sw== dependencies: "@walletconnect/events" "^1.0.1" "@walletconnect/heartbeat" "1.2.1" @@ -5916,42 +5909,30 @@ "@walletconnect/logger" "^2.0.1" events "^3.3.0" -"@walletconnect/types@^2.10.1": - version "2.10.2" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.10.2.tgz#68e433a29ec2cf42d79d8b50c77bd5c1d91db721" - integrity sha512-luNV+07Wdla4STi9AejseCQY31tzWKQ5a7C3zZZaRK/di+rFaAAb7YW04OP4klE7tw/mJRGPTlekZElmHxO8kQ== - dependencies: - "@walletconnect/events" "^1.0.1" - "@walletconnect/heartbeat" "1.2.1" - "@walletconnect/jsonrpc-types" "1.0.3" - "@walletconnect/keyvaluestorage" "^1.0.2" - "@walletconnect/logger" "^2.0.1" - events "^3.3.0" - "@walletconnect/types@~1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.8.0.tgz#3f5e85b2d6b149337f727ab8a71b8471d8d9a195" integrity sha512-Cn+3I0V0vT9ghMuzh1KzZvCkiAxTq+1TR2eSqw5E5AVWfmCtECFkVZBP6uUJZ8YjwLqXheI+rnjqPy7sVM4Fyg== -"@walletconnect/universal-provider@2.10.6": - version "2.10.6" - resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.10.6.tgz#1a6c42517581f11ce275474bc70d0eb4f1044525" - integrity sha512-CEivusqqoD31BhCTKp08DnrccfGjwD9MFjZs5BNRorDteRFE8zVm9LmP6DSiNJCw82ZajGlZThggLQ/BAATfwA== +"@walletconnect/universal-provider@2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@walletconnect/universal-provider/-/universal-provider-2.11.0.tgz#89053c2360b5ce766c213ca4e33bb4ce4976b0be" + integrity sha512-zgJv8jDvIMP4Qse/D9oIRXGdfoNqonsrjPZanQ/CHNe7oXGOBiQND2IIeX+tS0H7uNA0TPvctljCLiIN9nw4eA== dependencies: "@walletconnect/jsonrpc-http-connection" "^1.0.7" "@walletconnect/jsonrpc-provider" "1.0.13" "@walletconnect/jsonrpc-types" "^1.0.2" "@walletconnect/jsonrpc-utils" "^1.0.7" "@walletconnect/logger" "^2.0.1" - "@walletconnect/sign-client" "2.10.6" - "@walletconnect/types" "2.10.6" - "@walletconnect/utils" "2.10.6" + "@walletconnect/sign-client" "2.11.0" + "@walletconnect/types" "2.11.0" + "@walletconnect/utils" "2.11.0" events "^3.3.0" -"@walletconnect/utils@2.10.6", "@walletconnect/utils@^2.10.1", "@walletconnect/utils@^2.10.6": - version "2.10.6" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.10.6.tgz#749b37d14e291e346862e7027ec7548463350226" - integrity sha512-oRsWWhN2+hi3aiDXrQEOfysz6FHQJGXLsNQPVt+WIBJplO6Szmdau9dbleD88u1iiT4GKPqE0R9FOYvvPm1H/w== +"@walletconnect/utils@2.11.0", "@walletconnect/utils@^2.10.1", "@walletconnect/utils@^2.11.0": + version "2.11.0" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.11.0.tgz#31c95151c823022077883dda61800cdea71879b7" + integrity sha512-hxkHPlTlDQILHfIKXlmzgNJau/YcSBC3XHUSuZuKZbNEw3duFT6h6pm3HT/1+j1a22IG05WDsNBuTCRkwss+BQ== dependencies: "@stablelib/chacha20poly1305" "1.0.1" "@stablelib/hkdf" "1.0.1" @@ -5961,26 +5942,26 @@ "@walletconnect/relay-api" "^1.0.9" "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.10.6" + "@walletconnect/types" "2.11.0" "@walletconnect/window-getters" "^1.0.1" "@walletconnect/window-metadata" "^1.0.1" detect-browser "5.3.0" query-string "7.1.3" uint8arrays "^3.1.0" -"@walletconnect/web3wallet@^1.9.5": - version "1.9.5" - resolved "https://registry.yarnpkg.com/@walletconnect/web3wallet/-/web3wallet-1.9.5.tgz#abb87a2fbe7ada506d1550e9c7c5ce2a3a7e8555" - integrity sha512-98ymdQ1QuNaC0krsyalzSBpMX6iYmBG2pvUMeStqnI4bas30dt4LaVqGXcklmnxZd2nFJsHLIFqN9jHAg39hhw== +"@walletconnect/web3wallet@^1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@walletconnect/web3wallet/-/web3wallet-1.10.0.tgz#5a7f181bbea213e8fb5961900a0069e58da36d18" + integrity sha512-JyaYdnBKL1hVpsE5hpjZhohpX1Ak2mwai3GYr8nI1FfK8Z3IPIh+4ImeDMnEao1cQFoItQNompp5y5C7WlI69A== dependencies: "@walletconnect/auth-client" "2.1.2" - "@walletconnect/core" "2.10.6" + "@walletconnect/core" "2.11.0" "@walletconnect/jsonrpc-provider" "1.0.13" "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "2.0.1" - "@walletconnect/sign-client" "2.10.6" - "@walletconnect/types" "2.10.6" - "@walletconnect/utils" "2.10.6" + "@walletconnect/sign-client" "2.11.0" + "@walletconnect/types" "2.11.0" + "@walletconnect/utils" "2.11.0" "@walletconnect/window-getters@^1.0.1": version "1.0.1" @@ -11028,7 +11009,7 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -isomorphic-unfetch@^3.1.0: +isomorphic-unfetch@3.1.0, isomorphic-unfetch@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz#87341d5f4f7b63843d468438128cb087b7c3e98f" integrity sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q== @@ -14146,11 +14127,6 @@ safe-event-emitter@^1.0.1: dependencies: events "^3.0.0" -safe-json-utils@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/safe-json-utils/-/safe-json-utils-1.1.1.tgz#0e883874467d95ab914c3f511096b89bfb3e63b1" - integrity sha512-SAJWGKDs50tAbiDXLf89PDwt9XYkWyANFWVzn4dTXl5QyI8t2o/bW5/OJl3lvc2WVU4MEpTo9Yz5NVFNsp+OJQ== - safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" From 4d8628366d82b90c493387d19346e42c114032a6 Mon Sep 17 00:00:00 2001 From: Manoranjith Shankar <92922580+manoranjith-shankar@users.noreply.github.com> Date: Fri, 26 Jan 2024 19:39:48 +0530 Subject: [PATCH 02/38] fix: Bug fixes and improvements for Dark Mode (#3102) * fix:dark mode color palette * fixed: safeSDKLinkFab color mismatch * fix: alertInfo & alertSucess bg * refactor: networkSelector, safeAppsSDKLink color suggestions --- src/components/common/NetworkSelector/index.tsx | 11 +++++++++++ .../safe-apps/SafeAppsSDKLink/styles.module.css | 11 ++++++++++- .../settings/PushNotifications/styles.module.css | 4 ++++ src/components/sidebar/SafeListItem/index.tsx | 2 +- src/components/theme/darkPalette.ts | 2 +- src/components/theme/safeTheme.ts | 8 ++++---- 6 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/components/common/NetworkSelector/index.tsx b/src/components/common/NetworkSelector/index.tsx index ec3545ad88..b1e76fea60 100644 --- a/src/components/common/NetworkSelector/index.tsx +++ b/src/components/common/NetworkSelector/index.tsx @@ -1,4 +1,6 @@ import ChainIndicator from '@/components/common/ChainIndicator' +import { useDarkMode } from '@/hooks/useDarkMode' +import { useTheme } from '@mui/material/styles' import { type ChainInfo } from '@safe-global/safe-gateway-typescript-sdk' import Link from 'next/link' import type { SelectChangeEvent } from '@mui/material' @@ -36,6 +38,8 @@ const isTestnet = (shortName: string) => { const NetworkSelector = (props: { onChainSelect?: () => void }): ReactElement => { const wallet = useWallet() + const isDarkMode = useDarkMode() + const theme = useTheme() const { configs } = useChains() const chainId = useChainId() const router = useRouter() @@ -112,6 +116,13 @@ const NetworkSelector = (props: { onChainSelect?: () => void }): ReactElement => '& .MuiPaper-root': { overflow: 'auto', }, + ...(isDarkMode + ? { + '& .Mui-selected, & .Mui-selected:hover': { + backgroundColor: `${theme.palette.secondary.background} !important`, + }, + } + : {}), }, }} sx={{ diff --git a/src/components/safe-apps/SafeAppsSDKLink/styles.module.css b/src/components/safe-apps/SafeAppsSDKLink/styles.module.css index 586d7eb2f3..0487e03f6a 100644 --- a/src/components/safe-apps/SafeAppsSDKLink/styles.module.css +++ b/src/components/safe-apps/SafeAppsSDKLink/styles.module.css @@ -3,7 +3,7 @@ padding: var(--space-3); padding-top: var(--space-6); background-color: var(--color-background-main); - border: 1px solid var(--color-secondary-main); + border: 1px solid #12ff80; border-radius: 0 0 12px 12px; border-top: 0; position: fixed; @@ -51,6 +51,15 @@ left: 50%; bottom: -20px; margin-left: -25px; + background-color: var(--color-secondary-main); +} + +[data-theme='dark'] .openButton { + background-color: var(--color-secondary-light); +} + +[data-theme='dark'] .openButton:hover { + background-color: var(--color-primary-dark); } .openButton svg { diff --git a/src/components/settings/PushNotifications/styles.module.css b/src/components/settings/PushNotifications/styles.module.css index 963f986c7a..32003ef104 100644 --- a/src/components/settings/PushNotifications/styles.module.css +++ b/src/components/settings/PushNotifications/styles.module.css @@ -27,3 +27,7 @@ background-color: var(--color-secondary-background); padding: var(--space-2); } + +[data-theme='dark'] .globalInfo { + background-color: var(--color-info-background); +} diff --git a/src/components/sidebar/SafeListItem/index.tsx b/src/components/sidebar/SafeListItem/index.tsx index 1c8b72e254..9fd17d3fbe 100644 --- a/src/components/sidebar/SafeListItem/index.tsx +++ b/src/components/sidebar/SafeListItem/index.tsx @@ -105,7 +105,7 @@ const SafeListItem = ({ whiteSpace: 'nowrap', overflow: 'hidden', }} - secondaryTypographyProps={{ component: 'div', color: 'primary' }} + secondaryTypographyProps={{ component: 'div', color: 'text.primary' }} primary={name || ''} secondary={ { '& .MuiAlert-icon': { color: theme.palette.error.main, }, - '&. MuiPaper-root': { + '&.MuiPaper-root': { backgroundColor: theme.palette.error.background, }, border: `1px solid ${theme.palette.error.main}`, @@ -288,7 +288,7 @@ const createSafeTheme = (mode: PaletteMode): Theme => { '& .MuiAlert-icon': { color: theme.palette.info.main, }, - '&. MuiPaper-root': { + '&.MuiPaper-root': { backgroundColor: theme.palette.info.background, }, border: `1px solid ${theme.palette.info.main}`, @@ -297,7 +297,7 @@ const createSafeTheme = (mode: PaletteMode): Theme => { '& .MuiAlert-icon': { color: theme.palette.success.main, }, - '&. MuiPaper-root': { + '&.MuiPaper-root': { backgroundColor: theme.palette.success.background, }, border: `1px solid ${theme.palette.success.main}`, @@ -306,7 +306,7 @@ const createSafeTheme = (mode: PaletteMode): Theme => { '& .MuiAlert-icon': { color: theme.palette.warning.main, }, - '&. MuiPaper-root': { + '&.MuiPaper-root': { backgroundColor: theme.palette.warning.background, }, border: `1px solid ${theme.palette.warning.main}`, From 54aa39d2d1b4dec5fa48e253c2c0cdc0f05fdacf Mon Sep 17 00:00:00 2001 From: Daniel Dimitrov Date: Fri, 26 Jan 2024 17:50:13 +0100 Subject: [PATCH 03/38] feat: better number input (#3127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the number input. - on German keyboard instead of a dot on the numpad one has a comma (,). With this change a comma will be automatically replaced with a dot. - prevent user form entering any characters [abc…] or [+-] - replaces multiple . or , with just a single . --- cypress/e2e/pages/create_tx.pages.js | 10 -------- cypress/e2e/pages/spending_limits.pages.js | 4 ++++ cypress/e2e/smoke/create_tx.cy.js | 6 ----- cypress/e2e/smoke/spending_limits.cy.js | 4 ++-- .../common/NumberField/index.test.ts | 24 +++++++++++++++++++ src/components/common/NumberField/index.tsx | 16 +++++++++---- 6 files changed, 42 insertions(+), 22 deletions(-) diff --git a/cypress/e2e/pages/create_tx.pages.js b/cypress/e2e/pages/create_tx.pages.js index a5bf3b7a01..d5e05d697a 100644 --- a/cypress/e2e/pages/create_tx.pages.js +++ b/cypress/e2e/pages/create_tx.pages.js @@ -250,21 +250,11 @@ export function verifyWrongChecksum(wronglyChecksummedAddress) { cy.contains(constants.addressBookErrrMsg.invalidChecksum).should('be.visible') } -export function verifyAmountNegativeNumber() { - setSendValue(-1) - cy.contains(constants.amountErrorMsg.negativeValue).should('be.visible') -} - export function verifyAmountLargerThanCurrentBalance() { setSendValue(9999) cy.contains(constants.amountErrorMsg.largerThanCurrentBalance).should('be.visible') } -export function verifyAmountMustBeNumber() { - setSendValue('abc') - cy.contains(constants.amountErrorMsg.randomString).should('be.visible') -} - export function verifyTooltipMessage(message) { cy.get('div[role="tooltip"]').contains(message).should('be.visible') } diff --git a/cypress/e2e/pages/spending_limits.pages.js b/cypress/e2e/pages/spending_limits.pages.js index d0728a8ede..544d1d000f 100644 --- a/cypress/e2e/pages/spending_limits.pages.js +++ b/cypress/e2e/pages/spending_limits.pages.js @@ -191,3 +191,7 @@ export function verifyNumberErrorValidation() { export function verifyCharErrorValidation() { cy.get(tokenAmountSection).find('label').should('contain', invalidCharErrorStr) } + +export function verifyNumberAmountEntered(amount) { + cy.get(tokenAmountFld).find('input').should('have.value', amount) +} diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index dd1cd821c3..d5fa633939 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -33,9 +33,7 @@ describe('[SMOKE] Create transactions tests', () => { it('[SMOKE] Verify error message for invalid amount input', () => { createtx.clickOnTokenselectorAndSelectSepoliaEth() - createtx.verifyAmountNegativeNumber() createtx.verifyAmountLargerThanCurrentBalance() - createtx.verifyAmountMustBeNumber() }) it('[SMOKE] Verify MaxAmount button', () => { @@ -44,14 +42,10 @@ describe('[SMOKE] Create transactions tests', () => { }) it('[SMOKE] Verify nonce tooltip warning messages', () => { - createtx.changeNonce(-1) - createtx.verifyTooltipMessage(constants.nonceTooltipMsg.lowerThanCurrent + currentNonce.toString()) createtx.changeNonce(currentNonce + 50) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.higherThanRecommended) createtx.changeNonce(currentNonce + 150) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.muchHigherThanRecommended) - createtx.changeNonce('abc') - createtx.verifyTooltipMessage(constants.nonceTooltipMsg.mustBeNumber) }) it('[SMOKE] Verify advance parameters gas limit input', () => { diff --git a/cypress/e2e/smoke/spending_limits.cy.js b/cypress/e2e/smoke/spending_limits.cy.js index e4c8942890..e471bdc9c3 100644 --- a/cypress/e2e/smoke/spending_limits.cy.js +++ b/cypress/e2e/smoke/spending_limits.cy.js @@ -36,12 +36,12 @@ describe('[SMOKE] Spending limits tests', () => { it('Verify Amount input cannot be a negative number', () => { spendinglimit.enterSpendingLimitAmount('-1') - spendinglimit.verifyNumberErrorValidation() + spendinglimit.verifyNumberAmountEntered('1') }) it('Verify Amount input cannot be characters', () => { spendinglimit.enterSpendingLimitAmount('abc') - spendinglimit.verifyCharErrorValidation() + spendinglimit.verifyNumberAmountEntered('') }) it('Verify any positive number can be set in the amount input', () => { diff --git a/src/components/common/NumberField/index.test.ts b/src/components/common/NumberField/index.test.ts index c30a9f4e0a..d707140695 100644 --- a/src/components/common/NumberField/index.test.ts +++ b/src/components/common/NumberField/index.test.ts @@ -10,4 +10,28 @@ describe('NumberField', () => { expect(_formatNumber('000123')).toBe('123') expect(_formatNumber('0000.001')).toBe('0.001') }) + + it('should replace , with .', () => { + expect(_formatNumber('123,456')).toBe('123.456') + expect(_formatNumber('00,3')).toBe('0.3') + expect(_formatNumber('123,456.789')).toBe('123.456789') + }) + + it('should remove the leading .', () => { + expect(_formatNumber('.123')).toBe('0.123') + expect(_formatNumber('.123456')).toBe('0.123456') + expect(_formatNumber(',123')).toBe('0.123') + }) + + it('should not be possible to enter multiple . or ,', () => { + expect(_formatNumber('123.456.789')).toBe('123.456789') + expect(_formatNumber('123.456...789')).toBe('123.456789') + expect(_formatNumber('123,456,789')).toBe('123.456789') + expect(_formatNumber('123,456,,,,789')).toBe('123.456789') + }) + it('should not be possible to enter characters', () => { + expect(_formatNumber('abc')).toBe('') + expect(_formatNumber('abc123')).toBe('123') + expect(_formatNumber('123abc')).toBe('123') + }) }) diff --git a/src/components/common/NumberField/index.tsx b/src/components/common/NumberField/index.tsx index 523812f298..f394fd39d2 100644 --- a/src/components/common/NumberField/index.tsx +++ b/src/components/common/NumberField/index.tsx @@ -10,11 +10,19 @@ export const _formatNumber = (value: string) => { return value } - // Remove leading zeros from the string - value = value.replace(/^0+/, '') + // Replace commas with dots (used as decimal separator) + value = value.replace(/,/g, '.') - if (value === '') { - return '0' + let index = 0 + // replace all dots except the first one + value = value.replace(/\./g, (item) => (!index++ ? item : '')) + + // Remove all characters except numbers and dots + value = value.replace(/[^0-9.]/g, '') + + if (value.length > 1) { + // Remove leading zeros from the string + value = value.replace(/^0+/, '') } // If the string starts with a decimal point, add a leading zero From 4cd8a3dec813bc76c02cf68264246d31be7d416e Mon Sep 17 00:00:00 2001 From: Ryan Waldon Date: Mon, 29 Jan 2024 20:38:50 +1100 Subject: [PATCH 04/38] fix: TxType overlap/overflow (#3161) --- src/components/batch/BatchSidebar/BatchTxItem.tsx | 2 +- src/components/batch/BatchSidebar/styles.module.css | 7 +++++++ .../TxListItem/ExpandableTransactionItem.tsx | 13 ++++++++++++- .../transactions/TxSummary/styles.module.css | 4 ++++ src/components/transactions/TxType/index.tsx | 2 +- .../transactions/TxType/styles.module.css | 7 +++++++ 6 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/batch/BatchSidebar/BatchTxItem.tsx b/src/components/batch/BatchSidebar/BatchTxItem.tsx index f614bfcf1d..e9161b4def 100644 --- a/src/components/batch/BatchSidebar/BatchTxItem.tsx +++ b/src/components/batch/BatchSidebar/BatchTxItem.tsx @@ -62,7 +62,7 @@ const BatchTxItem = ({ } disabled={dragging} className={css.accordion}> - + {draggable && ( - } sx={{ justifyContent: 'flex-start', overflowX: 'auto' }}> + } + sx={{ + justifyContent: 'flex-start', + overflowX: 'auto', + ['.MuiAccordionSummary-content, .MuiAccordionSummary-content.Mui-expanded']: { + overflow: 'hidden', + margin: 0, + padding: '12px 0', + }, + }} + > diff --git a/src/components/transactions/TxSummary/styles.module.css b/src/components/transactions/TxSummary/styles.module.css index c1808447e1..76126bdce2 100644 --- a/src/components/transactions/TxSummary/styles.module.css +++ b/src/components/transactions/TxSummary/styles.module.css @@ -17,6 +17,10 @@ grid-template-areas: 'nonce type info date confirmations status'; } +.gridContainer > * { + max-width: 100%; +} + .gridContainer.history { grid-template-columns: var(--grid-nonce) var(--grid-type) var(--grid-info) var(--grid-date) var(--grid-status); grid-template-areas: 'nonce type info date status'; diff --git a/src/components/transactions/TxType/index.tsx b/src/components/transactions/TxType/index.tsx index 2fbf4c053d..e246bc1891 100644 --- a/src/components/transactions/TxType/index.tsx +++ b/src/components/transactions/TxType/index.tsx @@ -20,7 +20,7 @@ const TxType = ({ tx }: TxTypeProps) => { height={16} fallback="/images/transactions/custom.svg" /> - {type.text} + {type.text} ) } diff --git a/src/components/transactions/TxType/styles.module.css b/src/components/transactions/TxType/styles.module.css index e4e9a2cf4e..f15b480b91 100644 --- a/src/components/transactions/TxType/styles.module.css +++ b/src/components/transactions/TxType/styles.module.css @@ -4,4 +4,11 @@ flex-wrap: nowrap; gap: var(--space-1); color: var(--color-text-primary); + overflow: hidden; +} + +.txTypeText { + overflow: hidden; + text-overflow: ellipsis; + text-wrap: nowrap; } From d7e5365a978e7813b81a7ca748aa9678a421a766 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Mon, 29 Jan 2024 10:39:51 +0100 Subject: [PATCH 05/38] Fix: a more accurate WC app detection (#3155) --- src/pages/apps/open.tsx | 6 ++---- src/services/analytics/__tests__/tx-tracking.test.ts | 2 +- src/utils/gateway.ts | 7 +++++-- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/pages/apps/open.tsx b/src/pages/apps/open.tsx index cd2311c323..67a0985709 100644 --- a/src/pages/apps/open.tsx +++ b/src/pages/apps/open.tsx @@ -18,9 +18,7 @@ import { getOrigin } from '@/components/safe-apps/utils' import { useHasFeature } from '@/hooks/useChains' import { FEATURES } from '@/utils/chains' import { openWalletConnect } from '@/features/walletconnect/components' - -// TODO: Remove this once we properly deprecate the WC app -const WC_SAFE_APP = /wallet-connect/ +import { isWalletConnectSafeApp } from '@/utils/gateway' const SafeApps: NextPage = () => { const chainId = useChainId() @@ -60,7 +58,7 @@ const SafeApps: NextPage = () => { // appUrl is required to be present if (!isSafeAppsEnabled || !appUrl || !router.isReady) return null - if (isWalletConnectEnabled && WC_SAFE_APP.test(appUrl)) { + if (isWalletConnectEnabled && isWalletConnectSafeApp(appUrl)) { openWalletConnect() goToList() return null diff --git a/src/services/analytics/__tests__/tx-tracking.test.ts b/src/services/analytics/__tests__/tx-tracking.test.ts index 470108e226..7c0dac6150 100644 --- a/src/services/analytics/__tests__/tx-tracking.test.ts +++ b/src/services/analytics/__tests__/tx-tracking.test.ts @@ -144,7 +144,7 @@ describe('getTransactionTrackingType', () => { type: TransactionInfoType.CUSTOM, }, safeAppInfo: { - url: 'https://safe-apps.safe.global/wallet-connect', + url: 'https://apps-portal.safe.global/wallet-connect', }, } as unknown) diff --git a/src/utils/gateway.ts b/src/utils/gateway.ts index 1151bc0164..985960a211 100644 --- a/src/utils/gateway.ts +++ b/src/utils/gateway.ts @@ -28,6 +28,9 @@ export const getExplorerLink = ( } export const isWalletConnectSafeApp = (url: string): boolean => { - const WALLET_CONNECT = /wallet-connect/ - return WALLET_CONNECT.test(url) + const WC_APP_URLS = [ + 'https://apps-portal.safe.global/wallet-connect', + 'https://safe-apps.dev.5afe.dev/wallet-connect', + ] + return WC_APP_URLS.includes(url) } From bb1b04ca2b99da5b72c522902422d00a27c45802 Mon Sep 17 00:00:00 2001 From: francovenica Date: Mon, 29 Jan 2024 16:35:55 +0000 Subject: [PATCH 06/38] Tests: E2e - Remove owner - add signing action (#3076) * refactor: add signig action * change test description --- cypress/e2e/pages/create_tx.pages.js | 6 ++++++ cypress/e2e/pages/owners.pages.js | 17 +++++++++++++++++ cypress/e2e/smoke/remove_owner.cy.js | 15 +++++++++++---- cypress/support/constants.js | 2 ++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/pages/create_tx.pages.js b/cypress/e2e/pages/create_tx.pages.js index d5e05d697a..426b45d86f 100644 --- a/cypress/e2e/pages/create_tx.pages.js +++ b/cypress/e2e/pages/create_tx.pages.js @@ -402,3 +402,9 @@ export function clickOnExpandAllBtn() { export function clickOnCollapseAllBtn() { cy.contains(collapseAllBtnStr).click() } + +export function verifyTxDestinationAddress(receivedAddress) { + cy.get(receivedAddress).then((address) => { + cy.contains(address).should('exist') + }) +} diff --git a/cypress/e2e/pages/owners.pages.js b/cypress/e2e/pages/owners.pages.js index 355810f5aa..f545565e01 100644 --- a/cypress/e2e/pages/owners.pages.js +++ b/cypress/e2e/pages/owners.pages.js @@ -24,6 +24,8 @@ const singleOwnerNameInput = 'input[name="name"]' const finishTransactionBtn = '[data-testid="finish-transaction-btn"]' const addOwnerBtn = '[data-testid="add-owner-btn"]' const addOwnerNextBtn = '[data-testid="add-owner-next-btn"]' +const modalHeader = '[data-testid="modal-header"]' +const addressToBeRemoved = '[aria-label="Copy to clipboard"] span' const disconnectBtnStr = 'Disconnect' const notConnectedStatus = 'Connect' @@ -109,6 +111,21 @@ export function openRemoveOwnerWindow(btn) { cy.get('div').contains(removeOwnerStr).should('exist') } +export function getAddressToBeRemoved() { + let removedAddress + cy.get(modalHeader) + .next() + .should('exist') + .find(addressToBeRemoved) + .first() + .invoke('text') + .then((value) => { + removedAddress = value + cy.wrap(removedAddress).as('removedAddress') + }) + return removedAddress +} + export function openReplaceOwnerWindow() { cy.get(replaceOwnerBtn).click({ force: true }) cy.get(newOwnerName).should('be.visible') diff --git a/cypress/e2e/smoke/remove_owner.cy.js b/cypress/e2e/smoke/remove_owner.cy.js index d6127941ce..e188e98872 100644 --- a/cypress/e2e/smoke/remove_owner.cy.js +++ b/cypress/e2e/smoke/remove_owner.cy.js @@ -2,21 +2,28 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' import * as owner from '../pages/owners.pages' import * as createwallet from '../pages/create_wallet.pages' +import * as createTx from '../pages/create_tx.pages.js' describe('[SMOKE] Remove Owners tests', () => { beforeEach(() => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_1) + cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_11) cy.clearLocalStorage() main.acceptCookies() cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) }) - // TODO: Add Sign action. Check there is no error before sign action on UI when nonce not loaded - it('[SMOKE] Verify owner deletion confirmation is displayed', () => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_3) + it('[SMOKE] Verify owner deletion transaction has been created', () => { owner.waitForConnectionStatus() owner.openRemoveOwnerWindow(1) + cy.wait(3000) createwallet.clickOnNextBtn() + //This method creates the @removedAddress alias + owner.getAddressToBeRemoved() owner.verifyOwnerDeletionWindowDisplayed() + createTx.clickOnSignTransactionBtn() + createTx.waitForProposeRequest() + createTx.clickViewTransaction() + createTx.clickOnTransactionItemByName('removeOwner') + createTx.verifyTxDestinationAddress('@removedAddress') }) }) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index f7f73b7e74..032ea59783 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -18,6 +18,8 @@ export const SEPOLIA_TEST_SAFE_8 = 'sep:0x5912f6616c84024cD1aff0D5b55bb36F5180fF // SAFE 9 & 10 are used for safe apps tests export const SEPOLIA_TEST_SAFE_9 = 'sep:0xD1571E8Cc4438aFef2836DD9a0E5D09fb63EDE9a' export const SEPOLIA_TEST_SAFE_10 = 'sep:0x4DD4cB2299E491E1B469245DB589ccB2B16d7bde' +// SAFE 11 is used for remove owner tests +export const SEPOLIA_TEST_SAFE_11 = 'sep:0x81034C61a318649F7aD43f9e8C1051427e326443' // SAFE 12 is used for spending limits export const SEPOLIA_TEST_SAFE_12 = 'sep:0x9190cc22D592dDcf396Fa616ce84a9978fD96Fc9' export const SEPOLIA_CONTRACT_SHORT = '0x11AB...34aF' From fa16a20ddd8385e5ca9694caa604fb672fee851f Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Mon, 29 Jan 2024 19:12:23 +0100 Subject: [PATCH 07/38] Refactor: animated sidebar only on a Safe App page (#3131) --- cypress/e2e/safe-apps/apps_list.cy.js | 4 ++- cypress/e2e/safe-apps/constants.js | 1 - cypress/e2e/safe-apps/info_modal.cy.js | 4 ++- .../e2e/safe-apps/permissions_settings.cy.js | 5 +++- cypress/e2e/safe-apps/preview_drawer.cy.js | 4 ++- .../common/PageLayout/SideDrawer.tsx | 16 ++++-------- src/components/common/PageLayout/index.tsx | 26 +++++-------------- .../components/RecoveryModal/index.tsx | 7 ++++- src/hooks/__tests__/useChainId.test.ts | 4 +-- src/hooks/useChainId.ts | 2 +- src/hooks/useIsSidebarRoute.ts | 21 ++++++++++++--- 11 files changed, 52 insertions(+), 42 deletions(-) diff --git a/cypress/e2e/safe-apps/apps_list.cy.js b/cypress/e2e/safe-apps/apps_list.cy.js index 42d25d46b6..5d747cbc4e 100644 --- a/cypress/e2e/safe-apps/apps_list.cy.js +++ b/cypress/e2e/safe-apps/apps_list.cy.js @@ -8,7 +8,9 @@ const myCustomAppDescrAdded = 'Cypress Test App Description' describe('Safe Apps list tests', () => { beforeEach(() => { cy.clearLocalStorage() - cy.visit(constants.SEPOLIA_TEST_SAFE_4 + constants.appsUrl, { failOnStatusCode: false }) + cy.visit(`${constants.appsUrl}?safe=${constants.SEPOLIA_TEST_SAFE_4}`, { + failOnStatusCode: false, + }) main.acceptCookies() }) diff --git a/cypress/e2e/safe-apps/constants.js b/cypress/e2e/safe-apps/constants.js index 92f6e5be9e..6962ad04ee 100644 --- a/cypress/e2e/safe-apps/constants.js +++ b/cypress/e2e/safe-apps/constants.js @@ -1,6 +1,5 @@ import { LS_NAMESPACE } from '../../../src/config/constants' -export const TEST_SAFE = 'gor:0xE96C43C54B08eC528e9e815fC3D02Ea94A320505' export const BROWSER_PERMISSIONS_KEY = `${LS_NAMESPACE}SafeApps__browserPermissions` export const SAFE_PERMISSIONS_KEY = `${LS_NAMESPACE}SafeApps__safePermissions` export const INFO_MODAL_KEY = `${LS_NAMESPACE}SafeApps__infoModal` diff --git a/cypress/e2e/safe-apps/info_modal.cy.js b/cypress/e2e/safe-apps/info_modal.cy.js index 7aeab75e2e..db7d25c729 100644 --- a/cypress/e2e/safe-apps/info_modal.cy.js +++ b/cypress/e2e/safe-apps/info_modal.cy.js @@ -5,7 +5,9 @@ import * as safeapps from '../pages/safeapps.pages' describe('Info modal tests', () => { beforeEach(() => { cy.clearLocalStorage() - cy.visit(constants.SEPOLIA_TEST_SAFE_5 + constants.appsUrl, { failOnStatusCode: false }) + cy.visit(`${constants.appsUrl}?safe=${constants.SEPOLIA_TEST_SAFE_5}`, { + failOnStatusCode: false, + }) main.acceptCookies() }) diff --git a/cypress/e2e/safe-apps/permissions_settings.cy.js b/cypress/e2e/safe-apps/permissions_settings.cy.js index 41484f9638..a530f0c182 100644 --- a/cypress/e2e/safe-apps/permissions_settings.cy.js +++ b/cypress/e2e/safe-apps/permissions_settings.cy.js @@ -45,7 +45,10 @@ describe('Permissions settings tests', () => { ) }) - cy.visit(constants.SEPOLIA_TEST_SAFE_5 + constants.appSettingsUrl, { failOnStatusCode: false }) + cy.visit(`${constants.appSettingsUrl}?safe=${constants.SEPOLIA_TEST_SAFE_5}`, { + failOnStatusCode: false, + }) + main.acceptCookies() }) diff --git a/cypress/e2e/safe-apps/preview_drawer.cy.js b/cypress/e2e/safe-apps/preview_drawer.cy.js index d9d3b35cf4..a3c7e966f8 100644 --- a/cypress/e2e/safe-apps/preview_drawer.cy.js +++ b/cypress/e2e/safe-apps/preview_drawer.cy.js @@ -5,7 +5,9 @@ import * as safeapps from '../pages/safeapps.pages' describe('Preview drawer tests', () => { beforeEach(() => { cy.clearLocalStorage() - cy.visit(`/${constants.SEPOLIA_TEST_SAFE_5}/apps`, { failOnStatusCode: false }) + cy.visit(`${constants.appsUrl}?safe=${constants.SEPOLIA_TEST_SAFE_5}`, { + failOnStatusCode: false, + }) main.acceptCookies() }) diff --git a/src/components/common/PageLayout/SideDrawer.tsx b/src/components/common/PageLayout/SideDrawer.tsx index e151d80418..c4e954b96b 100644 --- a/src/components/common/PageLayout/SideDrawer.tsx +++ b/src/components/common/PageLayout/SideDrawer.tsx @@ -1,39 +1,33 @@ import { useEffect, type ReactElement } from 'react' import { IconButton, Drawer, useMediaQuery } from '@mui/material' -import type { ParsedUrlQuery } from 'querystring' import { useTheme } from '@mui/material/styles' import DoubleArrowRightIcon from '@mui/icons-material/KeyboardDoubleArrowRightRounded' import DoubleArrowLeftIcon from '@mui/icons-material/KeyboardDoubleArrowLeftRounded' -import { useRouter } from 'next/router' import classnames from 'classnames' import Sidebar from '@/components/sidebar/Sidebar' import css from './styles.module.css' -import { AppRoutes } from '@/config/routes' import useDebounce from '@/hooks/useDebounce' +import { useIsSidebarRoute } from '@/hooks/useIsSidebarRoute' type SideDrawerProps = { isOpen: boolean onToggle: (isOpen: boolean) => void } -const isSafeAppFrameRoute = (pathname: string, query: ParsedUrlQuery): boolean => { - return pathname === AppRoutes.apps.open && !!query.appUrl -} - const SideDrawer = ({ isOpen, onToggle }: SideDrawerProps): ReactElement => { - const { pathname, query } = useRouter() const { breakpoints } = useTheme() const isSmallScreen = useMediaQuery(breakpoints.down('md')) - const showSidebarToggle = isSafeAppFrameRoute(pathname, query) && !isSmallScreen + const [, isSafeAppRoute] = useIsSidebarRoute() + const showSidebarToggle = isSafeAppRoute && !isSmallScreen // Keep the sidebar hidden on small screens via CSS until we collapse it via JS. // With a small delay to avoid flickering. const smDrawerHidden = useDebounce(!isSmallScreen, 300) useEffect(() => { - const closeSidebar = isSmallScreen || isSafeAppFrameRoute(pathname, query) + const closeSidebar = isSmallScreen || isSafeAppRoute onToggle(!closeSidebar) - }, [isSmallScreen, onToggle, pathname, query]) + }, [isSmallScreen, isSafeAppRoute, onToggle]) return ( <> diff --git a/src/components/common/PageLayout/index.tsx b/src/components/common/PageLayout/index.tsx index 9ca6b90239..e0f00b8e4f 100644 --- a/src/components/common/PageLayout/index.tsx +++ b/src/components/common/PageLayout/index.tsx @@ -7,43 +7,31 @@ import SafeLoadingError from '../SafeLoadingError' import Footer from '../Footer' import SideDrawer from './SideDrawer' import { useIsSidebarRoute } from '@/hooks/useIsSidebarRoute' -import useDebounce from '@/hooks/useDebounce' -import { useRouter } from 'next/router' import { TxModalContext } from '@/components/tx-flow' import BatchSidebar from '@/components/batch/BatchSidebar' const PageLayout = ({ pathname, children }: { pathname: string; children: ReactElement }): ReactElement => { - const router = useRouter() - const isSidebarRoute = useIsSidebarRoute() - const [noSidebar, setNoSidebar] = useState(isSidebarRoute) + const [isSidebarRoute, isAnimated] = useIsSidebarRoute(pathname) const [isSidebarOpen, setSidebarOpen] = useState(true) const [isBatchOpen, setBatchOpen] = useState(false) - const hideSidebar = noSidebar || !isSidebarOpen const { setFullWidth } = useContext(TxModalContext) - let isAnimated = useDebounce(!noSidebar, 300) - if (!isSidebarRoute) isAnimated = false useEffect(() => { - const noSafeAddress = router.isReady && !router.query.safe - setNoSidebar(!isSidebarRoute || noSafeAddress) - }, [isSidebarRoute, pathname, router]) - - useEffect(() => { - setFullWidth(hideSidebar) - }, [hideSidebar, setFullWidth]) + setFullWidth(!isSidebarOpen) + }, [isSidebarOpen, setFullWidth]) return ( <>
-
+
- {!noSidebar && } + {isSidebarRoute && }
diff --git a/src/features/recovery/components/RecoveryModal/index.tsx b/src/features/recovery/components/RecoveryModal/index.tsx index 1ab729deea..85429e69e4 100644 --- a/src/features/recovery/components/RecoveryModal/index.tsx +++ b/src/features/recovery/components/RecoveryModal/index.tsx @@ -100,12 +100,17 @@ export function _RecoveryModal({ ) } +const useSidebar = () => { + const [isSidebarRoute] = useIsSidebarRoute() + return isSidebarRoute +} + export const RecoveryModal = madProps(_RecoveryModal, { isOwner: useIsSafeOwner, isRecoverer: useIsRecoverer, queue: useRecoveryQueue, wallet: useWallet, - isSidebarRoute: useIsSidebarRoute, + isSidebarRoute: useSidebar, }) export function _useDidDismissProposal() { diff --git a/src/hooks/__tests__/useChainId.test.ts b/src/hooks/__tests__/useChainId.test.ts index d964324471..521e871432 100644 --- a/src/hooks/__tests__/useChainId.test.ts +++ b/src/hooks/__tests__/useChainId.test.ts @@ -74,7 +74,7 @@ describe('useChainId hook', () => { it('should return the default chainId if no query params', () => { const { result } = renderHook(() => useChainId()) - expect(result.current).toBe('5') + expect(result.current).toBe('11155111') }) it('should return the chainId based on the chain query', () => { @@ -128,7 +128,7 @@ describe('useChainId hook', () => { })) const { result } = renderHook(() => useChainId()) - expect(result.current).toBe('5') + expect(result.current).toBe('11155111') }) it('should return the last used chain id if no wallet is connected and there is no chain in the URL', () => { diff --git a/src/hooks/useChainId.ts b/src/hooks/useChainId.ts index 8be8d525ff..64ad9ec8c6 100644 --- a/src/hooks/useChainId.ts +++ b/src/hooks/useChainId.ts @@ -9,7 +9,7 @@ import { prefixedAddressRe } from '@/utils/url' import useWallet from './wallets/useWallet' import useChains from './useChains' -const defaultChainId = IS_PRODUCTION ? chains.eth : chains.gor +const defaultChainId = IS_PRODUCTION ? chains.eth : chains.sep // Use the location object directly because Next.js's router.query is available only on mount const getLocationQuery = (): ParsedUrlQuery => { diff --git a/src/hooks/useIsSidebarRoute.ts b/src/hooks/useIsSidebarRoute.ts index 5f8f3d7c91..287fc689cd 100644 --- a/src/hooks/useIsSidebarRoute.ts +++ b/src/hooks/useIsSidebarRoute.ts @@ -1,11 +1,14 @@ import { AppRoutes } from '@/config/routes' import { usePathname } from 'next/navigation' +import { useRouter } from 'next/router' const NO_SIDEBAR_ROUTES = [ AppRoutes.share.safeApp, AppRoutes.newSafe.create, AppRoutes.newSafe.load, AppRoutes.index, + AppRoutes.welcome.index, + AppRoutes.welcome.socialLogin, AppRoutes.imprint, AppRoutes.privacy, AppRoutes.cookie, @@ -13,7 +16,19 @@ const NO_SIDEBAR_ROUTES = [ AppRoutes.licenses, ] -export function useIsSidebarRoute(): boolean { - const pathname = usePathname() - return !NO_SIDEBAR_ROUTES.includes(pathname) +const TOGGLE_SIDEBAR_ROUTES = [AppRoutes.apps.open] + +/** + * Returns a boolean tuple indicating if the current route should display the sidebar and if the sidebar can be toggled + * @param pathname Optional server-side pathname to check against + * @returns A tuple with the first value indicating if the sidebar should be displayed and the second value indicating if the sidebar can be toggled + */ +export function useIsSidebarRoute(pathname?: string): [boolean, boolean] { + const clientPathname = usePathname() + const route = pathname || clientPathname + const noSidebar = NO_SIDEBAR_ROUTES.includes(route) + const toggledSidebar = TOGGLE_SIDEBAR_ROUTES.includes(route) + const router = useRouter() + const hasSafe = !router.isReady || !!router.query.safe + return [!noSidebar && hasSafe, toggledSidebar] } From 8460f33583ca43690ab75051cb1612c9ff8590f4 Mon Sep 17 00:00:00 2001 From: Moganesan <43741383+Moganesan@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:15:30 +0530 Subject: [PATCH 08/38] Docs: update readme with a generate types command (#3167) --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 0997812865..98491e0c22 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,11 @@ Install the dependencies: yarn ``` +Generate types: +```bash +yarn postinstall +``` + Run the development server: ```bash From d12c70cbad098acbf2adf1c10701febe04794b7f Mon Sep 17 00:00:00 2001 From: Daniel Dimitrov Date: Tue, 30 Jan 2024 10:08:10 +0100 Subject: [PATCH 09/38] =?UTF-8?q?docs:=20improve=20the=20=E2=80=9Cpreparin?= =?UTF-8?q?g=20a=20release=E2=80=9D=20steps=20(#3170)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/release-procedure.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/release-procedure.md b/docs/release-procedure.md index 368e979145..ee1e755ed6 100644 --- a/docs/release-procedure.md +++ b/docs/release-procedure.md @@ -20,10 +20,12 @@ We prepare at least one release every sprint. Sprints are two weeks long. * Bump the version in the `package.json` * Create a PR with the list of changes -💡 To generate a quick changelog: -``` -git log origin/main..origin/dev --pretty=format:'* %s' -``` + > 💡 To generate a quick changelog: + > ```bash + > git log origin/main..origin/dev --pretty=format:'* %s' + > ``` +* Add the PR to the Project `Web Squad` and set the status to `Ready for QA` + ### QA * The QA team do regression testing on this branch @@ -47,7 +49,7 @@ git pull origin release/3.15.0 git push ``` -A deploy workflow will kick in and do the following things: +A deployment workflow will kick in and do the following things: * Deploy the code to staging * Create a new git tag from the version in package.json From 7d8574dc930ae84aee2e6e88efbd8e789906c9d5 Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Tue, 30 Jan 2024 13:46:19 +0100 Subject: [PATCH 10/38] Tests: add extra tests to address bug fix and restore a deleted test (#3173) * tests: add extra tests to address bug fix and restore a deleted test * tests: skip drain account tests - wip --- cypress/e2e/pages/create_tx.pages.js | 2 +- cypress/e2e/pages/spending_limits.pages.js | 13 +++++++++++-- cypress/e2e/regression/spending_limits.cy.js | 13 +++++++++++++ cypress/e2e/safe-apps/drain_account.spec.cy.js | 18 +++++++++--------- cypress/e2e/smoke/create_tx.cy.js | 4 +++- cypress/support/constants.js | 6 ++++++ 6 files changed, 43 insertions(+), 13 deletions(-) diff --git a/cypress/e2e/pages/create_tx.pages.js b/cypress/e2e/pages/create_tx.pages.js index 426b45d86f..84888d03ce 100644 --- a/cypress/e2e/pages/create_tx.pages.js +++ b/cypress/e2e/pages/create_tx.pages.js @@ -313,7 +313,7 @@ export function verifyNativeTokenTransfer() { } export function changeNonce(value) { - cy.get(nonceInput).clear().type(value, { force: true }).blur() + cy.get(nonceInput).clear().type(value, { force: true }) } export function verifyConfirmTransactionData() { diff --git a/cypress/e2e/pages/spending_limits.pages.js b/cypress/e2e/pages/spending_limits.pages.js index 544d1d000f..8cf1c1aca8 100644 --- a/cypress/e2e/pages/spending_limits.pages.js +++ b/cypress/e2e/pages/spending_limits.pages.js @@ -2,6 +2,7 @@ import * as constants from '../../support/constants' import * as main from './main.page' import * as addressBook from '../pages/address_book.page' import { invalidAddressFormatErrorMsg } from '../pages/load_safe.pages' +import * as ls from '../../support/localstorage_data.js' import 'cypress-file-upload' export const spendingLimitsSection = '[data-testid="spending-limit-section"]' @@ -31,7 +32,10 @@ const oldTokenAmount = '[data-testid="old-token-amount"]' const oldResetTime = '[data-testid="old-reset-time"]' const slimitReplacementWarning = '[data-testid="limit-replacement-warning"]' const addressItem = '[data-testid="address-item"]' -import * as ls from '../../support/localstorage_data.js' + +const actionSectionItem = () => { + return cy.get('[data-testid="CodeIcon"]').parent() +} export const timePeriodOptions = { oneTime: 'One time', @@ -43,7 +47,7 @@ export const timePeriodOptions = { const getBeneficiaryInput = () => cy.get(beneficiarySection).find('input').should('be.enabled') const automationOwner = ls.addressBookData.sepoliaAddress2[11155111]['0xC16Db0251654C0a72E91B190d81eAD367d2C6fED'] -const expectedSpendOptions = ['0 of 0.17 ETH', '0 of 0.05 ETH', '0 of 0.01 ETH'] +const expectedSpendOptions = ['0 of 0.17 ETH', '0.00001 of 0.05 ETH', '0 of 0.01 ETH'] const expectedResetOptions = new Array(3).fill('One-time') const newTransactionStr = 'New transaction' @@ -59,6 +63,11 @@ export function selectRecipient(recipient) { export function verifyOldValuesAreDisplayed() { main.verifyElementsIsVisible([oldTokenAmount, oldResetTime, slimitReplacementWarning]) } + +export function verifyActionNamesAreDisplayed(names) { + main.verifyValuesExist(actionSectionItem, names) +} + export function verifySpendingLimitBtnIsDisabled() { cy.get(newSpendingLimitBtn).should('be.disabled') } diff --git a/cypress/e2e/regression/spending_limits.cy.js b/cypress/e2e/regression/spending_limits.cy.js index 77f5c172b0..be906829ab 100644 --- a/cypress/e2e/regression/spending_limits.cy.js +++ b/cypress/e2e/regression/spending_limits.cy.js @@ -78,6 +78,19 @@ describe('Spending limits tests', () => { spendinglimit.verifyOldValuesAreDisplayed() }) + it('Verify that when editing spending limit for owner who used some of it, relevant actions are displayed', () => { + spendinglimit.clickOnNewSpendingLimitBtn() + spendinglimit.enterBeneficiaryAddress(constants.SPENDING_LIMIT_ADDRESS_2) + spendinglimit.enterSpendingLimitAmount(newTokenAmount) + spendinglimit.clickOnTimePeriodDropdown() + spendinglimit.selectTimePeriod(spendinglimit.timePeriodOptions.oneTime) + tx.clickOnNextBtn() + spendinglimit.verifyActionNamesAreDisplayed([ + constants.TXActionNames.resetAllowance, + constants.TXActionNames.setAllowance, + ]) + }) + it('Verify that when multiple assets are available, they are displayed in token dropdown', () => { main.addToLocalStorage(constants.localStorageKeys.SAFE_v2__settings, ls.safeSettings.slimitSettings).then(() => { main diff --git a/cypress/e2e/safe-apps/drain_account.spec.cy.js b/cypress/e2e/safe-apps/drain_account.spec.cy.js index 773559d748..3c2f2a14fa 100644 --- a/cypress/e2e/safe-apps/drain_account.spec.cy.js +++ b/cypress/e2e/safe-apps/drain_account.spec.cy.js @@ -6,10 +6,10 @@ import * as safeapps from '../pages/safeapps.pages' 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)}` + const visitUrl = `/apps/open?safe=${constants.SEPOLIA_TEST_SAFE_5}&appUrl=${encodeURIComponent(appUrl)}` beforeEach(() => { - cy.intercept(`**//v1/chains/5/safes/${constants.GOERLI_SAFE_APPS_SAFE.substring(4)}/balances/**`, { + cy.intercept(`**//v1/chains/5/safes/${constants.SEPOLIA_TEST_SAFE_5.substring(4)}/balances/**`, { fixture: 'balances.json', }) @@ -19,7 +19,7 @@ describe('Drain Account tests', { defaultCommandTimeout: 12000 }, () => { safeapps.clickOnContinueBtn() }) - it('Verify drain can be created', () => { + it.skip('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 tests', { defaultCommandTimeout: 12000 }, () => { cy.findByRole('button', { name: safeapps.testNativeTransfer2 }) }) - it('Verify partial drain can be created', () => { + it.skip('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 tests', { defaultCommandTimeout: 12000 }, () => { cy.findByRole('button', { name: safeapps.testNativeTransfer1 }) }) - it('Verify a drain can be created when a ENS is specified', () => { + it.skip('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() @@ -61,14 +61,14 @@ describe('Drain Account tests', { defaultCommandTimeout: 12000 }, () => { }) }) - it('Verify a drain cannot be created with no recipient selected', () => { + it.skip('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', () => { + it.skip('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 tests', { defaultCommandTimeout: 12000 }, () => { }) }) - it('Verify a drain cannot be created when no assets are selected', () => { + it.skip('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 tests', { defaultCommandTimeout: 12000 }, () => { }) }) - it('Verify a drain cannot be created when no assets and recipient are selected', () => { + it.skip('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/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index d5fa633939..81c1aa0a74 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -3,7 +3,7 @@ import * as main from '../../e2e/pages/main.page' import * as createtx from '../../e2e/pages/create_tx.pages' const sendValue = 0.00002 -const currentNonce = 0 +const currentNonce = 1 function happyPathToStepTwo() { createtx.typeRecipientAddress(constants.EOA) @@ -42,6 +42,8 @@ describe('[SMOKE] Create transactions tests', () => { }) it('[SMOKE] Verify nonce tooltip warning messages', () => { + createtx.changeNonce(0) + createtx.verifyTooltipMessage(constants.nonceTooltipMsg.lowerThanCurrent + currentNonce.toString()) createtx.changeNonce(currentNonce + 50) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.higherThanRecommended) createtx.changeNonce(currentNonce + 150) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index 032ea59783..51212cc766 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -32,6 +32,7 @@ export const SAFE_APP_ADDRESS = '0x11AB70A4564C62F567B92868Cb5e69b50c5434aF' export const SAFE_APP_ADDRESS_2 = '0x49d4450977E2c95362C13D3a31a09311E0Ea26A6' export const SAFE_APP_ADDRESS_3 = '0xc6b82bA149CFA113f8f48d5E3b1F78e933e16DfD' export const DEFAULT_OWNER_ADDRESS = '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED' +export const SPENDING_LIMIT_ADDRESS_2 = '0x52835f11E348605E9D791Ec09380a3224526d538' export const SEPOLIA_OWNER_2 = '0x96D4c6fFC338912322813a77655fCC926b9A5aC5' export const TEST_SAFE_2 = 'gor:0xE96C43C54B08eC528e9e815fC3D02Ea94A320505' export const SIDEBAR_ADDRESS = '0x04f8...1a91' @@ -85,6 +86,11 @@ export const safeListEndpoint = '**/safes' export const VALID_QR_CODE_PATH = '../fixtures/sepolia_test_safe_QR.png' export const INVALID_QR_CODE_PATH = '../fixtures/invalid_image_QR_test.png' +export const TXActionNames = { + resetAllowance: 'resetAllowance', + setAllowance: 'setAllowance', +} + export const networkKeys = { sepolia: '11155111', } From 137ca47352a2be17514cc51e9caca661bd09a398 Mon Sep 17 00:00:00 2001 From: Usame Algan <5880855+usame-algan@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:30:21 +0100 Subject: [PATCH 11/38] fix: Only show recovery expiry if its not null (#3174) --- src/features/recovery/components/RecoveryDetails/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/features/recovery/components/RecoveryDetails/index.tsx b/src/features/recovery/components/RecoveryDetails/index.tsx index 19c296e238..15e6b9dd9e 100644 --- a/src/features/recovery/components/RecoveryDetails/index.tsx +++ b/src/features/recovery/components/RecoveryDetails/index.tsx @@ -32,9 +32,8 @@ export function RecoveryDetails({ item }: { item: RecoveryQueueItem }): ReactEle {generateDataRowValue(transactionHash, 'hash', true)} {dateString(Number(timestamp))} {dateString(Number(validFrom))} - {dateString(Number(expiresAt))} - {Number(expiresAt) && {dateString(Number(expiresAt))}} + {expiresAt ? {dateString(Number(expiresAt))} : null} Advanced details From 4658381f51fcde43111a93ca320602da4f8839cd Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Tue, 30 Jan 2024 16:06:00 +0100 Subject: [PATCH 12/38] tests: update test chain (#3175) --- cypress/e2e/regression/create_safe_google.cy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/e2e/regression/create_safe_google.cy.js b/cypress/e2e/regression/create_safe_google.cy.js index f29ded27e1..54ca5e127a 100644 --- a/cypress/e2e/regression/create_safe_google.cy.js +++ b/cypress/e2e/regression/create_safe_google.cy.js @@ -6,7 +6,7 @@ import * as navigation from '../pages/navigation.page' describe('Safe creation Google tests', () => { beforeEach(() => { - cy.visit(constants.welcomeUrl + '?chain=gor') + cy.visit(constants.welcomeUrl + '?chain=sep') cy.clearLocalStorage() main.acceptCookies() // TODO: Need credentials to finish API Google login From e210bb5697f1c367fe7c14e8d741800755319ca9 Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:31:21 +0100 Subject: [PATCH 13/38] Tests: fix drain account tests (#3178) --- .../e2e/safe-apps/drain_account.spec.cy.js | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/cypress/e2e/safe-apps/drain_account.spec.cy.js b/cypress/e2e/safe-apps/drain_account.spec.cy.js index 3c2f2a14fa..a45d4b537b 100644 --- a/cypress/e2e/safe-apps/drain_account.spec.cy.js +++ b/cypress/e2e/safe-apps/drain_account.spec.cy.js @@ -2,14 +2,15 @@ import 'cypress-file-upload' import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as safeapps from '../pages/safeapps.pages' +import * as navigation from '../pages/navigation.page' describe('Drain Account tests', { defaultCommandTimeout: 12000 }, () => { const appUrl = constants.drainAccount_url const iframeSelector = `iframe[id="iframe-${appUrl}"]` - const visitUrl = `/apps/open?safe=${constants.SEPOLIA_TEST_SAFE_5}&appUrl=${encodeURIComponent(appUrl)}` + const visitUrl = `/apps/open?safe=${constants.SEPOLIA_TEST_SAFE_9}&appUrl=${encodeURIComponent(appUrl)}` beforeEach(() => { - cy.intercept(`**//v1/chains/5/safes/${constants.SEPOLIA_TEST_SAFE_5.substring(4)}/balances/**`, { + cy.intercept(`**//v1/chains/11155111/safes/${constants.SEPOLIA_TEST_SAFE_9.substring(4)}/balances/**`, { fixture: 'balances.json', }) @@ -19,72 +20,72 @@ describe('Drain Account tests', { defaultCommandTimeout: 12000 }, () => { safeapps.clickOnContinueBtn() }) - it.skip('Verify drain can be created', () => { + it('Verify drain can be created', () => { cy.enter(iframeSelector).then((getBody) => { - getBody().findByLabelText(safeapps.recipientStr).type(constants.SAFE_APP_ADDRESS_2) + getBody().findByLabelText(safeapps.recipientStr).type(constants.SEPOLIA_TEST_SAFE_10) getBody().findAllByText(safeapps.transferEverythingStr).click() }) cy.findByRole('button', { name: safeapps.testTransfer1 }) cy.findByRole('button', { name: safeapps.testNativeTransfer2 }) }) - it.skip('Verify partial drain can be created', () => { + it('Verify partial drain can be created', () => { cy.enter(iframeSelector).then((getBody) => { getBody().findByLabelText(safeapps.selectAllRowsChbxStr).click() getBody().findAllByLabelText(safeapps.selectRowChbxStr).eq(1).click() getBody().findAllByLabelText(safeapps.selectRowChbxStr).eq(2).click() - getBody().findByLabelText(safeapps.recipientStr).clear().type(constants.SAFE_APP_ADDRESS_2) + getBody().findByLabelText(safeapps.recipientStr).clear().type(constants.SEPOLIA_TEST_SAFE_10) getBody().findAllByText(safeapps.transfer2AssetsStr).click() }) cy.findByRole('button', { name: safeapps.testTransfer2 }) cy.findByRole('button', { name: safeapps.testNativeTransfer1 }) }) + // TODO: ENS does not resolve it.skip('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().findByLabelText(safeapps.recipientStr).type(constants.ENS_TEST_SEPOLIA).wait(2000) getBody().findAllByText(safeapps.transferEverythingStr).click() }) cy.findByRole('button', { name: safeapps.testTransfer1 }) cy.findByRole('button', { name: safeapps.testNativeTransfer2 }) }) - // TODO: Adjust safe - owner - it.skip('Verify when cancelling a drain, previous data is preserved', () => { + it('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().findByLabelText(safeapps.recipientStr).type(constants.SEPOLIA_TEST_SAFE_10) getBody().findAllByText(safeapps.transferEverythingStr).click() }) - cy.findByRole('button', { name: safeapps.cancelBtnStr }).click() + navigation.clickOnModalCloseBtn() cy.enter(iframeSelector).then((getBody) => { getBody().findAllByText(safeapps.transferEverythingStr).should('be.visible') }) }) - it.skip('Verify a drain cannot be created with no recipient selected', () => { + 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.skip('Verify a drain cannot be created with invalid recipient selected', () => { + 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().findByLabelText(safeapps.recipientStr).type(constants.SEPOLIA_TEST_SAFE_10.substring(1)) getBody().findAllByText(safeapps.transferEverythingStr).click() getBody().findByText(safeapps.validRecipientAddressStr) }) }) - it.skip('Verify a drain cannot be created when no assets are selected', () => { + 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) + getBody().findByLabelText(safeapps.recipientStr).type(constants.SEPOLIA_TEST_SAFE_10) getBody().findAllByText(safeapps.noTokensSelectedStr).should('be.visible') }) }) - it.skip('Verify a drain cannot be created 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') From ca17d27f1c31c433120992b9877676fd99b8e895 Mon Sep 17 00:00:00 2001 From: Usame Algan <5880855+usame-algan@users.noreply.github.com> Date: Wed, 31 Jan 2024 14:07:38 +0100 Subject: [PATCH 14/38] fix: max amount button and timeout error (#3181) * fix: Hide MAX amount button if amount is 0 * fix: Show human-readable timeout error * fix: Max amount button --- src/components/common/TokenAmountInput/index.tsx | 2 +- .../tx-flow/flows/SuccessScreen/StatusMessage.tsx | 4 ++-- src/utils/ethers-utils.ts | 6 +----- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/components/common/TokenAmountInput/index.tsx b/src/components/common/TokenAmountInput/index.tsx index 9be9a9b9cd..854f7db600 100644 --- a/src/components/common/TokenAmountInput/index.tsx +++ b/src/components/common/TokenAmountInput/index.tsx @@ -67,7 +67,7 @@ const TokenAmountInput = ({ variant="standard" InputProps={{ disableUnderline: true, - endAdornment: maxAmount !== undefined && Number(maxAmount) && ( + endAdornment: maxAmount !== undefined && ( diff --git a/src/components/tx-flow/flows/SuccessScreen/StatusMessage.tsx b/src/components/tx-flow/flows/SuccessScreen/StatusMessage.tsx index c91c74d3bd..4632a14182 100644 --- a/src/components/tx-flow/flows/SuccessScreen/StatusMessage.tsx +++ b/src/components/tx-flow/flows/SuccessScreen/StatusMessage.tsx @@ -1,4 +1,4 @@ -import { getTimeoutErrorMessage, isTimeoutError } from '@/utils/ethers-utils' +import { isTimeoutError } from '@/utils/ethers-utils' import classNames from 'classnames' import { Box, Typography } from '@mui/material' import LoadingSpinner, { SpinnerStatus } from '@/components/new-safe/create/steps/StatusStep/LoadingSpinner' @@ -21,7 +21,7 @@ const getStep = (status: PendingStatus, error?: Error) => { default: return { description: error ? 'Transaction failed' : 'Transaction was successful', - instruction: error ? (isTimeoutError(error) ? getTimeoutErrorMessage(error) : error.message) : '', + instruction: error ? (isTimeoutError(error) ? 'Transaction timed out' : error.message) : '', } } } diff --git a/src/utils/ethers-utils.ts b/src/utils/ethers-utils.ts index 7abef1b3d1..1513707971 100644 --- a/src/utils/ethers-utils.ts +++ b/src/utils/ethers-utils.ts @@ -26,11 +26,7 @@ type TimeoutError = Error & { } export const isTimeoutError = (value?: Error): value is TimeoutError => { - return !!value && 'timeout' in value && 'code' in value -} - -export const getTimeoutErrorMessage = (error: TimeoutError) => { - return `Transaction timed out after ${Math.floor(error.timeout / 1000)} seconds` + return !!value && 'reason' in value && value.reason === 'timeout' && 'code' in value } export const splitSignature = (sigBytes: string): Signature => { From ee5cce6d00b1419e38ebc14d069cd1e4514325d9 Mon Sep 17 00:00:00 2001 From: Daniel Dimitrov Date: Mon, 29 Jan 2024 20:05:56 +0100 Subject: [PATCH 15/38] 1.28.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 862dee9c87..3d157c8405 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "safe-wallet-web", "homepage": "https://github.com/safe-global/safe-wallet-web", "license": "GPL-3.0", - "version": "1.27.0", + "version": "1.28.0", "type": "module", "scripts": { "dev": "next dev", From c47e6bbfa22c3af247e7ed237d48847e8ed21f74 Mon Sep 17 00:00:00 2001 From: katspaugh Date: Thu, 1 Feb 2024 09:44:35 +0100 Subject: [PATCH 16/38] Fix: [Cookie banner] Accept selection -> Save settings --- cypress/e2e/pages/address_book.page.js | 3 +-- cypress/e2e/pages/main.page.js | 2 +- src/components/common/CookieBanner/index.tsx | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/pages/address_book.page.js b/cypress/e2e/pages/address_book.page.js index abe9536846..611b7d826b 100644 --- a/cypress/e2e/pages/address_book.page.js +++ b/cypress/e2e/pages/address_book.page.js @@ -1,4 +1,4 @@ -export const acceptSelection = 'Accept selection' +export const acceptSelection = 'Save settings' export const addressBook = 'Address book' const createEntryBtn = 'Create entry' @@ -12,7 +12,6 @@ export const editEntryBtn = 'button[aria-label="Edit entry"]' export const deleteEntryBtn = 'button[aria-label="Delete entry"]' export const deleteEntryModalBtnSection = '.MuiDialogActions-root' export const delteEntryModaldeleteBtn = 'Delete' -const exportFileModalExportBtn = 'Export' const importBtn = 'Import' const exportBtn = 'Export' const exportModalBtn = '[data-testid="export-modal-btn"]' diff --git a/cypress/e2e/pages/main.page.js b/cypress/e2e/pages/main.page.js index 40624b8dab..afbd957e9a 100644 --- a/cypress/e2e/pages/main.page.js +++ b/cypress/e2e/pages/main.page.js @@ -1,6 +1,6 @@ import * as constants from '../../support/constants' -const acceptSelection = 'Accept selection' +const acceptSelection = 'Save settings' const executeStr = 'Execute' export const modalDialogCloseBtn = '[data-testid="modal-dialog-close-btn"]' diff --git a/src/components/common/CookieBanner/index.tsx b/src/components/common/CookieBanner/index.tsx index 188eabd8d1..742a2fb84f 100644 --- a/src/components/common/CookieBanner/index.tsx +++ b/src/components/common/CookieBanner/index.tsx @@ -112,7 +112,7 @@ export const CookieBanner = ({ From e55c145622849e25bdfa398c788575c712d5dfbd Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Thu, 1 Feb 2024 12:10:53 +0100 Subject: [PATCH 17/38] Refactor: remove QR scanner in address inputs (#3165) --- cypress/e2e/pages/load_safe.pages.js | 7 -- cypress/e2e/smoke/load_safe.cy.js | 12 --- cypress/e2e/smoke/spending_limits.cy.js | 6 -- package.json | 2 - src/components/common/AddressInput/index.tsx | 3 - .../common/ScanQRModal/ScanQRButton.tsx | 45 -------- src/components/common/ScanQRModal/index.tsx | 101 ------------------ src/pages/licenses.tsx | 16 --- src/services/analytics/events/modals.ts | 8 -- yarn.lock | 11 +- 10 files changed, 2 insertions(+), 209 deletions(-) delete mode 100644 src/components/common/ScanQRModal/ScanQRButton.tsx delete mode 100644 src/components/common/ScanQRModal/index.tsx diff --git a/cypress/e2e/pages/load_safe.pages.js b/cypress/e2e/pages/load_safe.pages.js index 62b9627fe6..5b51424b9c 100644 --- a/cypress/e2e/pages/load_safe.pages.js +++ b/cypress/e2e/pages/load_safe.pages.js @@ -11,7 +11,6 @@ const addressInput = 'input[name="address"]' const sideBarIcon = '[data-testid="ChevronRightIcon"]' const sidebarCheckIcon = '[data-testid="CheckIcon"]' const addressStepNextBtn = '[data-testid="load-safe-next-btn"]' -const qrCodeBtn = '[data-testid="address-qr-scan"]' const typeFile = '[type="file"]' const nextBtnStr = 'Next' const addBtnStr = 'Add' @@ -65,12 +64,6 @@ export function verifyNameLengthErrorMessage() { cy.get(nameInput).parent().prev('label').contains(invalidAddressNameLengthErrorMsg) } -export function scanQRCode(image) { - cy.get(qrCodeBtn).click() - cy.contains('Upload an image').click() - cy.get(typeFile).attachFile(image) -} - export function inputAddress(address) { cy.get(addressInput).clear().type(address) } diff --git a/cypress/e2e/smoke/load_safe.cy.js b/cypress/e2e/smoke/load_safe.cy.js index d71fb615f2..5dc451d870 100644 --- a/cypress/e2e/smoke/load_safe.cy.js +++ b/cypress/e2e/smoke/load_safe.cy.js @@ -49,18 +49,6 @@ describe('[SMOKE] Load Safe tests', () => { safe.clickOnNextBtn() }) - it('[SMOKE] Verify a valid QR code is accepted', () => { - safe.scanQRCode(constants.VALID_QR_CODE_PATH) - safe.verifyAddressInputValue(constants.SEPOLIA_TEST_SAFE_6) - safe.verifyNextButtonStatus('be.enabled') - safe.clickOnNextBtn() - }) - - it('[SMOKE] Verify a non QR code is not accepted', () => { - safe.scanQRCode(constants.INVALID_QR_CODE_PATH) - safe.verifyQRCodeErrorMsg() - }) - it('[SMOKE] Verify the custom Safe name is successfully loaded', () => { safe.inputNameAndAddress(testSafeName, constants.SEPOLIA_TEST_SAFE_2) safe.clickOnNextBtn() diff --git a/cypress/e2e/smoke/spending_limits.cy.js b/cypress/e2e/smoke/spending_limits.cy.js index e471bdc9c3..038005efe5 100644 --- a/cypress/e2e/smoke/spending_limits.cy.js +++ b/cypress/e2e/smoke/spending_limits.cy.js @@ -2,7 +2,6 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as spendinglimit from '../pages/spending_limits.pages' import * as owner from '../pages/owners.pages' -import * as safe from '../pages/load_safe.pages' describe('[SMOKE] Spending limits tests', () => { beforeEach(() => { @@ -24,11 +23,6 @@ describe('[SMOKE] Spending limits tests', () => { spendinglimit.verifyValidAddressShowsNoErrors() }) - it('Verify Fill by QR code with a valid address', () => { - safe.scanQRCode(constants.VALID_QR_CODE_PATH) - spendinglimit.checkBeneficiaryENS(constants.SEPOLIA_TEST_SAFE_6) - }) - it('Verify Amount input cannot be 0', () => { spendinglimit.enterSpendingLimitAmount('0') spendinglimit.verifyNumberErrorValidation() diff --git a/package.json b/package.json index 3d157c8405..0eef25c804 100644 --- a/package.json +++ b/package.json @@ -90,7 +90,6 @@ "react-gtm-module": "^2.0.11", "react-hook-form": "7.41.1", "react-papaparse": "^4.0.2", - "react-qr-reader": "2.2.1", "react-redux": "^8.0.5", "semver": "^7.5.2" }, @@ -114,7 +113,6 @@ "@types/react": "18.2.28", "@types/react-dom": "18.2.13", "@types/react-gtm-module": "^2.0.1", - "@types/react-qr-reader": "^2.1.5", "@types/semver": "^7.3.10", "@typescript-eslint/eslint-plugin": "^5.47.1", "@walletconnect/types": "^2.11.0", diff --git a/src/components/common/AddressInput/index.tsx b/src/components/common/AddressInput/index.tsx index 99e77dc25b..d4789d5976 100644 --- a/src/components/common/AddressInput/index.tsx +++ b/src/components/common/AddressInput/index.tsx @@ -13,7 +13,6 @@ import { useFormContext, useWatch, type Validate, get } from 'react-hook-form' import { validatePrefixedAddress } from '@/utils/validation' import { useCurrentChain } from '@/hooks/useChains' import useNameResolver from './useNameResolver' -import ScanQRButton from '../ScanQRModal/ScanQRButton' import { FEATURES, hasFeature } from '@/utils/chains' import { cleanInputValue, parsePrefixedAddress } from '@/utils/addresses' import useDebounce from '@/hooks/useDebounce' @@ -97,8 +96,6 @@ const AddressInput = ({ )} - - {onOpenListClick && ( import('.')) - -type Props = { - onScan: (value: string) => void -} - -const ScanQRButton = ({ onScan }: Props): ReactElement => { - const [open, setOpen] = useState(false) - - const openQrModal = () => { - setOpen(true) - trackEvent(MODALS_EVENTS.SCAN_QR) - } - - const closeQrModal = () => { - setOpen(false) - } - - const onScanFinished = (value: string) => { - onScan(value) - closeQrModal() - trackEvent(MODALS_EVENTS.SCAN_QR_FINISHED) - } - - return ( - <> - - - - - {open && ( - - - - )} - - ) -} - -export default ScanQRButton diff --git a/src/components/common/ScanQRModal/index.tsx b/src/components/common/ScanQRModal/index.tsx deleted file mode 100644 index e2b766d772..0000000000 --- a/src/components/common/ScanQRModal/index.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import { useState, useRef, useEffect } from 'react' -import { Box, Dialog, DialogTitle, IconButton, Button, Divider } from '@mui/material' -import QrReader from 'react-qr-reader' -import CloseIcon from '@mui/icons-material/Close' -import Typography from '@mui/material/Typography' -import ErrorMessage from '@/components/tx/ErrorMessage' - -type Props = { - isOpen: boolean - onClose: () => void - onScan: (value: string) => void -} - -const ScanQRModal = ({ isOpen, onClose, onScan }: Props): React.ReactElement => { - const [fileUploadModalOpen, setFileUploadModalOpen] = useState(false) - const [error, setError] = useState('') - const [cameraBlocked, setCameraBlocked] = useState(false) - const scannerRef = useRef(null) - - useEffect(() => { - if (!fileUploadModalOpen && cameraBlocked && !error) { - setFileUploadModalOpen(true) - scannerRef.current?.openImageDialog() - } - }, [cameraBlocked, fileUploadModalOpen, error]) - - const onFileScannedError = (error: Error) => { - if (error.name === 'NotAllowedError' || error.name === 'PermissionDismissedError') { - setCameraBlocked(true) - setFileUploadModalOpen(false) - } else { - setError('The QR could not be read') - } - } - - const onFileScannedResolve = (successData: string | null) => { - if (!successData) { - setError('The QR could not be read') - return - } - - setError('') - onScan(successData) - } - - return ( - - - Scan QR - {onClose ? ( - - - - ) : null} - - - - - - {error && {error}} - - - - - - - - - - - - ) -} - -export default ScanQRModal diff --git a/src/pages/licenses.tsx b/src/pages/licenses.tsx index 7f098df9cb..defd4a398f 100644 --- a/src/pages/licenses.tsx +++ b/src/pages/licenses.tsx @@ -354,14 +354,6 @@ const SafeLicenses = () => ( - - @date-io/date-fns - - - https://github.com/dmtrKovalenko/date-io/blob/master/LICENSE - - - @emotion/cache @@ -698,14 +690,6 @@ const SafeLicenses = () => ( - - react-qr-reader - - - https://github.com/JodusNodus/react-qr-reader/blob/master/LICENSE - - - react-redux diff --git a/src/services/analytics/events/modals.ts b/src/services/analytics/events/modals.ts index 0cd92994e5..7798487023 100644 --- a/src/services/analytics/events/modals.ts +++ b/src/services/analytics/events/modals.ts @@ -15,14 +15,6 @@ export const MODALS_EVENTS = { action: 'Contract interaction', category: MODALS_CATEGORY, }, - SCAN_QR: { - action: 'Scan QR', - category: MODALS_CATEGORY, - }, - SCAN_QR_FINISHED: { - action: 'Scan QR finished', - category: MODALS_CATEGORY, - }, TX_DETAILS: { action: 'Transaction details', category: MODALS_CATEGORY, diff --git a/yarn.lock b/yarn.lock index 92ad7fb689..a1e452314b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5447,13 +5447,6 @@ resolved "https://registry.yarnpkg.com/@types/react-gtm-module/-/react-gtm-module-2.0.1.tgz#b2c6cd14ec251d6ae7fa576edf1d43825908a378" integrity sha512-T/DN9gAbCYk5wJ1nxf4pSwmXz4d1iVjM++OoG+mwMfz9STMAotGjSb65gJHOS5bPvl6vLSsJnuC+y/43OQrltg== -"@types/react-qr-reader@^2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@types/react-qr-reader/-/react-qr-reader-2.1.5.tgz#933351729b01f61ab7700fec3ed3f78737ed08a1" - integrity sha512-a21g42lQkBKr87zS0idEkTsnVB/UObQol9RcB7YTkLokz3bwwDVJqnXaOliITIAfZtQAYpsPAj08Mb9G2U8K4A== - dependencies: - "@types/react" "*" - "@types/react-transition-group@^4.4.10", "@types/react-transition-group@^4.4.5": version "4.4.10" resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac" @@ -5697,7 +5690,7 @@ events "^3.3.0" isomorphic-unfetch "^3.1.0" -"@walletconnect/core@2.11.0", "@walletconnect/core@^2.10.1", "@walletconnect/core@^2.11.0": +"@walletconnect/core@2.11.0", "@walletconnect/core@^2.10.1": version "2.11.0" resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.11.0.tgz#3a4e301077b2f858fd916b7a20b5b984d1afce63" integrity sha512-2Tjp5BCevI7dbmqo/OrCjX4tqgMqwJNQLlQAlphqPfvwlF9+tIu6pGcVbSN3U9zyXzWIZCeleqEaWUeSeET4Ew== @@ -13562,7 +13555,7 @@ react-papaparse@^4.0.2: "@types/papaparse" "^5.3.9" papaparse "^5.4.1" -react-qr-reader@2.2.1, react-qr-reader@^2.2.1: +react-qr-reader@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/react-qr-reader/-/react-qr-reader-2.2.1.tgz#dc89046d1c1a1da837a683dd970de5926817d55b" integrity sha512-EL5JEj53u2yAOgtpAKAVBzD/SiKWn0Bl7AZy6ZrSf1lub7xHwtaXe6XSx36Wbhl1VMGmvmrwYMRwO1aSCT2fwA== From 81713cbccd578dc893ceb75b16f6acc7e3ee7451 Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Thu, 1 Feb 2024 14:03:42 +0100 Subject: [PATCH 18/38] Tests: Sidebar tests (#3188) * tests: add sidebar tests --- cypress/e2e/pages/import_export.pages.js | 2 +- cypress/e2e/pages/main.page.js | 10 ++- cypress/e2e/pages/modals.page.js | 2 + cypress/e2e/pages/navigation.page.js | 17 +++- cypress/e2e/pages/nfts.pages.js | 5 +- cypress/e2e/pages/sidebar.pages.js | 83 +++++++++++++++++++ cypress/e2e/regression/create_tx.cy.js | 35 ++++++++ cypress/e2e/regression/sidebar.cy.js | 56 +++++++++++++ cypress/e2e/smoke/create_tx.cy.js | 14 +--- cypress/support/constants.js | 10 +++ .../common/ChainIndicator/index.tsx | 1 + .../common/ExplorerButton/index.tsx | 1 + src/components/sidebar/QrCodeButton/index.tsx | 4 +- src/components/sidebar/Sidebar/index.tsx | 4 +- .../sidebar/SidebarFooter/index.tsx | 8 +- .../sidebar/SidebarHeader/index.tsx | 4 +- .../sidebar/SidebarNavigation/index.tsx | 2 +- 17 files changed, 229 insertions(+), 29 deletions(-) create mode 100644 cypress/e2e/pages/modals.page.js create mode 100644 cypress/e2e/pages/sidebar.pages.js create mode 100644 cypress/e2e/regression/create_tx.cy.js create mode 100644 cypress/e2e/regression/sidebar.cy.js diff --git a/cypress/e2e/pages/import_export.pages.js b/cypress/e2e/pages/import_export.pages.js index ad81087352..1a214aac88 100644 --- a/cypress/e2e/pages/import_export.pages.js +++ b/cypress/e2e/pages/import_export.pages.js @@ -13,7 +13,7 @@ const appearenceTabStr = 'Appearance' const showMoreTabsBtn = '[data-testid="KeyboardArrowRightIcon"]' const dataTabStr = 'Data' const tab = 'div[role="tablist"] a' -const safeHeaderInfo = '[data-testid="safe-header-info"]' +export const safeHeaderInfo = '[data-testid="safe-header-info"]' export const prependChainPrefixStr = 'Prepend chain prefix to addresses' export const copyAddressStr = 'Copy addresses with chain prefix' export const darkModeStr = 'Dark mode' diff --git a/cypress/e2e/pages/main.page.js b/cypress/e2e/pages/main.page.js index afbd957e9a..75d5bb5cba 100644 --- a/cypress/e2e/pages/main.page.js +++ b/cypress/e2e/pages/main.page.js @@ -41,10 +41,6 @@ export function acceptCookies(index = 0) { }) } -export function verifyGoerliWalletHeader() { - cy.contains(constants.goerlyE2EWallet) -} - export function verifyHomeSafeUrl(safe) { cy.location('href', { timeout: 10000 }).should('include', constants.homeUrl + safe) } @@ -166,3 +162,9 @@ export function checkTextOrder(selector, expectedTextArray) { expect(text).to.eq(expectedTextArray[index]) }) } + +export function verifyElementsStatus(elements, status) { + elements.forEach((element) => { + cy.get(element).should(status) + }) +} diff --git a/cypress/e2e/pages/modals.page.js b/cypress/e2e/pages/modals.page.js new file mode 100644 index 0000000000..9893e3b830 --- /dev/null +++ b/cypress/e2e/pages/modals.page.js @@ -0,0 +1,2 @@ +export const modalTitle = '[data-testid="modal-title"]' +export const modal = '[data-testid="modal-view"]' diff --git a/cypress/e2e/pages/navigation.page.js b/cypress/e2e/pages/navigation.page.js index 477ef4463d..8748d2a581 100644 --- a/cypress/e2e/pages/navigation.page.js +++ b/cypress/e2e/pages/navigation.page.js @@ -1,8 +1,13 @@ export const sideNavSettingsIcon = '[data-testid="settings-nav-icon"]' export const setupSection = '[data-testid="setup-section"]' export const modalBackBtn = '[data-testid="modal-back-btn"]' +export const newTxBtn = '[data-testid="new-tx-btn"]' const modalCloseIcon = '[data-testid="CloseIcon"]' -const newTxBtn = '[data-testid="new-tx-btn"]' +const expandMoreIcon = 'svg[data-testid="ExpandMoreIcon"]' +const sentinelStart = 'div[data-testid="sentinelStart"]' + +const disconnectBtnStr = 'Disconnect' +const notConnectedStatus = 'Connect' export function clickOnSideNavigation(option) { cy.get(option).should('exist').click() @@ -15,3 +20,13 @@ export function clickOnModalCloseBtn() { export function clickOnNewTxBtn() { cy.get(newTxBtn).click() } + +export function clickOnWalletExpandMoreIcon() { + cy.get(expandMoreIcon).eq(0).click() + cy.get(sentinelStart).next().should('be.visible') +} + +export function clickOnDisconnectBtn() { + cy.get('button').contains(disconnectBtnStr).click() + cy.get('button').contains(notConnectedStatus) +} diff --git a/cypress/e2e/pages/nfts.pages.js b/cypress/e2e/pages/nfts.pages.js index 82796786f6..436a722e69 100644 --- a/cypress/e2e/pages/nfts.pages.js +++ b/cypress/e2e/pages/nfts.pages.js @@ -1,8 +1,9 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' +import * as modal from '../pages/modals.page' -const nftModalTitle = '[data-testid="modal-title"]' -const nftModal = '[data-testid="modal-view"]' +const nftModalTitle = modal.modalTitle +const nftModal = modal.modal const nftModalCloseBtn = main.modalDialogCloseBtn const recipientInput = 'input[name="recipient"]' diff --git a/cypress/e2e/pages/sidebar.pages.js b/cypress/e2e/pages/sidebar.pages.js new file mode 100644 index 0000000000..43a81e530f --- /dev/null +++ b/cypress/e2e/pages/sidebar.pages.js @@ -0,0 +1,83 @@ +import * as constants from '../../support/constants' +import * as main from './main.page' +import * as modal from '../pages/modals.page' +import * as navigation from './navigation.page' +import { safeHeaderInfo } from './import_export.pages' + +const chainLogo = '[data-testid="chain-logo"]' +const safeIcon = '[data-testid="safe-icon"]' +const sidebarContainer = '[data-testid="sidebar-container"]' +const openSafesIcon = '[data-testid="open-safes-icon"]' +const qrModalBtn = '[data-testid="qr-modal-btn"]' +const copyAddressBtn = '[data-testid="copy-address-btn"]' +const explorerBtn = '[data-testid="explorer-btn"]' +const sideBarListItem = '[data-testid="sidebar-list-item"]' +const sideBarListItemWhatsNew = '[data-testid="list-item-whats-new"]' +const sideBarListItemNeedHelp = '[data-testid="list-item-need-help"]' + +export const sideBarListItems = ['Home', 'Assets', 'Transactions', 'Address book', 'Apps', 'Settings'] +export const testSafeHeaderDetails = ['2.04 USD', '2/2', constants.SEPOLIA_TEST_SAFE_13_SHORT] +const receiveAssetsStr = 'Receive assets' + +export function verifyNetworkIsDisplayed(netwrok) { + cy.get(sidebarContainer) + .should('be.visible') + .within(() => { + cy.get(chainLogo).should('contain', netwrok) + }) +} + +export function verifySafeHeaderDetails(details) { + main.checkTextsExistWithinElement(safeHeaderInfo, details) + main.verifyElementsExist([safeIcon]) +} + +export function clickOnQRCodeBtn() { + cy.get(sidebarContainer) + .should('be.visible') + .within(() => { + cy.get(qrModalBtn).click() + }) +} + +export function verifyQRModalDisplayed() { + cy.get(modal.modal).should('be.visible') + cy.get(modal.modalTitle).should('contain', receiveAssetsStr) +} + +export function verifyCopyAddressBtn(data) { + cy.get(sidebarContainer) + .should('be.visible') + .within(() => { + cy.get(copyAddressBtn) + .click() + .then(() => + cy.window().then((win) => { + win.navigator.clipboard.readText().then((text) => { + expect(text).to.contain(data) + }) + }), + ) + }) +} + +export function verifyEtherscanLinkExists() { + cy.get(sidebarContainer) + .should('be.visible') + .within(() => { + cy.get(explorerBtn).should('have.attr', 'href').and('include', constants.sepoliaEtherscanlLink) + }) +} + +export function verifyNewTxBtnStatus(status) { + main.verifyElementsStatus([navigation.newTxBtn], status) +} + +export function verifySideListItems() { + main.verifyValuesExist(sideBarListItem, sideBarListItems) + main.verifyElementsExist([sideBarListItemWhatsNew, sideBarListItemNeedHelp]) +} + +export function verifyTxCounter(counter) { + cy.get(sideBarListItem).contains(sideBarListItems[2]).should('contain', counter) +} diff --git a/cypress/e2e/regression/create_tx.cy.js b/cypress/e2e/regression/create_tx.cy.js new file mode 100644 index 0000000000..69fb2d9f1a --- /dev/null +++ b/cypress/e2e/regression/create_tx.cy.js @@ -0,0 +1,35 @@ +import * as constants from '../../support/constants' +import * as main from '../../e2e/pages/main.page' +import * as createtx from '../../e2e/pages/create_tx.pages' + +const sendValue = 0.00002 + +function happyPathToStepTwo() { + createtx.typeRecipientAddress(constants.EOA) + createtx.clickOnTokenselectorAndSelectSepoliaEth() + createtx.setSendValue(sendValue) + createtx.clickOnNextBtn() +} + +describe('Create transactions tests', () => { + beforeEach(() => { + cy.clearLocalStorage() + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_7) + main.acceptCookies() + createtx.clickOnNewtransactionBtn() + createtx.clickOnSendTokensBtn() + }) + + it('Verify submitting a tx and that clicking on notification shows the transaction in queue', () => { + happyPathToStepTwo() + createtx.verifySubmitBtnIsEnabled() + createtx.changeNonce(14) + cy.wait(1000) + createtx.clickOnSignTransactionBtn() + createtx.waitForProposeRequest() + createtx.clickViewTransaction() + createtx.verifySingleTxPage() + createtx.verifyQueueLabel() + createtx.verifyTransactionSummary(sendValue) + }) +}) diff --git a/cypress/e2e/regression/sidebar.cy.js b/cypress/e2e/regression/sidebar.cy.js new file mode 100644 index 0000000000..db5888dc02 --- /dev/null +++ b/cypress/e2e/regression/sidebar.cy.js @@ -0,0 +1,56 @@ +import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as sideBar from '../pages/sidebar.pages' +import * as navigation from '../pages/navigation.page' + +describe('Sidebar tests', () => { + beforeEach(() => { + cy.visit(constants.homeUrl + constants.SEPOLIA_TEST_SAFE_13) + cy.clearLocalStorage() + main.acceptCookies() + }) + + it('Verify Current network is displayed at the top', () => { + sideBar.verifyNetworkIsDisplayed(constants.networks.sepolia) + }) + + it('Verify current safe details', () => { + sideBar.verifySafeHeaderDetails(sideBar.testSafeHeaderDetails) + }) + + it('Verify QR button opens the QR code modal', () => { + sideBar.clickOnQRCodeBtn() + sideBar.verifyQRModalDisplayed() + }) + + it('Verify Copy button copies the address', () => { + sideBar.verifyCopyAddressBtn(constants.SEPOLIA_TEST_SAFE_13.substring(4)) + }) + + it('Verify Open blockexplorer button contain etherscan link', () => { + sideBar.verifyEtherscanLinkExists() + }) + + it('Verify New transaction button enabled for owners', () => { + sideBar.verifyNewTxBtnStatus(constants.enabledStates.enabled) + }) + + it('Verify New transaction button enabled for beneficiaries who are non-owners', () => { + cy.visit(constants.homeUrl + constants.SEPOLIA_TEST_SAFE_14) + sideBar.verifyNewTxBtnStatus(constants.enabledStates.enabled) + }) + + it('Verify New Transaction button disabled for non-owners', () => { + navigation.clickOnWalletExpandMoreIcon() + navigation.clickOnDisconnectBtn() + sideBar.verifyNewTxBtnStatus(constants.enabledStates.disabled) + }) + + it('Verify the side menu buttons exist', () => { + sideBar.verifySideListItems() + }) + + it('Verify counter in the "Transaction" menu item if there are tx in the queue tab', () => { + sideBar.verifyTxCounter(1) + }) +}) diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index 81c1aa0a74..0cf86ec6e0 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -44,7 +44,7 @@ describe('[SMOKE] Create transactions tests', () => { it('[SMOKE] Verify nonce tooltip warning messages', () => { createtx.changeNonce(0) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.lowerThanCurrent + currentNonce.toString()) - createtx.changeNonce(currentNonce + 50) + createtx.changeNonce(currentNonce + 53) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.higherThanRecommended) createtx.changeNonce(currentNonce + 150) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.muchHigherThanRecommended) @@ -69,16 +69,4 @@ describe('[SMOKE] Create transactions tests', () => { createtx.clickOnNoLaterOption() createtx.verifyAddToBatchBtnIsEnabled() }) - - it('[SMOKE] Verify submitting a tx and that clicking on notification shows the transaction in queue', () => { - happyPathToStepTwo() - createtx.verifySubmitBtnIsEnabled() - createtx.changeNonce(14) - createtx.clickOnSignTransactionBtn() - createtx.waitForProposeRequest() - createtx.clickViewTransaction() - createtx.verifySingleTxPage() - createtx.verifyQueueLabel() - createtx.verifyTransactionSummary(sendValue) - }) }) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index 51212cc766..0de3273b1e 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -22,6 +22,10 @@ export const SEPOLIA_TEST_SAFE_10 = 'sep:0x4DD4cB2299E491E1B469245DB589ccB2B16d7 export const SEPOLIA_TEST_SAFE_11 = 'sep:0x81034C61a318649F7aD43f9e8C1051427e326443' // SAFE 12 is used for spending limits export const SEPOLIA_TEST_SAFE_12 = 'sep:0x9190cc22D592dDcf396Fa616ce84a9978fD96Fc9' +// SAFE 13 & 14 are used for sidebar with pending tx, no fyrther tx should be made in it +export const SEPOLIA_TEST_SAFE_13 = 'sep:0x98705770aF3b18db0a64597F6d4DCe825915fec0' +export const SEPOLIA_TEST_SAFE_13_SHORT = '0x9870...fec0' +export const SEPOLIA_TEST_SAFE_14 = 'sep:0xC23e061252BFc7967203D054136d8fA7c7df2fc4' export const SEPOLIA_CONTRACT_SHORT = '0x11AB...34aF' export const SEPOLIA_RECIPIENT_ADDR_SHORT = '0x4DD4...7bde' export const GNO_TEST_SAFE = 'gno:0xB8d760a90a5ed54D3c2b3EFC231277e99188642A' @@ -32,6 +36,7 @@ export const SAFE_APP_ADDRESS = '0x11AB70A4564C62F567B92868Cb5e69b50c5434aF' export const SAFE_APP_ADDRESS_2 = '0x49d4450977E2c95362C13D3a31a09311E0Ea26A6' export const SAFE_APP_ADDRESS_3 = '0xc6b82bA149CFA113f8f48d5E3b1F78e933e16DfD' export const DEFAULT_OWNER_ADDRESS = '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED' +// Below is also used in sidebar tests as a beneficiary export const SPENDING_LIMIT_ADDRESS_2 = '0x52835f11E348605E9D791Ec09380a3224526d538' export const SEPOLIA_OWNER_2 = '0x96D4c6fFC338912322813a77655fCC926b9A5aC5' export const TEST_SAFE_2 = 'gor:0xE96C43C54B08eC528e9e815fC3D02Ea94A320505' @@ -141,6 +146,11 @@ export const checkboxStates = { checked: 'be.checked', } +export const enabledStates = { + enabled: 'not.be.disabled', + disabled: 'be.disabled', +} + export const elementExistanceStates = { exist: 'exist', not_exist: 'not.exist', diff --git a/src/components/common/ChainIndicator/index.tsx b/src/components/common/ChainIndicator/index.tsx index 3d26f9eebc..ab84e47a71 100644 --- a/src/components/common/ChainIndicator/index.tsx +++ b/src/components/common/ChainIndicator/index.tsx @@ -54,6 +54,7 @@ const ChainIndicator = ({ ) : chainConfig ? ( diff --git a/src/components/common/ExplorerButton/index.tsx b/src/components/common/ExplorerButton/index.tsx index e3d1ce88b1..c29133bf79 100644 --- a/src/components/common/ExplorerButton/index.tsx +++ b/src/components/common/ExplorerButton/index.tsx @@ -19,6 +19,7 @@ const ExplorerButton = ({ }: ExplorerButtonProps): ReactElement => ( { return ( <> -
setModalOpen(true)}>{children}
+
setModalOpen(true)}> + {children} +
{modalOpen && ( diff --git a/src/components/sidebar/Sidebar/index.tsx b/src/components/sidebar/Sidebar/index.tsx index ae2f474dc7..e6d0128553 100644 --- a/src/components/sidebar/Sidebar/index.tsx +++ b/src/components/sidebar/Sidebar/index.tsx @@ -26,12 +26,12 @@ const Sidebar = (): ReactElement => { const closeDrawer = useCallback(() => setIsDrawerOpen(false), []) return ( -
+
{/* Open the safes list */} - + diff --git a/src/components/sidebar/SidebarFooter/index.tsx b/src/components/sidebar/SidebarFooter/index.tsx index 6624930784..318061e940 100644 --- a/src/components/sidebar/SidebarFooter/index.tsx +++ b/src/components/sidebar/SidebarFooter/index.tsx @@ -54,7 +54,9 @@ const SidebarFooter = (): ReactElement => { - What's new + + What's new + @@ -66,7 +68,9 @@ const SidebarFooter = (): ReactElement => { - Need help? + + Need help? + diff --git a/src/components/sidebar/SidebarHeader/index.tsx b/src/components/sidebar/SidebarHeader/index.tsx index a9f61acc65..2aba8c87e4 100644 --- a/src/components/sidebar/SidebarHeader/index.tsx +++ b/src/components/sidebar/SidebarHeader/index.tsx @@ -52,7 +52,7 @@ const SafeHeader = (): ReactElement => {
-
+
{safeAddress ? ( ) : ( @@ -89,7 +89,7 @@ const SafeHeader = (): ReactElement => { - + diff --git a/src/components/sidebar/SidebarNavigation/index.tsx b/src/components/sidebar/SidebarNavigation/index.tsx index 62e40d9459..c37f7efbe7 100644 --- a/src/components/sidebar/SidebarNavigation/index.tsx +++ b/src/components/sidebar/SidebarNavigation/index.tsx @@ -67,7 +67,7 @@ const Navigation = (): ReactElement => { > {item.icon && {item.icon}} - + {item.label} From 16cd21a73bf302a2cc3cbdf066500bc686c98178 Mon Sep 17 00:00:00 2001 From: francovenica Date: Thu, 1 Feb 2024 17:30:35 +0000 Subject: [PATCH 19/38] Tests: E2e Assets - Adding the token test file (#3077) * remove balances file, add tokens file * remove 'only' from test * Send button disabled verification * solving clonflict with test safes * fix balances test * Change safe in token test * adding verifyOwner method * renaming method --- .../{balances.pages.js => assets.pages.js} | 15 +- cypress/e2e/pages/main.page.js | 5 + cypress/e2e/regression/assets.cy.js | 185 ------------------ cypress/e2e/regression/balances.cy.js | 125 ------------ .../e2e/regression/balances_pagination.cy.js | 18 +- cypress/e2e/regression/tokens.cy.js | 185 ++++++++++++++++++ cypress/e2e/smoke/assets.cy.js | 36 ++-- cypress/support/constants.js | 2 + 8 files changed, 228 insertions(+), 343 deletions(-) rename cypress/e2e/pages/{balances.pages.js => assets.pages.js} (97%) delete mode 100644 cypress/e2e/regression/assets.cy.js delete mode 100644 cypress/e2e/regression/balances.cy.js create mode 100644 cypress/e2e/regression/tokens.cy.js diff --git a/cypress/e2e/pages/balances.pages.js b/cypress/e2e/pages/assets.pages.js similarity index 97% rename from cypress/e2e/pages/balances.pages.js rename to cypress/e2e/pages/assets.pages.js index ebd08d0de9..2509eefb30 100644 --- a/cypress/e2e/pages/balances.pages.js +++ b/cypress/e2e/pages/assets.pages.js @@ -1,6 +1,5 @@ -import * as main from '../pages/main.page' +import * as main from './main.page' -let etherscanLink = 'a[aria-label="View on sepolia.etherscan.io"]' let etherscanLinkSepolia = 'a[aria-label="View on sepolia.etherscan.io"]' export const balanceSingleRow = '[aria-labelledby="tableTitle"] > tbody tr' const currencyDropdown = '[id="currency"]' @@ -109,6 +108,10 @@ export function showSendBtn(index) { }) } +export function VerifySendButtonIsDisabled() { + cy.get('button').contains(sendBtnStr).should('be.disabled') +} + export function verifyTableRows(assetsLength) { cy.get(balanceSingleRow).should('have.length', assetsLength) } @@ -207,14 +210,14 @@ export function verityTokenAltImageIsVisible(currency, alttext) { }) } -export function verifyAssetNameHasExplorerLink(currency, columnName, sepolia = false) { - if (sepolia) etherscanLink = etherscanLinkSepolia +export function verifyAssetNameHasExplorerLink(currency, columnName) { + etherscanLinkSepolia cy.get(tokenListTable) .contains(currency) .parents('tr') .find('td') .eq(columnName) - .find(etherscanLink) + .find(etherscanLinkSepolia) .should('be.visible') } @@ -225,7 +228,7 @@ export function verifyAssetExplorerLinkNotAvailable(currency, columnName) { .find('td') .eq(columnName) .within(() => { - cy.get(etherscanLink).should('not.exist') + cy.get(etherscanLinkSepolia).should('not.exist') }) } diff --git a/cypress/e2e/pages/main.page.js b/cypress/e2e/pages/main.page.js index 75d5bb5cba..f77d74b050 100644 --- a/cypress/e2e/pages/main.page.js +++ b/cypress/e2e/pages/main.page.js @@ -2,6 +2,7 @@ import * as constants from '../../support/constants' const acceptSelection = 'Save settings' const executeStr = 'Execute' +const connectedOwnerBlock = '[data-testid="open-account-center"]' export const modalDialogCloseBtn = '[data-testid="modal-dialog-close-btn"]' export function clickOnExecuteBtn() { @@ -41,6 +42,10 @@ export function acceptCookies(index = 0) { }) } +export function verifyOwnerConnected(prefix = 'sep:') { + cy.get(connectedOwnerBlock).should('contain', prefix) +} + export function verifyHomeSafeUrl(safe) { cy.location('href', { timeout: 10000 }).should('include', constants.homeUrl + safe) } diff --git a/cypress/e2e/regression/assets.cy.js b/cypress/e2e/regression/assets.cy.js deleted file mode 100644 index d50cb2be18..0000000000 --- a/cypress/e2e/regression/assets.cy.js +++ /dev/null @@ -1,185 +0,0 @@ -import * as constants from '../../support/constants' -import * as main from '../../e2e/pages/main.page' -import * as balances from '../pages/balances.pages' -import * as owner from '../pages/owners.pages' - -const ASSET_NAME_COLUMN = 0 -const TOKEN_AMOUNT_COLUMN = 1 -const FIAT_AMOUNT_COLUMN = 2 - -describe('Assets tests', () => { - const fiatRegex = balances.fiatRegex - - beforeEach(() => { - cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - cy.clearLocalStorage() - main.acceptCookies() - }) - - it('Verify that non-native tokens are present and have balance', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.verifyBalance(balances.currencyDaiCap, TOKEN_AMOUNT_COLUMN, balances.currencyDaiAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyDaiCap, - balances.currencyDaiFormat_2, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyAave, TOKEN_AMOUNT_COLUMN, balances.currencyAaveAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyAave, - balances.currentcyAaveFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyLink, TOKEN_AMOUNT_COLUMN, balances.currencyLinkAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyLink, - balances.currentcyLinkFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyTestTokenA, TOKEN_AMOUNT_COLUMN, balances.currencyTestTokenAAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyTestTokenA, - balances.currentcyTestTokenAFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyTestTokenB, TOKEN_AMOUNT_COLUMN, balances.currencyTestTokenBAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyTestTokenB, - balances.currentcyTestTokenBFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyBalance(balances.currencyUSDC, TOKEN_AMOUNT_COLUMN, balances.currencyTestUSDCAlttext) - balances.verifyTokenBalanceFormat( - balances.currencyUSDC, - balances.currentcyTestUSDCFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - }) - - it('Verify that every token except the native token has a "go to blockexplorer link"', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - // Specifying true for Sepolia. Will delete the flag once completely migrate to Sepolia - balances.verifyAssetNameHasExplorerLink(balances.currencyUSDC, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyTestTokenB, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyTestTokenA, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyLink, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyAave, ASSET_NAME_COLUMN, true) - balances.verifyAssetNameHasExplorerLink(balances.currencyDaiCap, ASSET_NAME_COLUMN, true) - balances.verifyAssetExplorerLinkNotAvailable(constants.tokenNames.sepoliaEther, ASSET_NAME_COLUMN) - }) - - it('Verify the default Fiat currency and the effects after changing it', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.verifyFirstRowDoesNotContainCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) - balances.verifyFirstRowContainsCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) - balances.clickOnCurrencyDropdown() - balances.selectCurrency(balances.currencyEUR) - balances.verifyFirstRowDoesNotContainCurrency(balances.currencyUSD, FIAT_AMOUNT_COLUMN) - balances.verifyFirstRowContainsCurrency(balances.currencyEUR, FIAT_AMOUNT_COLUMN) - }) - - it('Verify that a tool tip is shown pointing to "Token list" dropdown', () => { - //Spam warning message is removed in beforeEach hook - cy.reload() - }) - - it('Verify that checking the checkboxes increases the token selected counter', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.checkTokenCounter(1) - }) - - it('Verify that selecting tokens and saving hides them from the table', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.saveHiddenTokenSelection() - main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink]) - }) - - it('Verify that Cancel closes the menu and does not change the table status', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.clickOnTokenCheckbox(balances.currencyAave) - balances.saveHiddenTokenSelection() - main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.clickOnTokenCheckbox(balances.currencyAave) - balances.cancelSaveHiddenTokenSelection() - main.verifyValuesDoNotExist(balances.tokenListTable, [balances.currencyLink, balances.currencyAave]) - }) - - it('Verify that Deselect All unchecks all tokens from the list', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.clickOnTokenCheckbox(balances.currencyAave) - balances.deselecAlltHiddenTokenSelection() - balances.verifyEachRowHasCheckbox(constants.checkboxStates.unchecked) - }) - - it('Verify the Hidden tokens counter works for spam tokens', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyLink) - balances.saveHiddenTokenSelection() - balances.checkHiddenTokenBtnCounter(1) - }) - - it('Verify the Hidden tokens counter works for native tokens', () => { - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(constants.tokenNames.sepoliaEther) - balances.saveHiddenTokenSelection() - balances.checkHiddenTokenBtnCounter(1) - }) - - it('Verify you can hide tokens from the eye icon in the table rows', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.hideAsset(balances.currencyLink) - }) - - it('Verify the sorting of "Assets" and "Balance" in the table', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.verifyTableRows(7) - balances.clickOnTokenNameSortBtn() - balances.verifyTokenNamesOrder() - balances.clickOnTokenNameSortBtn() - balances.verifyTokenNamesOrder('descending') - balances.clickOnTokenBalanceSortBtn() - balances.verifyTokenBalanceOrder() - balances.clickOnTokenBalanceSortBtn() - balances.verifyTokenBalanceOrder('descending') - }) - - // TODO: New title: "Verify that when owner is disconnected, Send button is disabled". - // Modify test accordingly. Include in smoke. - it('Verify that the Send button shows when hovering a row', () => { - owner.clickOnWalletExpandMoreIcon() - owner.clickOnDisconnectBtn() - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.showSendBtn(0) - owner.verifyTooltiptext(owner.disconnectedUserErrorMsg) - // Removed the part that checks for a non owner error message in the tooltip - // because the safe has no assets, and we don't have a safe with assets where e2e wallet is not an owner - }) -}) diff --git a/cypress/e2e/regression/balances.cy.js b/cypress/e2e/regression/balances.cy.js deleted file mode 100644 index 9638173827..0000000000 --- a/cypress/e2e/regression/balances.cy.js +++ /dev/null @@ -1,125 +0,0 @@ -import * as constants from '../../support/constants' -import * as balances from '../pages/balances.pages' -import * as main from '../../e2e/pages/main.page' - -const ASSETS_LENGTH = 7 -const ASSET_NAME_COLUMN = 0 -const TOKEN_AMOUNT_COLUMN = 1 -const FIAT_AMOUNT_COLUMN = 2 - -describe('Balance tests', () => { - // Fiat balance regex - const fiatRegex = balances.fiatRegex - - beforeEach(() => { - cy.clearLocalStorage() - cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) - 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', () => { - 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', () => { - 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', () => { - 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', () => { - balances.verifyTokenBalanceFormat( - balances.currencyDaiCap, - balances.currentcyDaiFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyTokenBalanceFormat( - balances.currencyAave, - balances.currentcyAaveFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyTokenBalanceFormat( - balances.currencyLink, - balances.currentcyLinkFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyTokenBalanceFormat( - balances.currencyTestTokenA, - balances.currentcyTestTokenAFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyTokenBalanceFormat( - balances.currencyTestTokenB, - balances.currentcyTestTokenBFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyTokenBalanceFormat( - balances.currencyUSDC, - balances.currentcyTestUSDCFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - - balances.verifyTokenBalanceFormat( - constants.tokenNames.sepoliaEther, - balances.currentcySepoliaFormat, - TOKEN_AMOUNT_COLUMN, - FIAT_AMOUNT_COLUMN, - fiatRegex, - ) - }) - - 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', () => { - 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', () => { - balances.hideAsset(balances.currencyDaiCap) - }) - - it('Verify a token can be unhidden', () => { - balances.hideAsset(balances.currencyDaiCap) - balances.openHideTokenMenu() - balances.clickOnTokenCheckbox(balances.currencyDaiCap) - balances.saveHiddenTokenSelection() - balances.verifyTokenIsVisible(balances.currencyDaiCap) - balances.verifyMenuButtonLabelIsDefault() - }) -}) diff --git a/cypress/e2e/regression/balances_pagination.cy.js b/cypress/e2e/regression/balances_pagination.cy.js index 767780c633..a0569de6cc 100644 --- a/cypress/e2e/regression/balances_pagination.cy.js +++ b/cypress/e2e/regression/balances_pagination.cy.js @@ -1,5 +1,5 @@ import * as constants from '../../support/constants' -import * as balances from '../pages/balances.pages' +import * as assets from '../pages/assets.pages' import * as main from '../../e2e/pages/main.page' const ASSETS_LENGTH = 8 @@ -11,16 +11,16 @@ describe('Balance pagination tests', () => { cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_6) main.acceptCookies() - balances.selectTokenList(balances.tokenListOptions.allTokens) + assets.selectTokenList(assets.tokenListOptions.allTokens) }) it('Verify a user can change rows per page and navigate to next and previous page', () => { - balances.verifyInitialTableState() - balances.changeTo10RowsPerPage() - balances.verifyTableHas10Rows() - balances.navigateToNextPage() - balances.verifyTableHasNRows(ASSETS_LENGTH) - balances.navigateToPreviousPage() - balances.verifyTableHas10RowsAgain() + assets.verifyInitialTableState() + assets.changeTo10RowsPerPage() + assets.verifyTableHas10Rows() + assets.navigateToNextPage() + assets.verifyTableHasNRows(ASSETS_LENGTH) + assets.navigateToPreviousPage() + assets.verifyTableHas10RowsAgain() }) }) diff --git a/cypress/e2e/regression/tokens.cy.js b/cypress/e2e/regression/tokens.cy.js new file mode 100644 index 0000000000..927c8e149c --- /dev/null +++ b/cypress/e2e/regression/tokens.cy.js @@ -0,0 +1,185 @@ +import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as assets from '../pages/assets.pages' +import * as owner from '../pages/owners.pages' + +const ASSET_NAME_COLUMN = 0 +const TOKEN_AMOUNT_COLUMN = 1 +const FIAT_AMOUNT_COLUMN = 2 + +describe('Tokens tests', () => { + const fiatRegex = assets.fiatRegex + + beforeEach(() => { + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) + cy.clearLocalStorage() + main.acceptCookies() + }) + + it('Verify that non-native tokens are present and have balance', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.verifyBalance(assets.currencyDaiCap, TOKEN_AMOUNT_COLUMN, assets.currencyDaiAlttext) + assets.verifyTokenBalanceFormat( + assets.currencyDaiCap, + assets.currencyDaiFormat_2, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + assets.verifyBalance(assets.currencyAave, TOKEN_AMOUNT_COLUMN, assets.currencyAaveAlttext) + assets.verifyTokenBalanceFormat( + assets.currencyAave, + assets.currentcyAaveFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + assets.verifyBalance(assets.currencyLink, TOKEN_AMOUNT_COLUMN, assets.currencyLinkAlttext) + assets.verifyTokenBalanceFormat( + assets.currencyLink, + assets.currentcyLinkFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + assets.verifyBalance(assets.currencyTestTokenA, TOKEN_AMOUNT_COLUMN, assets.currencyTestTokenAAlttext) + assets.verifyTokenBalanceFormat( + assets.currencyTestTokenA, + assets.currentcyTestTokenAFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + assets.verifyBalance(assets.currencyTestTokenB, TOKEN_AMOUNT_COLUMN, assets.currencyTestTokenBAlttext) + assets.verifyTokenBalanceFormat( + assets.currencyTestTokenB, + assets.currentcyTestTokenBFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + + assets.verifyBalance(assets.currencyUSDC, TOKEN_AMOUNT_COLUMN, assets.currencyTestUSDCAlttext) + assets.verifyTokenBalanceFormat( + assets.currencyUSDC, + assets.currentcyTestUSDCFormat, + TOKEN_AMOUNT_COLUMN, + FIAT_AMOUNT_COLUMN, + fiatRegex, + ) + }) + + it('Verify that every token except the native token has a "go to blockexplorer link"', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.verifyAssetNameHasExplorerLink(assets.currencyUSDC, ASSET_NAME_COLUMN) + assets.verifyAssetNameHasExplorerLink(assets.currencyTestTokenB, ASSET_NAME_COLUMN) + assets.verifyAssetNameHasExplorerLink(assets.currencyTestTokenA, ASSET_NAME_COLUMN) + assets.verifyAssetNameHasExplorerLink(assets.currencyLink, ASSET_NAME_COLUMN) + assets.verifyAssetNameHasExplorerLink(assets.currencyAave, ASSET_NAME_COLUMN) + assets.verifyAssetNameHasExplorerLink(assets.currencyDaiCap, ASSET_NAME_COLUMN) + assets.verifyAssetExplorerLinkNotAvailable(constants.tokenNames.sepoliaEther, ASSET_NAME_COLUMN) + }) + + it('Verify the default Fiat currency and the effects after changing it', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.verifyFirstRowDoesNotContainCurrency(assets.currencyEUR, FIAT_AMOUNT_COLUMN) + assets.verifyFirstRowContainsCurrency(assets.currencyUSD, FIAT_AMOUNT_COLUMN) + assets.clickOnCurrencyDropdown() + assets.selectCurrency(assets.currencyEUR) + assets.verifyFirstRowDoesNotContainCurrency(assets.currencyUSD, FIAT_AMOUNT_COLUMN) + assets.verifyFirstRowContainsCurrency(assets.currencyEUR, FIAT_AMOUNT_COLUMN) + }) + + it('Verify that checking the checkboxes increases the token selected counter', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(assets.currencyLink) + assets.checkTokenCounter(1) + }) + + it('Verify that selecting tokens and saving hides them from the table', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(assets.currencyLink) + assets.saveHiddenTokenSelection() + main.verifyValuesDoNotExist(assets.tokenListTable, [assets.currencyLink]) + }) + + it('Verify that Cancel closes the menu and does not change the table status', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(assets.currencyLink) + assets.clickOnTokenCheckbox(assets.currencyAave) + assets.saveHiddenTokenSelection() + main.verifyValuesDoNotExist(assets.tokenListTable, [assets.currencyLink, assets.currencyAave]) + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(assets.currencyLink) + assets.clickOnTokenCheckbox(assets.currencyAave) + assets.cancelSaveHiddenTokenSelection() + main.verifyValuesDoNotExist(assets.tokenListTable, [assets.currencyLink, assets.currencyAave]) + }) + + it('Verify that Deselect All unchecks all tokens from the list', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(assets.currencyLink) + assets.clickOnTokenCheckbox(assets.currencyAave) + assets.deselecAlltHiddenTokenSelection() + assets.verifyEachRowHasCheckbox(constants.checkboxStates.unchecked) + }) + + it('Verify the Hidden tokens counter works for spam tokens', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(assets.currencyLink) + assets.saveHiddenTokenSelection() + assets.checkHiddenTokenBtnCounter(1) + }) + + it('Verify the Hidden tokens counter works for native tokens', () => { + assets.openHideTokenMenu() + assets.clickOnTokenCheckbox(constants.tokenNames.sepoliaEther) + assets.saveHiddenTokenSelection() + assets.checkHiddenTokenBtnCounter(1) + }) + + it('Verify you can hide tokens from the eye icon in the table rows', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.hideAsset(assets.currencyLink) + }) + + it('Verify the sorting of "Assets" and "Balance" in the table', () => { + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.verifyTableRows(7) + assets.clickOnTokenNameSortBtn() + assets.verifyTokenNamesOrder() + assets.clickOnTokenNameSortBtn() + assets.verifyTokenNamesOrder('descending') + assets.clickOnTokenBalanceSortBtn() + assets.verifyTokenBalanceOrder() + assets.clickOnTokenBalanceSortBtn() + assets.verifyTokenBalanceOrder('descending') + }) + + //Include in smoke. + it('Verify that when owner is disconnected, Send button is disabled', () => { + //waits for the user to look connected. Sends a default prefix "sep:" if it is called with no params + main.verifyOwnerConnected() + owner.clickOnWalletExpandMoreIcon() + owner.clickOnDisconnectBtn() + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.showSendBtn(0) + assets.VerifySendButtonIsDisabled() + }) + + it('Verify that when connected user is not owner, Send button is disabled', () => { + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_15_TOKEN) + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.showSendBtn(0) + assets.VerifySendButtonIsDisabled() + }) +}) diff --git a/cypress/e2e/smoke/assets.cy.js b/cypress/e2e/smoke/assets.cy.js index 7eb9e311f2..c7fcafcfee 100644 --- a/cypress/e2e/smoke/assets.cy.js +++ b/cypress/e2e/smoke/assets.cy.js @@ -1,13 +1,13 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' -import * as balances from '../pages/balances.pages' +import * as assets from '../pages/assets.pages' const ASSET_NAME_COLUMN = 0 const TOKEN_AMOUNT_COLUMN = 1 const FIAT_AMOUNT_COLUMN = 2 describe('[SMOKE] Assets tests', () => { - const fiatRegex = balances.fiatRegex + const fiatRegex = assets.fiatRegex beforeEach(() => { cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_5) @@ -16,37 +16,37 @@ describe('[SMOKE] Assets tests', () => { }) it('[SMOKE] Verify that the token tab is selected by default and the table is visible', () => { - balances.verifyTokensTabIsSelected('true') + assets.verifyTokensTabIsSelected('true') }) it('[SMOKE] Verify that the native token is visible', () => { - balances.verifyTokenIsPresent(constants.tokenNames.sepoliaEther) + assets.verifyTokenIsPresent(constants.tokenNames.sepoliaEther) }) it('[SMOKE] Verify that Token list dropdown down options show/hide spam tokens', () => { let spamTokens = [ - balances.currencyAave, - balances.currencyTestTokenA, - balances.currencyTestTokenB, - balances.currencyUSDC, - balances.currencyLink, - balances.currencyDaiCap, + assets.currencyAave, + assets.currencyTestTokenA, + assets.currencyTestTokenB, + assets.currencyUSDC, + assets.currencyLink, + assets.currencyDaiCap, ] - main.verifyValuesDoNotExist(balances.tokenListTable, spamTokens) - balances.selectTokenList(balances.tokenListOptions.allTokens) + main.verifyValuesDoNotExist(assets.tokenListTable, spamTokens) + assets.selectTokenList(assets.tokenListOptions.allTokens) spamTokens.push(constants.tokenNames.sepoliaEther) - main.verifyValuesExist(balances.tokenListTable, spamTokens) + main.verifyValuesExist(assets.tokenListTable, spamTokens) }) it('[SMOKE] Verify that "Hide token" button is present and opens the "Hide tokens menu"', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.openHideTokenMenu() - balances.verifyEachRowHasCheckbox() + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.openHideTokenMenu() + assets.verifyEachRowHasCheckbox() }) it('[SMOKE] Verify that clicking the button with an owner opens the Send funds form', () => { - balances.selectTokenList(balances.tokenListOptions.allTokens) - balances.clickOnSendBtn(0) + assets.selectTokenList(assets.tokenListOptions.allTokens) + assets.clickOnSendBtn(0) }) }) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index 0de3273b1e..afc376aba8 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -26,6 +26,8 @@ export const SEPOLIA_TEST_SAFE_12 = 'sep:0x9190cc22D592dDcf396Fa616ce84a9978fD96 export const SEPOLIA_TEST_SAFE_13 = 'sep:0x98705770aF3b18db0a64597F6d4DCe825915fec0' export const SEPOLIA_TEST_SAFE_13_SHORT = '0x9870...fec0' export const SEPOLIA_TEST_SAFE_14 = 'sep:0xC23e061252BFc7967203D054136d8fA7c7df2fc4' +// SAFE 15 is a safe with native tokens but the automation user is not its owner +export const SEPOLIA_TEST_SAFE_15_TOKEN = 'sep:0xfC0A7ac73Fde7547ac0792Cca1D8A50CE0AFC4Df' export const SEPOLIA_CONTRACT_SHORT = '0x11AB...34aF' export const SEPOLIA_RECIPIENT_ADDR_SHORT = '0x4DD4...7bde' export const GNO_TEST_SAFE = 'gno:0xB8d760a90a5ed54D3c2b3EFC231277e99188642A' From 4c98177493f5726e6d4614f30bd28708f19f5951 Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Mon, 5 Feb 2024 12:48:07 +0100 Subject: [PATCH 20/38] Tests: add sidebar tests -p2 (#3201) * tests: add sidebar tests -p2 * tests: replace hardcoded currency value with element --- cypress/e2e/pages/main.page.js | 4 + cypress/e2e/pages/modals.page.js | 9 ++ cypress/e2e/pages/sidebar.pages.js | 111 +++++++++++++++++- cypress/e2e/regression/sidebar_2.cy.js | 48 ++++++++ cypress/e2e/smoke/create_tx.cy.js | 2 +- cypress/support/constants.js | 3 +- cypress/support/localstorage_data.js | 87 ++++++++++++++ .../address-book/EntryDialog/index.tsx | 14 ++- src/components/sidebar/SafeList/index.tsx | 2 +- .../sidebar/SafeListContextMenu/index.tsx | 6 +- src/components/sidebar/SafeListItem/index.tsx | 1 + .../SafeListItemSecondaryAction/index.tsx | 1 + .../sidebar/SafeListRemoveDialog/index.tsx | 6 +- .../sidebar/SidebarHeader/index.tsx | 2 +- 14 files changed, 282 insertions(+), 14 deletions(-) create mode 100644 cypress/e2e/regression/sidebar_2.cy.js diff --git a/cypress/e2e/pages/main.page.js b/cypress/e2e/pages/main.page.js index f77d74b050..36096346de 100644 --- a/cypress/e2e/pages/main.page.js +++ b/cypress/e2e/pages/main.page.js @@ -90,6 +90,10 @@ export function verifyElementsCount(element, count) { cy.get(element).should('have.length', count) } +export function verifyMinimumElementsCount(element, count) { + cy.get(element).should('have.length.at.least', count) +} + export function verifyValuesDoNotExist(element, values) { values.forEach((value) => { cy.get(element).should('not.contain', value) diff --git a/cypress/e2e/pages/modals.page.js b/cypress/e2e/pages/modals.page.js index 9893e3b830..ca5426ba2f 100644 --- a/cypress/e2e/pages/modals.page.js +++ b/cypress/e2e/pages/modals.page.js @@ -1,2 +1,11 @@ export const modalTitle = '[data-testid="modal-title"]' export const modal = '[data-testid="modal-view"]' + +export const modalTitiles = { + editEntry: 'Edit entry', + deleteEntry: 'Delete entry', +} + +export function verifyModalTitle(title) { + cy.get(modalTitle).should('contain', title) +} diff --git a/cypress/e2e/pages/sidebar.pages.js b/cypress/e2e/pages/sidebar.pages.js index 43a81e530f..9604103e55 100644 --- a/cypress/e2e/pages/sidebar.pages.js +++ b/cypress/e2e/pages/sidebar.pages.js @@ -14,9 +14,23 @@ const explorerBtn = '[data-testid="explorer-btn"]' const sideBarListItem = '[data-testid="sidebar-list-item"]' const sideBarListItemWhatsNew = '[data-testid="list-item-whats-new"]' const sideBarListItemNeedHelp = '[data-testid="list-item-need-help"]' +const sideSafeListItem = '[data-testid="safe-list-item"]' +const sidebarSafeHeader = '[data-testid="sidebar-safe-header"]' +const sidebarSafeContainer = '[data-testid="sidebar-safe-container"]' +const safeItemOptionsBtn = '[data-testid="safe-options-btn"]' +const safeItemOptionsRenameBtn = '[data-testid="rename-btn"]' +const safeItemOptionsRemoveBtn = '[data-testid="remove-btn"]' +const nameInput = '[data-testid="name-input"]' +const saveBtn = '[data-testid="save-btn"]' +const cancelBtn = '[data-testid="cancel-btn"]' +const deleteBtn = '[data-testid="delete-btn"]' +const readOnlyVisibility = '[data-testid="read-only-visibility"]' +const currencySection = '[data-testid="currency-section"]' +export const addedSafesGnosis = ['0x17b3...98C8', '0x11A6...F1BB', '0xB8d7...642A'] +export const addedSafesSepolia = ['0x6d0b...6dC1', '0x5912...fFdb', '0x0637...708e', '0xD157...DE9a'] export const sideBarListItems = ['Home', 'Assets', 'Transactions', 'Address book', 'Apps', 'Settings'] -export const testSafeHeaderDetails = ['2.04 USD', '2/2', constants.SEPOLIA_TEST_SAFE_13_SHORT] +export const testSafeHeaderDetails = ['2/2', constants.SEPOLIA_TEST_SAFE_13_SHORT] const receiveAssetsStr = 'Receive assets' export function verifyNetworkIsDisplayed(netwrok) { @@ -29,7 +43,7 @@ export function verifyNetworkIsDisplayed(netwrok) { export function verifySafeHeaderDetails(details) { main.checkTextsExistWithinElement(safeHeaderInfo, details) - main.verifyElementsExist([safeIcon]) + main.verifyElementsExist([safeIcon, currencySection]) } export function clickOnQRCodeBtn() { @@ -81,3 +95,96 @@ export function verifySideListItems() { export function verifyTxCounter(counter) { cy.get(sideBarListItem).contains(sideBarListItems[2]).should('contain', counter) } + +export function verifySafeCount(count) { + main.verifyMinimumElementsCount(sideSafeListItem, count) +} + +export function openSidebar() { + cy.get(openSafesIcon).click() + main.verifyElementsExist([sidebarSafeContainer]) +} + +export function verifyAddedSafesExist(safes) { + main.verifyValuesExist(sideSafeListItem, safes) +} + +export function verifySafesByNetwork(netwrok, safes) { + cy.get(sidebarSafeContainer).within(() => { + cy.get(chainLogo) + .contains(netwrok) + .next() + .within(() => { + main.verifyValuesExist(sideSafeListItem, safes) + }) + }) +} + +export function verifySafeReadOnlyState(safe) { + cy.get(sidebarSafeContainer).within(() => { + cy.get(sideSafeListItem) + .contains(safe) + .parents('li') + .within(() => { + cy.get(readOnlyVisibility).should('exist') + }) + }) +} + +function clickOnSafeItemOptionsBtn(name) { + cy.get(sidebarSafeContainer).within(() => { + cy.get(sideSafeListItem) + .contains(name) + .parents('li') + .within(() => { + cy.get(safeItemOptionsBtn).click() + }) + }) +} +export function renameSafeItem(oldName, newName) { + clickOnSafeItemOptionsBtn(oldName) + clickOnRenameBtn() + typeSafeName(newName) +} + +export function removeSafeItem(name) { + clickOnSafeItemOptionsBtn(name) + clickOnRemoveBtn() + confirmSafeItemRemoval() + verifyModalRemoved() +} + +function typeSafeName(name) { + cy.get(nameInput).find('input').clear().type(name) +} + +function clickOnRenameBtn() { + cy.get(safeItemOptionsRenameBtn).click() +} + +function clickOnRemoveBtn() { + cy.get(safeItemOptionsRemoveBtn).click() +} + +function confirmSafeItemRemoval() { + cy.get(deleteBtn).click() +} + +export function verifySafeNameExists(name) { + cy.get(sidebarSafeContainer).within(() => { + cy.get(sideSafeListItem).contains(name) + }) +} + +export function verifySafeRemoved(name) { + main.verifyValuesDoNotExist(sidebarSafeContainer, [name]) +} + +export function clickOnSaveBtn() { + cy.get(saveBtn).click() + verifyModalRemoved() +} + +function verifyModalRemoved() { + main.verifyElementsCount(modal.modalTitle, 0) +} diff --git a/cypress/e2e/regression/sidebar_2.cy.js b/cypress/e2e/regression/sidebar_2.cy.js new file mode 100644 index 0000000000..daef0b5844 --- /dev/null +++ b/cypress/e2e/regression/sidebar_2.cy.js @@ -0,0 +1,48 @@ +import * as constants from '../../support/constants' +import * as main from '../pages/main.page' +import * as sideBar from '../pages/sidebar.pages' +import * as ls from '../../support/localstorage_data.js' + +const newSafeName = 'Added safe 3' +const oldSafeName = 'Added safe 2' +const staticSafe100 = 'Added safe 100' + +describe('Sidebar added sidebar tests', () => { + beforeEach(() => { + cy.visit(constants.homeUrl + constants.SEPOLIA_TEST_SAFE_13) + cy.wait(2000) + cy.clearLocalStorage() + main.acceptCookies() + main.addToLocalStorage(constants.localStorageKeys.SAFE_v2__addedSafes, ls.addedSafes.set2) + main.addToLocalStorage(constants.localStorageKeys.SAFE_v2__addressBook, ls.addressBookData.addedSafes) + }) + + it('Verify the safe added are listed in the sidebar', () => { + sideBar.openSidebar() + sideBar.verifyAddedSafesExist(sideBar.addedSafesGnosis, sideBar.addedSafesSepolia) + }) + + it('Verify Safes are separated by networks', () => { + sideBar.openSidebar() + sideBar.verifySafesByNetwork(constants.networks.gnosis, sideBar.addedSafesGnosis) + sideBar.verifySafesByNetwork(constants.networks.sepolia, sideBar.addedSafesSepolia) + }) + + it('Verify a safe can be renamed', () => { + sideBar.openSidebar() + sideBar.renameSafeItem(oldSafeName, newSafeName) + sideBar.clickOnSaveBtn() + sideBar.verifySafeNameExists(newSafeName) + }) + + it('Verify a safe can be removed', () => { + sideBar.openSidebar() + sideBar.removeSafeItem(oldSafeName) + sideBar.verifySafeRemoved([oldSafeName]) + }) + + it('Verify the "Read only" tag if the connected user is not an owner of a safe', () => { + sideBar.openSidebar() + sideBar.verifySafeReadOnlyState(staticSafe100) + }) +}) diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index 0cf86ec6e0..a2bb78c06a 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -41,7 +41,7 @@ describe('[SMOKE] Create transactions tests', () => { createtx.verifyMaxAmount(constants.tokenNames.sepoliaEther, constants.tokenAbbreviation.sep) }) - it('[SMOKE] Verify nonce tooltip warning messages', () => { + it.skip('[SMOKE] Verify nonce tooltip warning messages', () => { createtx.changeNonce(0) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.lowerThanCurrent + currentNonce.toString()) createtx.changeNonce(currentNonce + 53) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index afc376aba8..93da606e84 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -22,7 +22,7 @@ export const SEPOLIA_TEST_SAFE_10 = 'sep:0x4DD4cB2299E491E1B469245DB589ccB2B16d7 export const SEPOLIA_TEST_SAFE_11 = 'sep:0x81034C61a318649F7aD43f9e8C1051427e326443' // SAFE 12 is used for spending limits export const SEPOLIA_TEST_SAFE_12 = 'sep:0x9190cc22D592dDcf396Fa616ce84a9978fD96Fc9' -// SAFE 13 & 14 are used for sidebar with pending tx, no fyrther tx should be made in it +// SAFE 13 & 14 are used for sidebar with pending tx, no further tx should be made in it export const SEPOLIA_TEST_SAFE_13 = 'sep:0x98705770aF3b18db0a64597F6d4DCe825915fec0' export const SEPOLIA_TEST_SAFE_13_SHORT = '0x9870...fec0' export const SEPOLIA_TEST_SAFE_14 = 'sep:0xC23e061252BFc7967203D054136d8fA7c7df2fc4' @@ -119,6 +119,7 @@ export const networks = { goerli: /^G(ö|oe)rli$/, sepolia: 'Sepolia', polygon: 'Polygon', + gnosis: 'Gnosis', } export const tokenAbbreviation = { diff --git a/cypress/support/localstorage_data.js b/cypress/support/localstorage_data.js index 7c2b47ab81..62e6a1c879 100644 --- a/cypress/support/localstorage_data.js +++ b/cypress/support/localstorage_data.js @@ -316,6 +316,13 @@ export const addressBookData = { '0x6E834E9D04ad6b26e1525dE1a37BFd9b215f40B7': 'test-sepolia-3', }, }, + addedSafes: { + 100: { + '0x17b34aEf1428A358bA2eA360a098b8A3BEb698C8': 'Added safe 1', + '0x11A6B41322C57Bd0e56cEe06abB11A1E5c1FF1BB': 'Added safe 2', + '0xB8d760a90a5ed54D3c2b3EFC231277e99188642A': 'Added safe 100', + }, + }, } export const safeSettings = { @@ -439,6 +446,86 @@ export const addedSafes = { }, }, }, + set2: { + 11155111: { + '0x6d0b6F96f665Bb4490f9ddb2e450Da2f7e546dC1': { + owners: [ + { + value: '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED', + name: null, + logoUri: null, + }, + ], + threshold: 1, + ethBalance: '0', + }, + '0x5912f6616c84024cD1aff0D5b55bb36F5180fFdb': { + owners: [ + { + value: '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED', + name: null, + logoUri: null, + }, + { + value: '0x96D4c6fFC338912322813a77655fCC926b9A5aC5', + name: null, + logoUri: null, + }, + ], + threshold: 2, + ethBalance: '0.442500000005', + }, + '0x06373d5e45AD31BD354CeBfA8dB4eD2c75B8708e': { + owners: [ + { + value: '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED', + name: null, + logoUri: null, + }, + { + value: '0x96D4c6fFC338912322813a77655fCC926b9A5aC5', + name: null, + logoUri: null, + }, + ], + threshold: 1, + ethBalance: '0.058000000005', + }, + '0xD1571E8Cc4438aFef2836DD9a0E5D09fb63EDE9a': { + owners: [ + { + value: '0x96D4c6fFC338912322813a77655fCC926b9A5aC5', + name: null, + logoUri: null, + }, + { + value: '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED', + name: null, + logoUri: null, + }, + ], + threshold: 1, + ethBalance: '0', + }, + }, + 100: { + '0x17b34aEf1428A358bA2eA360a098b8A3BEb698C8': { + owners: [{ value: '0x11B1D54B66e5e226D6f89069c21A569A22D98cfd' }], + threshold: 1, + ethBalance: '0.001000002', + }, + '0x11A6B41322C57Bd0e56cEe06abB11A1E5c1FF1BB': { + owners: [{ value: '0x7724b234c9099C205F03b458944942bcEBA13408' }], + threshold: 1, + ethBalance: '0', + }, + '0xB8d760a90a5ed54D3c2b3EFC231277e99188642A': { + owners: [{ value: '0x11B1D54B66e5e226D6f89069c21A569A22D98cfd' }], + threshold: 1, + ethBalance: '0.92132507668989', + }, + }, + }, } export const pinnedApps = { diff --git a/src/components/address-book/EntryDialog/index.tsx b/src/components/address-book/EntryDialog/index.tsx index eaae59e9d5..4faa8c199b 100644 --- a/src/components/address-book/EntryDialog/index.tsx +++ b/src/components/address-book/EntryDialog/index.tsx @@ -56,7 +56,7 @@ function EntryDialog({
- + @@ -72,8 +72,16 @@ function EntryDialog({ - - + diff --git a/src/components/sidebar/SafeList/index.tsx b/src/components/sidebar/SafeList/index.tsx index 6a3794b7d1..84c767a83d 100644 --- a/src/components/sidebar/SafeList/index.tsx +++ b/src/components/sidebar/SafeList/index.tsx @@ -101,7 +101,7 @@ const SafeList = ({ closeDrawer }: { closeDrawer?: () => void }): ReactElement = ) return ( -
+
My Safe Accounts diff --git a/src/components/sidebar/SafeListContextMenu/index.tsx b/src/components/sidebar/SafeListContextMenu/index.tsx index 8871fb8dc2..e14dd1719e 100644 --- a/src/components/sidebar/SafeListContextMenu/index.tsx +++ b/src/components/sidebar/SafeListContextMenu/index.tsx @@ -61,7 +61,7 @@ const SafeListContextMenu = ({ return ( <> - + ({ color: palette.border.main })} /> @@ -69,7 +69,7 @@ const SafeListContextMenu = ({ - Rename + Rename {isAdded && ( @@ -77,7 +77,7 @@ const SafeListContextMenu = ({ - Remove + Remove )} diff --git a/src/components/sidebar/SafeListItem/index.tsx b/src/components/sidebar/SafeListItem/index.tsx index 9fd17d3fbe..cc0badb910 100644 --- a/src/components/sidebar/SafeListItem/index.tsx +++ b/src/components/sidebar/SafeListItem/index.tsx @@ -65,6 +65,7 @@ const SafeListItem = ({ return ( - - + diff --git a/src/components/sidebar/SidebarHeader/index.tsx b/src/components/sidebar/SidebarHeader/index.tsx index 2aba8c87e4..1b0fe8617b 100644 --- a/src/components/sidebar/SidebarHeader/index.tsx +++ b/src/components/sidebar/SidebarHeader/index.tsx @@ -70,7 +70,7 @@ const SafeHeader = (): ReactElement => { )} - + {fiatTotal || }
From f220e812d0dc3e073a843a0d21c5a46563c89cda Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Mon, 5 Feb 2024 13:33:01 +0100 Subject: [PATCH 21/38] Fix: edit Safe Apps heading (#3193) --- src/components/safe-apps/SafeAppsHeader/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/safe-apps/SafeAppsHeader/index.tsx b/src/components/safe-apps/SafeAppsHeader/index.tsx index 41a1997c5c..412a5338f0 100644 --- a/src/components/safe-apps/SafeAppsHeader/index.tsx +++ b/src/components/safe-apps/SafeAppsHeader/index.tsx @@ -14,7 +14,7 @@ const SafeAppsHeader = (): ReactElement => { {/* Safe Apps Title */} - Explore the Safe {chain?.chainName} ecosystem + Explore the {chain?.chainName} ecosystem {/* Safe Apps Subtitle */} From b2e774bc9a3a80eca909947de00a9cc8849c6fbe Mon Sep 17 00:00:00 2001 From: francovenica Date: Tue, 6 Feb 2024 07:25:10 +0000 Subject: [PATCH 22/38] Test: E2e fix create tx test (#3205) --- cypress/e2e/regression/remove_owner.cy.js | 18 ++++++++++++++ cypress/e2e/smoke/create_tx.cy.js | 6 ++--- cypress/e2e/smoke/remove_owner.cy.js | 29 ----------------------- cypress/support/constants.js | 1 + 4 files changed, 22 insertions(+), 32 deletions(-) delete mode 100644 cypress/e2e/smoke/remove_owner.cy.js diff --git a/cypress/e2e/regression/remove_owner.cy.js b/cypress/e2e/regression/remove_owner.cy.js index 62563557ab..d4df4d28c0 100644 --- a/cypress/e2e/regression/remove_owner.cy.js +++ b/cypress/e2e/regression/remove_owner.cy.js @@ -1,6 +1,8 @@ import * as constants from '../../support/constants' import * as main from '../../e2e/pages/main.page' import * as owner from '../pages/owners.pages' +import * as createwallet from '../pages/create_wallet.pages' +import * as createTx from '../pages/create_tx.pages.js' describe('Remove Owners tests', () => { beforeEach(() => { @@ -38,4 +40,20 @@ describe('Remove Owners tests', () => { owner.verifyThresholdLimit(1, 1) owner.getThresholdOptions().should('have.length', 1) }) + + it('Verify owner deletion transaction has been created', () => { + owner.waitForConnectionStatus() + owner.openRemoveOwnerWindow(1) + cy.wait(3000) + createwallet.clickOnNextBtn() + //This method creates the @removedAddress alias + owner.getAddressToBeRemoved() + owner.verifyOwnerDeletionWindowDisplayed() + createTx.changeNonce(10) + createTx.clickOnSignTransactionBtn() + createTx.waitForProposeRequest() + createTx.clickViewTransaction() + createTx.clickOnTransactionItemByName('removeOwner') + createTx.verifyTxDestinationAddress('@removedAddress') + }) }) diff --git a/cypress/e2e/smoke/create_tx.cy.js b/cypress/e2e/smoke/create_tx.cy.js index a2bb78c06a..c994a7887a 100644 --- a/cypress/e2e/smoke/create_tx.cy.js +++ b/cypress/e2e/smoke/create_tx.cy.js @@ -3,7 +3,7 @@ import * as main from '../../e2e/pages/main.page' import * as createtx from '../../e2e/pages/create_tx.pages' const sendValue = 0.00002 -const currentNonce = 1 +const currentNonce = 2 function happyPathToStepTwo() { createtx.typeRecipientAddress(constants.EOA) @@ -15,7 +15,7 @@ function happyPathToStepTwo() { describe('[SMOKE] Create transactions tests', () => { beforeEach(() => { cy.clearLocalStorage() - cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_7) + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_16_CREATE_TX) main.acceptCookies() createtx.clickOnNewtransactionBtn() createtx.clickOnSendTokensBtn() @@ -41,7 +41,7 @@ describe('[SMOKE] Create transactions tests', () => { createtx.verifyMaxAmount(constants.tokenNames.sepoliaEther, constants.tokenAbbreviation.sep) }) - it.skip('[SMOKE] Verify nonce tooltip warning messages', () => { + it('[SMOKE] Verify nonce tooltip warning messages', () => { createtx.changeNonce(0) createtx.verifyTooltipMessage(constants.nonceTooltipMsg.lowerThanCurrent + currentNonce.toString()) createtx.changeNonce(currentNonce + 53) diff --git a/cypress/e2e/smoke/remove_owner.cy.js b/cypress/e2e/smoke/remove_owner.cy.js deleted file mode 100644 index e188e98872..0000000000 --- a/cypress/e2e/smoke/remove_owner.cy.js +++ /dev/null @@ -1,29 +0,0 @@ -import * as constants from '../../support/constants' -import * as main from '../../e2e/pages/main.page' -import * as owner from '../pages/owners.pages' -import * as createwallet from '../pages/create_wallet.pages' -import * as createTx from '../pages/create_tx.pages.js' - -describe('[SMOKE] Remove Owners tests', () => { - beforeEach(() => { - cy.visit(constants.setupUrl + constants.SEPOLIA_TEST_SAFE_11) - cy.clearLocalStorage() - main.acceptCookies() - cy.contains(owner.safeAccountNonceStr, { timeout: 10000 }) - }) - - it('[SMOKE] Verify owner deletion transaction has been created', () => { - owner.waitForConnectionStatus() - owner.openRemoveOwnerWindow(1) - cy.wait(3000) - createwallet.clickOnNextBtn() - //This method creates the @removedAddress alias - owner.getAddressToBeRemoved() - owner.verifyOwnerDeletionWindowDisplayed() - createTx.clickOnSignTransactionBtn() - createTx.waitForProposeRequest() - createTx.clickViewTransaction() - createTx.clickOnTransactionItemByName('removeOwner') - createTx.verifyTxDestinationAddress('@removedAddress') - }) -}) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index 93da606e84..f83fb7cfc0 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -28,6 +28,7 @@ export const SEPOLIA_TEST_SAFE_13_SHORT = '0x9870...fec0' export const SEPOLIA_TEST_SAFE_14 = 'sep:0xC23e061252BFc7967203D054136d8fA7c7df2fc4' // SAFE 15 is a safe with native tokens but the automation user is not its owner export const SEPOLIA_TEST_SAFE_15_TOKEN = 'sep:0xfC0A7ac73Fde7547ac0792Cca1D8A50CE0AFC4Df' +export const SEPOLIA_TEST_SAFE_16_CREATE_TX = 'sep:0xc2F3645bfd395516d1a18CA6ad9298299d328C01' export const SEPOLIA_CONTRACT_SHORT = '0x11AB...34aF' export const SEPOLIA_RECIPIENT_ADDR_SHORT = '0x4DD4...7bde' export const GNO_TEST_SAFE = 'gno:0xB8d760a90a5ed54D3c2b3EFC231277e99188642A' From 91f441899e5df9d6f67834291d380605d2b96e03 Mon Sep 17 00:00:00 2001 From: Manuel Gellfart Date: Tue, 6 Feb 2024 11:25:48 +0100 Subject: [PATCH 23/38] Feat: enable base sepolia (#3157) - updates safe-deployments - updates safe-core-sdk packages --- package.json | 11 +- src/config/chains.ts | 3 +- .../security/modules/RedefineModule/index.ts | 2 +- src/services/tx/tx-sender/create.ts | 8 +- src/services/tx/tx-sender/sdk.ts | 13 +-- src/tests/transactions.ts | 1 + yarn.lock | 101 ++++++++++++------ 7 files changed, 93 insertions(+), 46 deletions(-) diff --git a/package.json b/package.json index 0eef25c804..b939beeaf7 100644 --- a/package.json +++ b/package.json @@ -49,10 +49,13 @@ "@mui/material": "^5.14.20", "@mui/x-date-pickers": "^5.0.20", "@reduxjs/toolkit": "^1.9.5", - "@safe-global/api-kit": "^2.0.0", - "@safe-global/protocol-kit": "^2.0.0", + "@safe-global/api-kit": "2.1.0", + "@safe-global/protocol-kit": "3.0.0", "@safe-global/safe-apps-sdk": "^9.0.0-next.1", - "@safe-global/safe-deployments": "1.28.0", + "@safe-global/safe-core-sdk": "^3.3.5", + "@safe-global/safe-core-sdk-utils": "^1.7.4", + "@safe-global/safe-ethers-lib": "^1.9.4", + "@safe-global/safe-deployments": "1.32.0", "@safe-global/safe-gateway-typescript-sdk": "^3.14.0", "@safe-global/safe-modules-deployments": "^1.2.0", "@sentry/react": "^7.91.0", @@ -97,7 +100,7 @@ "@faker-js/faker": "^8.1.0", "@next/bundle-analyzer": "^13.5.6", "@openzeppelin/contracts": "^4.9.2", - "@safe-global/safe-core-sdk-types": "^3.0.1", + "@safe-global/safe-core-sdk-types": "4.0.0", "@sentry/types": "^7.74.0", "@svgr/webpack": "^6.3.1", "@testing-library/cypress": "^8.0.7", diff --git a/src/config/chains.ts b/src/config/chains.ts index aa8fdd7a53..e6615f9980 100644 --- a/src/config/chains.ts +++ b/src/config/chains.ts @@ -24,7 +24,8 @@ const ChainLogos = { [chains.matic]: '/images/networks/polygon.svg', [chains.aurora]: '/images/networks/aurora.svg', [chains.base]: '/images/networks/base.svg', - [chains['base-gor']]: '/images/networks/base.svg', + [chains.basegor]: '/images/networks/base.svg', + [chains.basesep]: '/images/networks/base.svg', [chains.zkevm]: '/images/networks/polygon.svg', [chains.zksync]: '/images/networks/zksync.svg', [chains.celo]: '/images/networks/celo.svg', diff --git a/src/services/security/modules/RedefineModule/index.ts b/src/services/security/modules/RedefineModule/index.ts index 34ae630b44..b0d7ac93ed 100644 --- a/src/services/security/modules/RedefineModule/index.ts +++ b/src/services/security/modules/RedefineModule/index.ts @@ -121,7 +121,7 @@ export class RedefineModule implements SecurityModule { - safeTx.addSignature({ signer, data, staticPart: () => data, dynamicPart: () => '' }) + safeTx.addSignature({ + signer, + data, + staticPart: () => data, + dynamicPart: () => '', + isContractSignature: false, + }) }) return safeTx diff --git a/src/services/tx/tx-sender/sdk.ts b/src/services/tx/tx-sender/sdk.ts index 62d933d57b..587ffc3296 100644 --- a/src/services/tx/tx-sender/sdk.ts +++ b/src/services/tx/tx-sender/sdk.ts @@ -1,6 +1,6 @@ import { getSafeSDK } from '@/hooks/coreSDK/safeCoreSDK' import type Safe from '@safe-global/protocol-kit' -import { EthersAdapter } from '@safe-global/protocol-kit' +import { EthersAdapter, SigningMethod } from '@safe-global/protocol-kit' import type { JsonRpcSigner } from 'ethers' import { ethers } from 'ethers' import { isWalletRejection, isHardwareWallet } from '@/utils/wallets' @@ -123,17 +123,12 @@ export const getSafeSDKWithSigner = async (onboard: OnboardAPI, chainId: SafeInf return sdk.connect({ ethAdapter }) } -type SigningMethods = Parameters[1] - -export const getSupportedSigningMethods = (safeVersion: SafeInfo['version']): SigningMethods[] => { - const ETH_SIGN_TYPED_DATA: SigningMethods = 'eth_signTypedData' - const ETH_SIGN: SigningMethods = 'eth_sign' - +export const getSupportedSigningMethods = (safeVersion: SafeInfo['version']): SigningMethod[] => { if (!hasSafeFeature(SAFE_FEATURES.ETH_SIGN, safeVersion)) { - return [ETH_SIGN_TYPED_DATA] + return [SigningMethod.ETH_SIGN_TYPED_DATA] } - return [ETH_SIGN_TYPED_DATA, ETH_SIGN] + return [SigningMethod.ETH_SIGN_TYPED_DATA, SigningMethod.ETH_SIGN] } export const tryOffChainTxSigning = async ( diff --git a/src/tests/transactions.ts b/src/tests/transactions.ts index 7f8031a9b9..85ed054182 100644 --- a/src/tests/transactions.ts +++ b/src/tests/transactions.ts @@ -96,5 +96,6 @@ export const createMockSafeTransaction = ({ encodedSignatures: () => { return '0x' }, + getSignature: (signer: string) => signatures.get(signer), } } diff --git a/yarn.lock b/yarn.lock index a1e452314b..e7dfb5e69a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1759,7 +1759,7 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/transactions" "^5.5.0" -"@ethersproject/contracts@5.7.0": +"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== @@ -2110,7 +2110,7 @@ "@ethersproject/sha2" "^5.5.0" "@ethersproject/strings" "^5.5.0" -"@ethersproject/solidity@5.7.0": +"@ethersproject/solidity@5.7.0", "@ethersproject/solidity@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" integrity sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA== @@ -3653,7 +3653,7 @@ resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== -"@noble/hashes@^1.3.2": +"@noble/hashes@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== @@ -3911,23 +3911,23 @@ resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz#5f1b518ec5fa54437c0b7c4a821546c64fed6922" integrity sha512-6i/8UoL0P5y4leBIGzvkZdS85RDMG9y1ihZzmTZQ5LdHUYmZ7pKFoj8X0236s3lusPs1Fa5HTQUpwI+UfTcmeA== -"@safe-global/api-kit@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@safe-global/api-kit/-/api-kit-2.0.0.tgz#5d7aaf0a36f5843ba1d788343f0036bf015aa92f" - integrity sha512-Tz6pLEmhhv/ROsYSjVzoR8qw4YK72yNPJCFcK97kSvNJQpM2+HpRVYNjB53rY0IkvP0kVFvF6Ogp/BJri8g1Pw== +"@safe-global/api-kit@2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@safe-global/api-kit/-/api-kit-2.1.0.tgz#ae87aadf15d508549fbb115e601317d568a6a11a" + integrity sha512-pobd2UtvmuUVXn42u6IEW/Lm6ltYx0JBR7qKBigcK3XtNNffYHsS8qIKoDYmG9GG34b+pPLOyLtENcgrYLpzvw== dependencies: - "@safe-global/protocol-kit" "^2.0.0" - "@safe-global/safe-core-sdk-types" "^3.0.0" + "@safe-global/protocol-kit" "^3.0.0" + "@safe-global/safe-core-sdk-types" "^4.0.0" ethers "^6.7.1" node-fetch "^2.7.0" -"@safe-global/protocol-kit@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-2.0.0.tgz#9d7cd6299dc9728c5c5d229a45b73a3b07ec4af7" - integrity sha512-alnSxNZKC1ssKrFG5ytluu9kNKGwBifb1xhOyCqwMnm72JksbCEo0UWlNvaeCiYMwhYvMyS++mfxcLAsV/8Gfw== +"@safe-global/protocol-kit@3.0.0", "@safe-global/protocol-kit@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@safe-global/protocol-kit/-/protocol-kit-3.0.0.tgz#9db15a214bf307af81b082eab329b33642d5acd8" + integrity sha512-0x/VqxWQvjUpkK9VeO/llfYMNoUV/Zngq3PKD8mC3H40v/KzOa2jUSgwxkTiRJRxH1cjuwAG+tXcdVluk73Wfw== dependencies: - "@noble/hashes" "^1.3.2" - "@safe-global/safe-deployments" "^1.28.0" + "@noble/hashes" "^1.3.3" + "@safe-global/safe-deployments" "^1.32.0" ethereumjs-util "^7.1.5" ethers "^6.7.1" semver "^7.5.4" @@ -3943,30 +3943,66 @@ "@safe-global/safe-gateway-typescript-sdk" "^3.5.3" viem "^1.6.0" -"@safe-global/safe-core-sdk-types@^3.0.0", "@safe-global/safe-core-sdk-types@^3.0.1": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-3.0.1.tgz#ec6d053b1648ab80c8d6909cf9d6b0be90b3d0f1" - integrity sha512-2AdlK6GJ5YEZXrQwFsHFwQScnNo3OonF3O6KzVeMc0/7OAuOTYBzKq1jzju2Eck6Z8UNPUinlHoF2Zb2pvTKhw== +"@safe-global/safe-core-sdk-types@4.0.0", "@safe-global/safe-core-sdk-types@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-4.0.0.tgz#68f6206d14cac077986f99d8c9b75348d63cf5f3" + integrity sha512-KZPkCK3Ttnt3nSqn+M/rkkaY6ZZXy/vBC8lvI3lp3Y8SRgERdIymgLYqiizs+JTT9FZk+IFwfCaZz+4gFqvmpw== dependencies: - "@safe-global/safe-deployments" "^1.28.0" + "@safe-global/safe-deployments" "^1.32.0" ethers "^6.7.1" web3-core "^1.10.3" web3-utils "^1.10.3" -"@safe-global/safe-deployments@1.28.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.28.0.tgz#9984b513999e5a1cd4449ed2c1ba9a66cb5b223c" - integrity sha512-zWn55unMucN3i3awjDA0XxH9BzGNHyC/qCbuISBh0GMZP/q+VCxERAOEO4OqwyGaxk6sSAzP4usGdmgz2y2svg== +"@safe-global/safe-core-sdk-types@^1.9.2": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-types/-/safe-core-sdk-types-1.10.1.tgz#94331b982671d2f2b8cc23114c58baf63d460c81" + integrity sha512-BKvuYTLOlY16Rq6qCXglmnL6KxInDuXMFqZMaCzwDKiEh+uoHu3xCumG5tVtWOkCgBF4XEZXMqwZUiLcon7IsA== dependencies: - semver "^7.3.7" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/contracts" "^5.7.0" + "@safe-global/safe-deployments" "^1.20.2" + web3-core "^1.8.1" + web3-utils "^1.8.1" -"@safe-global/safe-deployments@^1.28.0": - version "1.29.0" - resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.29.0.tgz#72091773dccdea67d4a0066eeaaa816613b43cca" - integrity sha512-rXTktZblfklQyPe2JLK7GtXW/jH8htE6oP9MQHpVU5K/98OLkR4ApLAzlJscQEcyCaK+XOQunOk/gQYK1M2IpQ== +"@safe-global/safe-core-sdk-utils@^1.7.4": + version "1.7.4" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk-utils/-/safe-core-sdk-utils-1.7.4.tgz#810d36cf9629129a28eb1b9c6e690b163834b572" + integrity sha512-ITocwSWlFUA1K9VMP/eJiMfgbP/I9qDxAaFz7ukj5N5NZD3ihVQZkmqML6hjse5UhrfjCnfIEcLkNZhtB2XC2Q== + dependencies: + "@safe-global/safe-core-sdk-types" "^1.9.2" + semver "^7.3.8" + web3-utils "^1.8.1" + +"@safe-global/safe-core-sdk@^3.3.5": + version "3.3.5" + resolved "https://registry.yarnpkg.com/@safe-global/safe-core-sdk/-/safe-core-sdk-3.3.5.tgz#30884639d368a9f50aa5fc96f78de87261ebdab3" + integrity sha512-ul+WmpxZOXgDIXrZ6MIHptThYbm0CVV3/rypMQEn4tZLkudh/yXK7EuWBFnx9prR3MePuku51Zcz9fu1vi7sfQ== + dependencies: + "@ethersproject/solidity" "^5.7.0" + "@safe-global/safe-core-sdk-types" "^1.9.2" + "@safe-global/safe-core-sdk-utils" "^1.7.4" + "@safe-global/safe-deployments" "^1.25.0" + ethereumjs-util "^7.1.5" + semver "^7.3.8" + web3-utils "^1.8.1" + zksync-web3 "^0.14.3" + +"@safe-global/safe-deployments@1.32.0", "@safe-global/safe-deployments@^1.20.2", "@safe-global/safe-deployments@^1.25.0", "@safe-global/safe-deployments@^1.32.0": + version "1.32.0" + resolved "https://registry.yarnpkg.com/@safe-global/safe-deployments/-/safe-deployments-1.32.0.tgz#3632194d883aa07bd446a6e530e825ba022c9b76" + integrity sha512-7RXmnBrUzJ9+Iat74yx3Gel0kygmsaWjJhqr+0Fy8mkP5ly/6dTZ/2ize1pv3j9Yal04NTOqXbaJG4JnbTANQw== dependencies: semver "^7.3.7" +"@safe-global/safe-ethers-lib@^1.9.4": + version "1.9.4" + resolved "https://registry.yarnpkg.com/@safe-global/safe-ethers-lib/-/safe-ethers-lib-1.9.4.tgz#049989a302c6f2010c574cf3a834b0cfb9cf67c5" + integrity sha512-WhzcmNun0s0VxeVQKRqaapV0vEpdm76zZBR2Du+S+58u1r57OjZkOSL2Gru0tdwkt3FIZZtE3OhDu09M70pVkA== + dependencies: + "@safe-global/safe-core-sdk-types" "^1.9.2" + "@safe-global/safe-core-sdk-utils" "^1.7.4" + ethers "5.7.2" + "@safe-global/safe-gateway-typescript-sdk@^3.14.0": version "3.14.0" resolved "https://registry.yarnpkg.com/@safe-global/safe-gateway-typescript-sdk/-/safe-gateway-typescript-sdk-3.14.0.tgz#9581c524c1ea4956555f40761eb6b4007392aa82" @@ -15916,7 +15952,7 @@ web3-core@1.10.2: web3-core-requestmanager "1.10.2" web3-utils "1.10.2" -web3-core@1.10.3, web3-core@^1.10.3: +web3-core@1.10.3, web3-core@^1.10.3, web3-core@^1.8.1: version "1.10.3" resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.10.3.tgz#4aeb8f4b0cb5775d9fa4edf1127864743f1c3ae3" integrity sha512-Vbk0/vUNZxJlz3RFjAhNNt7qTpX8yE3dn3uFxfX5OHbuon5u65YEOd3civ/aQNW745N0vGUlHFNxxmn+sG9DIw== @@ -16986,3 +17022,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zksync-web3@^0.14.3: + version "0.14.4" + resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.14.4.tgz#0b70a7e1a9d45cc57c0971736079185746d46b1f" + integrity sha512-kYehMD/S6Uhe1g434UnaMN+sBr9nQm23Ywn0EUP5BfQCsbjcr3ORuS68PosZw8xUTu3pac7G6YMSnNHk+fwzvg== From 7a0e42f454a5d85a00821c880a759e6a8de11745 Mon Sep 17 00:00:00 2001 From: Manuel Gellfart Date: Tue, 6 Feb 2024 16:14:08 +0100 Subject: [PATCH 24/38] fix: do not forward eth_chainId (#3208) - also fixes lint issues in test files --- .../safe-apps/AppFrame/useAppCommunicator.ts | 4 ++-- .../tx/SignOrExecuteForm/hooks.test.ts | 7 +++++++ src/hooks/wallets/useInitWeb3.ts | 7 +++---- src/hooks/wallets/web3.ts | 20 +++++++++++-------- .../tx/__tests__/encodeSignatures.test.ts | 3 +++ 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/components/safe-apps/AppFrame/useAppCommunicator.ts b/src/components/safe-apps/AppFrame/useAppCommunicator.ts index df36131719..e87a09472f 100644 --- a/src/components/safe-apps/AppFrame/useAppCommunicator.ts +++ b/src/components/safe-apps/AppFrame/useAppCommunicator.ts @@ -28,11 +28,11 @@ import type { Permission, PermissionRequest } from '@safe-global/safe-apps-sdk/d import type { SafeSettings } from '@safe-global/safe-apps-sdk' import AppCommunicator from '@/services/safe-apps/AppCommunicator' import { Errors, logError } from '@/services/exceptions' -import { createSafeAppsWeb3Provider } from '@/hooks/wallets/web3' import type { SafePermissionsRequest } from '@/hooks/safe-apps/permissions' import { SAFE_APPS_EVENTS, trackSafeAppEvent } from '@/services/analytics' import { useAppSelector } from '@/store' import { selectRpc } from '@/store/settingsSlice' +import { createSafeAppsWeb3Provider } from '@/hooks/wallets/web3' export enum CommunicatorMessages { REJECT_TRANSACTION_MESSAGE = 'Transaction was rejected', @@ -79,7 +79,7 @@ const useAppCommunicator = ( return } - return createSafeAppsWeb3Provider(chain.rpcUri, customRpc?.[chain.chainId]) + return createSafeAppsWeb3Provider(chain, customRpc?.[chain.chainId]) }, [chain, customRpc]) useEffect(() => { diff --git a/src/components/tx/SignOrExecuteForm/hooks.test.ts b/src/components/tx/SignOrExecuteForm/hooks.test.ts index b2ede6cbb6..d0bb6f7125 100644 --- a/src/components/tx/SignOrExecuteForm/hooks.test.ts +++ b/src/components/tx/SignOrExecuteForm/hooks.test.ts @@ -410,6 +410,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) const id = await executeTx({ gasPrice: 1 }, tx, '123', 'origin.com', true) @@ -442,6 +443,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) const proposeSpy = jest @@ -453,6 +455,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) return Promise.resolve(tx) }) @@ -492,6 +495,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) const proposeSpy = jest @@ -503,6 +507,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) return Promise.resolve(tx) }) @@ -535,6 +540,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) const { result } = renderHook(() => useAlreadySigned(tx)) expect(result.current).toEqual(true) @@ -554,6 +560,7 @@ describe('SignOrExecute hooks', () => { data: '0x0001', staticPart: () => '', dynamicPart: () => '', + isContractSignature: false, }) const { result } = renderHook(() => useAlreadySigned(tx)) expect(result.current).toEqual(false) diff --git a/src/hooks/wallets/useInitWeb3.ts b/src/hooks/wallets/useInitWeb3.ts index 1015214a39..e9e897ef35 100644 --- a/src/hooks/wallets/useInitWeb3.ts +++ b/src/hooks/wallets/useInitWeb3.ts @@ -9,7 +9,6 @@ import { selectRpc } from '@/store/settingsSlice' export const useInitWeb3 = () => { const chain = useCurrentChain() const chainId = chain?.chainId - const rpcUri = chain?.rpcUri const wallet = useWallet() const customRpc = useAppSelector(selectRpc) const customRpcUrl = chain ? customRpc?.[chain.chainId] : undefined @@ -24,11 +23,11 @@ export const useInitWeb3 = () => { }, [wallet, chainId]) useEffect(() => { - if (!rpcUri) { + if (!chain) { setWeb3ReadOnly(undefined) return } - const web3ReadOnly = createWeb3ReadOnly(rpcUri, customRpcUrl) + const web3ReadOnly = createWeb3ReadOnly(chain, customRpcUrl) setWeb3ReadOnly(web3ReadOnly) - }, [rpcUri, customRpcUrl]) + }, [chain, customRpcUrl]) } diff --git a/src/hooks/wallets/web3.ts b/src/hooks/wallets/web3.ts index 181b1875ef..3ac3a6792e 100644 --- a/src/hooks/wallets/web3.ts +++ b/src/hooks/wallets/web3.ts @@ -1,6 +1,6 @@ -import { RPC_AUTHENTICATION, type RpcUri } from '@safe-global/safe-gateway-typescript-sdk' +import { type ChainInfo, RPC_AUTHENTICATION, type RpcUri } from '@safe-global/safe-gateway-typescript-sdk' import { INFURA_TOKEN, SAFE_APPS_INFURA_TOKEN } from '@/config/constants' -import { JsonRpcProvider, BrowserProvider, type Eip1193Provider, type Provider } from 'ethers' +import { JsonRpcProvider, BrowserProvider, type Eip1193Provider, type Provider, Network } from 'ethers' import ExternalStore from '@/services/ExternalStore' import { EMPTY_DATA } from '@safe-global/protocol-kit/dist/src/utils/constants' @@ -20,20 +20,24 @@ export const getRpcServiceUrl = (rpcUri: RpcUri): string => { return formatRpcServiceUrl(rpcUri, INFURA_TOKEN) } -export const createWeb3ReadOnly = (rpcUri: RpcUri, customRpc?: string): JsonRpcProvider | undefined => { - const url = customRpc || getRpcServiceUrl(rpcUri) +export const createWeb3ReadOnly = (chain: ChainInfo, customRpc?: string): JsonRpcProvider | undefined => { + const url = customRpc || getRpcServiceUrl(chain.rpcUri) if (!url) return - return new JsonRpcProvider(url) + return new JsonRpcProvider(url, new Network(chain.chainName, chain.chainId), { + staticNetwork: true, + }) } export const createWeb3 = (walletProvider: Eip1193Provider): BrowserProvider => { return new BrowserProvider(walletProvider) } -export const createSafeAppsWeb3Provider = (safeAppsRpcUri: RpcUri, customRpc?: string): JsonRpcProvider | undefined => { - const url = customRpc || formatRpcServiceUrl(safeAppsRpcUri, SAFE_APPS_INFURA_TOKEN) +export const createSafeAppsWeb3Provider = (chain: ChainInfo, customRpc?: string): JsonRpcProvider | undefined => { + const url = customRpc || formatRpcServiceUrl(chain.rpcUri, SAFE_APPS_INFURA_TOKEN) if (!url) return - return new JsonRpcProvider(url) + return new JsonRpcProvider(url, new Network(chain.chainName, chain.chainId), { + staticNetwork: true, + }) } export const { setStore: setWeb3, useStore: useWeb3 } = new ExternalStore() diff --git a/src/services/tx/__tests__/encodeSignatures.test.ts b/src/services/tx/__tests__/encodeSignatures.test.ts index 28a7271806..c66c44bd41 100644 --- a/src/services/tx/__tests__/encodeSignatures.test.ts +++ b/src/services/tx/__tests__/encodeSignatures.test.ts @@ -32,6 +32,7 @@ describe('encodeSignatures', () => { data: '0xEEE', staticPart: () => '0xEEE', dynamicPart: () => '', + isContractSignature: false, }) safeTx.addSignature({ @@ -39,6 +40,7 @@ describe('encodeSignatures', () => { data: '0xAAA', staticPart: () => '0xAAA', dynamicPart: () => '', + isContractSignature: false, }) const owner = '0x123' @@ -57,6 +59,7 @@ describe('encodeSignatures', () => { data: '0xAAA', staticPart: () => '0xAAA', dynamicPart: () => '', + isContractSignature: false, }) const owner = '0x123' From 074146d7a5b5639c38d6b214f286815c19b152b6 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Wed, 7 Feb 2024 10:57:41 +0100 Subject: [PATCH 25/38] Refactor: remove custom ENS resolver for Sepolia (#3204) --- src/components/common/AddressInput/index.tsx | 2 +- .../flows/NftTransfer/SendNftBatch.tsx | 2 +- .../TokenTransfer/CreateTokenTransfer.tsx | 6 +---- .../__tests__/CreateTokenTransfer.test.tsx | 2 +- src/services/ens/config.ts | 6 ----- src/services/ens/custom.ts | 24 ------------------- src/services/ens/index.test.ts | 17 ++----------- src/services/ens/index.ts | 8 ------- 8 files changed, 6 insertions(+), 61 deletions(-) delete mode 100644 src/services/ens/config.ts delete mode 100644 src/services/ens/custom.ts diff --git a/src/components/common/AddressInput/index.tsx b/src/components/common/AddressInput/index.tsx index d4789d5976..a72a2e950e 100644 --- a/src/components/common/AddressInput/index.tsx +++ b/src/components/common/AddressInput/index.tsx @@ -117,7 +117,7 @@ const AddressInput = ({ className={inputCss.input} autoComplete="off" autoFocus={props.focused} - label={<>{error?.message || props.label}} + label={<>{error?.message || props.label || `Recipient address${isDomainLookupEnabled ? ' or ENS' : ''}`}} error={!!error} fullWidth spellCheck={false} diff --git a/src/components/tx-flow/flows/NftTransfer/SendNftBatch.tsx b/src/components/tx-flow/flows/NftTransfer/SendNftBatch.tsx index f60583c5e7..9f2bb7977c 100644 --- a/src/components/tx-flow/flows/NftTransfer/SendNftBatch.tsx +++ b/src/components/tx-flow/flows/NftTransfer/SendNftBatch.tsx @@ -113,7 +113,7 @@ const SendNftBatch = ({ params, onSubmit }: SendNftBatchProps) => { - + diff --git a/src/components/tx-flow/flows/TokenTransfer/CreateTokenTransfer.tsx b/src/components/tx-flow/flows/TokenTransfer/CreateTokenTransfer.tsx index f312db8aaf..6900b3c3ed 100644 --- a/src/components/tx-flow/flows/TokenTransfer/CreateTokenTransfer.tsx +++ b/src/components/tx-flow/flows/TokenTransfer/CreateTokenTransfer.tsx @@ -103,11 +103,7 @@ export const CreateTokenTransfer = ({ - + diff --git a/src/components/tx-flow/flows/TokenTransfer/__tests__/CreateTokenTransfer.test.tsx b/src/components/tx-flow/flows/TokenTransfer/__tests__/CreateTokenTransfer.test.tsx index af351c4fa9..01c52de3bd 100644 --- a/src/components/tx-flow/flows/TokenTransfer/__tests__/CreateTokenTransfer.test.tsx +++ b/src/components/tx-flow/flows/TokenTransfer/__tests__/CreateTokenTransfer.test.tsx @@ -29,7 +29,7 @@ describe('CreateTokenTransfer', () => { , ) - expect(getAllByText('Recipient address or ENS')[0]).toBeInTheDocument() + expect(getAllByText('Recipient address')[0]).toBeInTheDocument() }) it('should disable the submit button and display a warning if $SAFE token is selected and not transferable', () => { diff --git a/src/services/ens/config.ts b/src/services/ens/config.ts deleted file mode 100644 index 7f96cde9f6..0000000000 --- a/src/services/ens/config.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @see https://docs.ens.domains/ens-deployments - */ -export const CUSTOM_REGISTRIES: Record = { - '11155111': '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e', // ENS on Sepolia -} diff --git a/src/services/ens/custom.ts b/src/services/ens/custom.ts deleted file mode 100644 index ca6d629596..0000000000 --- a/src/services/ens/custom.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Contract, namehash, ZeroAddress, type Provider } from 'ethers' - -// ENS Registry ABI (simplified version) -const ensRegistryAbi = ['function resolver(bytes32 node) external view returns (address)'] -const resolverAbi = ['function addr(bytes32 node) external view returns (address)'] - -export const customResolveName = async ( - registryAddress: string, - rpcProvider: Provider, - name: string, -): Promise => { - const ensRegistry = new Contract(registryAddress, ensRegistryAbi, rpcProvider) - const nhash = namehash(name) - const resolverAddress = await ensRegistry.resolver(nhash) - - if (resolverAddress === ZeroAddress) { - return undefined - } - - const resolver = new Contract(resolverAddress, resolverAbi, rpcProvider) - const address = await resolver.addr(nhash) - - return address || undefined -} diff --git a/src/services/ens/index.test.ts b/src/services/ens/index.test.ts index 42ac92a54a..c66abf42d8 100644 --- a/src/services/ens/index.test.ts +++ b/src/services/ens/index.test.ts @@ -4,7 +4,7 @@ import { logError } from '../exceptions' // mock rpcProvider const rpcProvider = { - resolveName: jest.fn(() => Promise.resolve('0x0000000000000000000000000000000000000000')), + resolveName: jest.fn(() => Promise.resolve('0x0000000000000000000000000000000000000001')), lookupAddress: jest.fn(() => Promise.resolve('safe.eth')), getNetwork: jest.fn(() => Promise.resolve({ chainId: 1 })), } as unknown as JsonRpcProvider @@ -20,10 +20,6 @@ jest.mock('../exceptions', () => ({ logError: jest.fn(), })) -jest.mock('./custom', () => ({ - customResolveName: jest.fn(() => Promise.resolve('0x0000001111111111111111111111111111111111')), -})) - describe('domains', () => { describe('isDomain', () => { it('should check the domain format', async () => { @@ -37,7 +33,7 @@ describe('domains', () => { describe('resolveName', () => { it('should resolve names', async () => { - expect(await resolveName(rpcProvider, 'test.eth')).toBe('0x0000000000000000000000000000000000000000') + expect(await resolveName(rpcProvider, 'test.eth')).toBe('0x0000000000000000000000000000000000000001') }) it('should return undefined and log on error', async () => { @@ -45,15 +41,6 @@ describe('domains', () => { expect(address).toBe(undefined) expect(logError).toHaveBeenCalledWith('101: Failed to resolve the address', 'bad resolveName') }) - - it('should look up names on Sepolia', async () => { - // mock rpcProvider - const rpcProvider = { - getNetwork: jest.fn(() => Promise.resolve({ chainId: 11155111 })), - } as unknown as JsonRpcProvider - - expect(await resolveName(rpcProvider, 'sepolia.eth')).toBe('0x0000001111111111111111111111111111111111') - }) }) describe('lookupAddress', () => { diff --git a/src/services/ens/index.ts b/src/services/ens/index.ts index 60b98da554..295f1368fc 100644 --- a/src/services/ens/index.ts +++ b/src/services/ens/index.ts @@ -1,8 +1,6 @@ import { type Provider } from 'ethers' import { logError } from '../exceptions' import ErrorCodes from '../exceptions/ErrorCodes' -import { CUSTOM_REGISTRIES } from './config' -import { customResolveName } from './custom' type EthersError = Error & { reason?: string @@ -22,12 +20,6 @@ export const resolveName = async (rpcProvider: Provider, name: string): Promise< } catch {} try { - // Try custom resolvers first - if (chainId && CUSTOM_REGISTRIES[chainId]) { - return await customResolveName(CUSTOM_REGISTRIES[chainId], rpcProvider, name) - } - - // The default ENS resolver return (await rpcProvider.resolveName(name)) || undefined } catch (e) { const err = e as EthersError From 5908386a71f4294115cb08cd13bde8a7c3a52054 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Wed, 7 Feb 2024 12:58:02 +0100 Subject: [PATCH 26/38] Fix: update the Safe Apps SDK link (#3213) --- src/components/safe-apps/AddCustomAppModal/index.tsx | 2 +- src/config/constants.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/safe-apps/AddCustomAppModal/index.tsx b/src/components/safe-apps/AddCustomAppModal/index.tsx index c32fce32cf..eeae0cc239 100644 --- a/src/components/safe-apps/AddCustomAppModal/index.tsx +++ b/src/components/safe-apps/AddCustomAppModal/index.tsx @@ -45,7 +45,7 @@ type CustomAppFormData = { safeApp: SafeAppData } -const HELP_LINK = 'https://docs.safe.global/safe-core-aa-sdk/safe-apps/get-started' +const HELP_LINK = 'https://docs.safe.global/apps-sdk-overview' const APP_ALREADY_IN_THE_LIST_ERROR = 'This Safe App is already in the list' const MANIFEST_ERROR = "The app doesn't support Safe App functionality" const INVALID_URL_ERROR = 'The url is invalid' diff --git a/src/config/constants.ts b/src/config/constants.ts index 459a1a51e5..7742697469 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -37,7 +37,7 @@ export const SAFE_TOKEN_ADDRESSES: { [chainId: string]: string } = { export const SAFE_APPS_INFURA_TOKEN = process.env.NEXT_PUBLIC_SAFE_APPS_INFURA_TOKEN || INFURA_TOKEN export const SAFE_APPS_THIRD_PARTY_COOKIES_CHECK_URL = 'https://third-party-cookies-check.gnosis-safe.com' export const SAFE_APPS_DEMO_SAFE_MAINNET = 'eth:0xfF501B324DC6d78dC9F983f140B9211c3EdB4dc7' -export const SAFE_APPS_SDK_DOCS_URL = 'https://docs.safe.global/safe-core-aa-sdk/safe-apps' +export const SAFE_APPS_SDK_DOCS_URL = 'https://docs.safe.global/apps-sdk-overview' // Google Tag Manager export const GOOGLE_TAG_MANAGER_ID = process.env.NEXT_PUBLIC_GOOGLE_TAG_MANAGER_ID || '' From 18239b2bcd664ebb85c97d5356d7238b53d040c0 Mon Sep 17 00:00:00 2001 From: katspaugh <381895+katspaugh@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:34:43 +0100 Subject: [PATCH 27/38] Chore: update WalletConnect packages (#3216) --- package.json | 11 ++++--- yarn.lock | 90 ++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 86 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index b939beeaf7..6e485592b3 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "cypress:run": "cypress run", "cypress:ci": "yarn cypress:run --config baseUrl=http://localhost:8080 --spec cypress/e2e/smoke/*.cy.js", "serve": "npx -y serve out -p ${REVERSE_PROXY_UI_PORT:=8080}", - "static-serve": "yarn build && yarn serve" + "static-serve": "yarn build && yarn serve", + "update-wc": "yarn add @walletconnect/web3wallet@latest @walletconnect/utils@latest @walletconnect/types@latest" }, "engines": { "node": ">=16" @@ -54,16 +55,16 @@ "@safe-global/safe-apps-sdk": "^9.0.0-next.1", "@safe-global/safe-core-sdk": "^3.3.5", "@safe-global/safe-core-sdk-utils": "^1.7.4", - "@safe-global/safe-ethers-lib": "^1.9.4", "@safe-global/safe-deployments": "1.32.0", + "@safe-global/safe-ethers-lib": "^1.9.4", "@safe-global/safe-gateway-typescript-sdk": "^3.14.0", "@safe-global/safe-modules-deployments": "^1.2.0", "@sentry/react": "^7.91.0", "@spindl-xyz/attribution-lite": "^1.4.0", "@tkey-mpc/common-types": "^8.2.2", "@truffle/hdwallet-provider": "^2.1.4", - "@walletconnect/utils": "^2.11.0", - "@walletconnect/web3wallet": "^1.10.0", + "@walletconnect/utils": "^2.11.1", + "@walletconnect/web3wallet": "^1.10.1", "@web3-onboard/coinbase": "^2.2.6", "@web3-onboard/core": "^2.21.2", "@web3-onboard/injected-wallets": "^2.10.7", @@ -118,7 +119,7 @@ "@types/react-gtm-module": "^2.0.1", "@types/semver": "^7.3.10", "@typescript-eslint/eslint-plugin": "^5.47.1", - "@walletconnect/types": "^2.11.0", + "@walletconnect/types": "^2.11.1", "cross-env": "^7.0.3", "cypress": "^12.15.0", "cypress-file-upload": "^5.0.8", diff --git a/yarn.lock b/yarn.lock index e7dfb5e69a..ec3a888815 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5749,6 +5749,29 @@ lodash.isequal "4.5.0" uint8arrays "^3.1.0" +"@walletconnect/core@2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.11.1.tgz#da2be26b8b6514c74f06dc9a5ffb450bdec3456d" + integrity sha512-T57Vd7YdbHPsy3tthBuwrhaZNafN0+PqjISFRNeJy/bsKdXxpJg2hGSARuOTpCO7V6VcaatqlaSMuG3DrnG5rA== + dependencies: + "@walletconnect/heartbeat" "1.2.1" + "@walletconnect/jsonrpc-provider" "1.0.13" + "@walletconnect/jsonrpc-types" "1.0.3" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/jsonrpc-ws-connection" "1.0.14" + "@walletconnect/keyvaluestorage" "^1.1.1" + "@walletconnect/logger" "^2.0.1" + "@walletconnect/relay-api" "^1.0.9" + "@walletconnect/relay-auth" "^1.0.4" + "@walletconnect/safe-json" "^1.0.2" + "@walletconnect/time" "^1.0.2" + "@walletconnect/types" "2.11.1" + "@walletconnect/utils" "2.11.1" + events "^3.3.0" + isomorphic-unfetch "3.1.0" + lodash.isequal "4.5.0" + uint8arrays "^3.1.0" + "@walletconnect/environment@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@walletconnect/environment/-/environment-1.0.1.tgz#1d7f82f0009ab821a2ba5ad5e5a7b8ae3b214cd7" @@ -5919,6 +5942,21 @@ "@walletconnect/utils" "2.11.0" events "^3.3.0" +"@walletconnect/sign-client@2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.11.1.tgz#c073b8d2d594e792bb783d36c8b021bd37a9d4f6" + integrity sha512-s3oKSx6/F5X2WmkV1jfJImBFACf9Km5HpTb+n5q+mobJVpUQw/clvoVyIrNNppLhm1V1S/ylHXh0qCrDppDpCA== + dependencies: + "@walletconnect/core" "2.11.1" + "@walletconnect/events" "^1.0.1" + "@walletconnect/heartbeat" "1.2.1" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/logger" "^2.0.1" + "@walletconnect/time" "^1.0.2" + "@walletconnect/types" "2.11.1" + "@walletconnect/utils" "2.11.1" + events "^3.3.0" + "@walletconnect/time@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@walletconnect/time/-/time-1.0.2.tgz#6c5888b835750ecb4299d28eecc5e72c6d336523" @@ -5926,7 +5964,7 @@ dependencies: tslib "1.14.1" -"@walletconnect/types@2.11.0", "@walletconnect/types@^2.11.0": +"@walletconnect/types@2.11.0": version "2.11.0" resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.11.0.tgz#474a009c56faa9ef4063b76ed84415c801dc9f1e" integrity sha512-AB5b1lrEbCGHxqS2vqfCkIoODieH+ZAUp9rA1O2ftrhnqDJiJK983Df87JhYhECsQUBHHfALphA8ydER0q+9sw== @@ -5938,6 +5976,18 @@ "@walletconnect/logger" "^2.0.1" events "^3.3.0" +"@walletconnect/types@2.11.1", "@walletconnect/types@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.11.1.tgz#4f705b43ddc286b69eb9bf91bb6e9496d20de0e3" + integrity sha512-UbdbX+d6MOK0AXKxt5imV3KvAcLVpZUHylaRDIP5ffwVylM/p4DHnKppil1Qq5N+IGDr3RsUwLGFkKjqsQYRKw== + dependencies: + "@walletconnect/events" "^1.0.1" + "@walletconnect/heartbeat" "1.2.1" + "@walletconnect/jsonrpc-types" "1.0.3" + "@walletconnect/keyvaluestorage" "^1.1.1" + "@walletconnect/logger" "^2.0.1" + events "^3.3.0" + "@walletconnect/types@~1.8.0": version "1.8.0" resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-1.8.0.tgz#3f5e85b2d6b149337f727ab8a71b8471d8d9a195" @@ -5958,7 +6008,7 @@ "@walletconnect/utils" "2.11.0" events "^3.3.0" -"@walletconnect/utils@2.11.0", "@walletconnect/utils@^2.10.1", "@walletconnect/utils@^2.11.0": +"@walletconnect/utils@2.11.0", "@walletconnect/utils@^2.10.1": version "2.11.0" resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.11.0.tgz#31c95151c823022077883dda61800cdea71879b7" integrity sha512-hxkHPlTlDQILHfIKXlmzgNJau/YcSBC3XHUSuZuKZbNEw3duFT6h6pm3HT/1+j1a22IG05WDsNBuTCRkwss+BQ== @@ -5978,19 +6028,39 @@ query-string "7.1.3" uint8arrays "^3.1.0" -"@walletconnect/web3wallet@^1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@walletconnect/web3wallet/-/web3wallet-1.10.0.tgz#5a7f181bbea213e8fb5961900a0069e58da36d18" - integrity sha512-JyaYdnBKL1hVpsE5hpjZhohpX1Ak2mwai3GYr8nI1FfK8Z3IPIh+4ImeDMnEao1cQFoItQNompp5y5C7WlI69A== +"@walletconnect/utils@2.11.1", "@walletconnect/utils@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.11.1.tgz#56116d9c410c6f2ae8d562017cf6876cccb366f1" + integrity sha512-wRFDHN86dZ05mCET1H3912odIeQa8j7cZKxl1FlWRpV2YsILj9HCYSX6Uq2brwO02Kv2vryke44G1r8XI/LViA== + dependencies: + "@stablelib/chacha20poly1305" "1.0.1" + "@stablelib/hkdf" "1.0.1" + "@stablelib/random" "^1.0.2" + "@stablelib/sha256" "1.0.1" + "@stablelib/x25519" "^1.0.3" + "@walletconnect/relay-api" "^1.0.9" + "@walletconnect/safe-json" "^1.0.2" + "@walletconnect/time" "^1.0.2" + "@walletconnect/types" "2.11.1" + "@walletconnect/window-getters" "^1.0.1" + "@walletconnect/window-metadata" "^1.0.1" + detect-browser "5.3.0" + query-string "7.1.3" + uint8arrays "^3.1.0" + +"@walletconnect/web3wallet@^1.10.1": + version "1.10.1" + resolved "https://registry.yarnpkg.com/@walletconnect/web3wallet/-/web3wallet-1.10.1.tgz#62271183b72b3e0ec47c683bf021549d47225f39" + integrity sha512-lXyfljLLQ/76F5ftgaKaIoPfj/R2Mi2Tv2JnIXNonlIlAITdghYVby+xYbh4b+0yldf8fr8lqxFuHBuVoWhMjw== dependencies: "@walletconnect/auth-client" "2.1.2" - "@walletconnect/core" "2.11.0" + "@walletconnect/core" "2.11.1" "@walletconnect/jsonrpc-provider" "1.0.13" "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "2.0.1" - "@walletconnect/sign-client" "2.11.0" - "@walletconnect/types" "2.11.0" - "@walletconnect/utils" "2.11.0" + "@walletconnect/sign-client" "2.11.1" + "@walletconnect/types" "2.11.1" + "@walletconnect/utils" "2.11.1" "@walletconnect/window-getters@^1.0.1": version "1.0.1" From 9887d9646bc5ab184064bb15faad17319ce37ee5 Mon Sep 17 00:00:00 2001 From: Michael <30682308+mike10ca@users.noreply.github.com> Date: Wed, 7 Feb 2024 15:09:20 +0100 Subject: [PATCH 28/38] tests: add sidebar tests -p3 (#3211) --- cypress/e2e/pages/assets.pages.js | 11 +++++ cypress/e2e/pages/navigation.page.js | 3 ++ cypress/e2e/pages/sidebar.pages.js | 41 +++++++++------- cypress/e2e/regression/sidebar_2.cy.js | 21 +++++++- cypress/e2e/regression/sidebar_nonowner.cy.js | 29 +++++++++++ cypress/support/constants.js | 6 +++ cypress/support/localstorage_data.js | 49 ++++++++++++++++++- .../balances/CurrencySelect/index.tsx | 3 +- .../sidebar/PendingActions/index.tsx | 2 + 9 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 cypress/e2e/regression/sidebar_nonowner.cy.js diff --git a/cypress/e2e/pages/assets.pages.js b/cypress/e2e/pages/assets.pages.js index 2509eefb30..444e4f58ab 100644 --- a/cypress/e2e/pages/assets.pages.js +++ b/cypress/e2e/pages/assets.pages.js @@ -17,6 +17,8 @@ const hiddenTokenSaveBtn = 'span[data-track="assets: Save hide dialog"]' const hiddenTokenCancelBtn = 'span[data-track="assets: Cancel hide dialog"]' const hiddenTokenDeselectAllBtn = 'span[data-track="assets: Deselect all hide dialog"]' const hiddenTokenIcon = 'svg[data-testid="VisibilityOffOutlinedIcon"]' +const currencySelector = '[data-testid="currency-selector"]' +const currencyItem = '[data-testid="currency-item"]' const hideTokenDefaultString = 'Hide tokens' const assetNameSortBtnStr = 'Asset' @@ -91,6 +93,15 @@ export const currentcyGnosisFormat = '< 0.00001 GNO' export const currencyOx = /^0x$/ export const currentcyOxFormat = '1.003 ZRX' +function clickOnCurrencySelector() { + cy.get(currencySelector).click() +} + +export function changeCurrency(currency) { + clickOnCurrencySelector() + cy.get(currencyItem).contains(currency).click() +} + export function clickOnSendBtn(index) { cy.get('button') .contains(sendBtnStr) diff --git a/cypress/e2e/pages/navigation.page.js b/cypress/e2e/pages/navigation.page.js index 8748d2a581..7b176e398e 100644 --- a/cypress/e2e/pages/navigation.page.js +++ b/cypress/e2e/pages/navigation.page.js @@ -9,6 +9,9 @@ const sentinelStart = 'div[data-testid="sentinelStart"]' const disconnectBtnStr = 'Disconnect' const notConnectedStatus = 'Connect' +export function verifyTxBtnStatus(status) { + cy.get(newTxBtn).should(status) +} export function clickOnSideNavigation(option) { cy.get(option).should('exist').click() } diff --git a/cypress/e2e/pages/sidebar.pages.js b/cypress/e2e/pages/sidebar.pages.js index 9604103e55..5dd6d2bbf5 100644 --- a/cypress/e2e/pages/sidebar.pages.js +++ b/cypress/e2e/pages/sidebar.pages.js @@ -15,7 +15,7 @@ const sideBarListItem = '[data-testid="sidebar-list-item"]' const sideBarListItemWhatsNew = '[data-testid="list-item-whats-new"]' const sideBarListItemNeedHelp = '[data-testid="list-item-need-help"]' const sideSafeListItem = '[data-testid="safe-list-item"]' -const sidebarSafeHeader = '[data-testid="sidebar-safe-header"]' +const sidebarSafeHeader = '[data-testid="safe-header-info"]' const sidebarSafeContainer = '[data-testid="sidebar-safe-container"]' const safeItemOptionsBtn = '[data-testid="safe-options-btn"]' const safeItemOptionsRenameBtn = '[data-testid="rename-btn"]' @@ -26,6 +26,8 @@ const cancelBtn = '[data-testid="cancel-btn"]' const deleteBtn = '[data-testid="delete-btn"]' const readOnlyVisibility = '[data-testid="read-only-visibility"]' const currencySection = '[data-testid="currency-section"]' +const missingSignatureInfo = '[data-testid="missing-signature-info"]' +const queuedTxInfo = '[data-testid="queued-tx-info"]' export const addedSafesGnosis = ['0x17b3...98C8', '0x11A6...F1BB', '0xB8d7...642A'] export const addedSafesSepolia = ['0x6d0b...6dC1', '0x5912...fFdb', '0x0637...708e', '0xD157...DE9a'] @@ -120,27 +122,26 @@ export function verifySafesByNetwork(netwrok, safes) { }) } +function getSafeItemByName(name) { + return cy.get(sidebarSafeContainer).find(sideSafeListItem).contains(name).parents('li') +} + export function verifySafeReadOnlyState(safe) { - cy.get(sidebarSafeContainer).within(() => { - cy.get(sideSafeListItem) - .contains(safe) - .parents('li') - .within(() => { - cy.get(readOnlyVisibility).should('exist') - }) - }) + getSafeItemByName(safe).find(readOnlyVisibility).should('exist') +} + +export function verifyMissingSignature(safe) { + getSafeItemByName(safe).find(missingSignatureInfo).should('exist') +} + +export function verifyQueuedTx(safe) { + return getSafeItemByName(safe).find(queuedTxInfo).should('exist') } function clickOnSafeItemOptionsBtn(name) { - cy.get(sidebarSafeContainer).within(() => { - cy.get(sideSafeListItem) - .contains(name) - .parents('li') - .within(() => { - cy.get(safeItemOptionsBtn).click() - }) - }) + getSafeItemByName(name).find(safeItemOptionsBtn).click() } + export function renameSafeItem(oldName, newName) { clickOnSafeItemOptionsBtn(oldName) clickOnRenameBtn() @@ -188,3 +189,9 @@ export function clickOnSaveBtn() { function verifyModalRemoved() { main.verifyElementsCount(modal.modalTitle, 0) } + +export function checkCurrencyInHeader(currency) { + cy.get(sidebarSafeHeader).within(() => { + cy.get(currencySection).contains(currency) + }) +} diff --git a/cypress/e2e/regression/sidebar_2.cy.js b/cypress/e2e/regression/sidebar_2.cy.js index daef0b5844..fef7fab98c 100644 --- a/cypress/e2e/regression/sidebar_2.cy.js +++ b/cypress/e2e/regression/sidebar_2.cy.js @@ -2,14 +2,16 @@ import * as constants from '../../support/constants' import * as main from '../pages/main.page' import * as sideBar from '../pages/sidebar.pages' import * as ls from '../../support/localstorage_data.js' +import * as assets from '../pages/assets.pages.js' const newSafeName = 'Added safe 3' -const oldSafeName = 'Added safe 2' +const oldSafeName = 'Added safe 900' const staticSafe100 = 'Added safe 100' +const staticSafe200 = 'Added safe 200' describe('Sidebar added sidebar tests', () => { beforeEach(() => { - cy.visit(constants.homeUrl + constants.SEPOLIA_TEST_SAFE_13) + cy.visit(constants.BALANCE_URL + constants.SEPOLIA_TEST_SAFE_13) cy.wait(2000) cy.clearLocalStorage() main.acceptCookies() @@ -45,4 +47,19 @@ describe('Sidebar added sidebar tests', () => { sideBar.openSidebar() sideBar.verifySafeReadOnlyState(staticSafe100) }) + + it('Verify Fiat currency changes when edited in the assets tab', () => { + assets.changeCurrency(constants.currencies.cad) + sideBar.checkCurrencyInHeader(constants.currencies.cad) + }) + + it('Verify "wallet" tag counter if the safe has tx ready for execution', () => { + sideBar.openSidebar() + sideBar.verifyMissingSignature(staticSafe200) + }) + + it('Verify "Wallet" tag counter only shows for owners', () => { + sideBar.openSidebar() + sideBar.verifyQueuedTx(staticSafe200) + }) }) diff --git a/cypress/e2e/regression/sidebar_nonowner.cy.js b/cypress/e2e/regression/sidebar_nonowner.cy.js new file mode 100644 index 0000000000..f88c2faedf --- /dev/null +++ b/cypress/e2e/regression/sidebar_nonowner.cy.js @@ -0,0 +1,29 @@ +import * as constants from '../../support/constants.js' +import * as main from '../pages/main.page.js' +import * as sideBar from '../pages/sidebar.pages.js' +import * as navigation from '../pages/navigation.page.js' +import * as ls from '../../support/localstorage_data.js' + +const addedOwner = 'Added owner' +const addedNonowner = 'Added non-owner' + +describe('Sidebar non-owner tests', () => { + beforeEach(() => { + cy.visit(constants.homeUrl + constants.SEPOLIA_TEST_SAFE_17_SIDEBAR_NONOWNER) + cy.wait(2000) + cy.clearLocalStorage() + main.acceptCookies() + main.addToLocalStorage(constants.localStorageKeys.SAFE_v2__addedSafes, ls.addedSafes.set3) + main.addToLocalStorage(constants.localStorageKeys.SAFE_v2__addressBook, ls.addressBookData.addedSafes) + }) + + it('Verify New Transaction button enabled for users with Spending limits allowed', () => { + navigation.verifyTxBtnStatus(constants.enabledStates.enabled) + }) + + it('Verify tag counting queue tx show for owners and non-owners', () => { + sideBar.openSidebar() + sideBar.verifyQueuedTx(addedOwner).contains(2) + sideBar.verifyQueuedTx(addedNonowner).contains(2) + }) +}) diff --git a/cypress/support/constants.js b/cypress/support/constants.js index f83fb7cfc0..ffd84749aa 100644 --- a/cypress/support/constants.js +++ b/cypress/support/constants.js @@ -29,6 +29,7 @@ export const SEPOLIA_TEST_SAFE_14 = 'sep:0xC23e061252BFc7967203D054136d8fA7c7df2 // SAFE 15 is a safe with native tokens but the automation user is not its owner export const SEPOLIA_TEST_SAFE_15_TOKEN = 'sep:0xfC0A7ac73Fde7547ac0792Cca1D8A50CE0AFC4Df' export const SEPOLIA_TEST_SAFE_16_CREATE_TX = 'sep:0xc2F3645bfd395516d1a18CA6ad9298299d328C01' +export const SEPOLIA_TEST_SAFE_17_SIDEBAR_NONOWNER = 'sep:0x10B45a24640E2170B6AA63ea3A289D723a0C9cba' export const SEPOLIA_CONTRACT_SHORT = '0x11AB...34aF' export const SEPOLIA_RECIPIENT_ADDR_SHORT = '0x4DD4...7bde' export const GNO_TEST_SAFE = 'gno:0xB8d760a90a5ed54D3c2b3EFC231277e99188642A' @@ -134,6 +135,11 @@ export const tokenAbbreviation = { link: 'LINK', } +export const currencies = { + cad: 'CAD', + aud: 'AUD', +} + export const appNames = { walletConnect: 'walletconnect', customContract: 'compose custom contract', diff --git a/cypress/support/localstorage_data.js b/cypress/support/localstorage_data.js index 62e6a1c879..96c80044e6 100644 --- a/cypress/support/localstorage_data.js +++ b/cypress/support/localstorage_data.js @@ -319,9 +319,14 @@ export const addressBookData = { addedSafes: { 100: { '0x17b34aEf1428A358bA2eA360a098b8A3BEb698C8': 'Added safe 1', - '0x11A6B41322C57Bd0e56cEe06abB11A1E5c1FF1BB': 'Added safe 2', + '0x11A6B41322C57Bd0e56cEe06abB11A1E5c1FF1BB': 'Added safe 900', '0xB8d760a90a5ed54D3c2b3EFC231277e99188642A': 'Added safe 100', }, + 11155111: { + '0x0A0EEb6fBCc7c82259E548Fc4617175A357b3e71': 'Added safe 200', + '0xF21445699e91aC6F2EeeAF1a19510AC4197e59aB': 'Added owner', + '0x9E6DAfe829431e1892EcF8461FDAd02665170c31': 'Added non-owner', + }, }, } @@ -507,6 +512,22 @@ export const addedSafes = { threshold: 1, ethBalance: '0', }, + '0x0A0EEb6fBCc7c82259E548Fc4617175A357b3e71': { + owners: [ + { + value: '0x8aEf2f5c3F17261F6F1C4dA058D022BE92776af8', + name: null, + logoUri: null, + }, + { + value: '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED', + name: null, + logoUri: null, + }, + ], + threshold: 2, + ethBalance: '0', + }, }, 100: { '0x17b34aEf1428A358bA2eA360a098b8A3BEb698C8': { @@ -526,6 +547,32 @@ export const addedSafes = { }, }, }, + set3: { + 11155111: { + '0xF21445699e91aC6F2EeeAF1a19510AC4197e59aB': { + owners: [ + { + value: '0xC16Db0251654C0a72E91B190d81eAD367d2C6fED', + name: null, + logoUri: null, + }, + ], + threshold: 2, + ethBalance: '0', + }, + '0x9E6DAfe829431e1892EcF8461FDAd02665170c31': { + owners: [ + { + value: '0x96D4c6fFC338912322813a77655fCC926b9A5aC5', + name: null, + logoUri: null, + }, + ], + threshold: 2, + ethBalance: '0', + }, + }, + }, } export const pinnedApps = { diff --git a/src/components/balances/CurrencySelect/index.tsx b/src/components/balances/CurrencySelect/index.tsx index bfec864f59..04d9af8e93 100644 --- a/src/components/balances/CurrencySelect/index.tsx +++ b/src/components/balances/CurrencySelect/index.tsx @@ -34,6 +34,7 @@ const CurrencySelect = (): ReactElement => { Currency