From 4c6f4a0c0bfb40132109c3e83dda8cb2dac0805e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Thu, 11 Apr 2024 13:44:34 +0200 Subject: [PATCH 01/20] chore: change CY version to 13.3.2 --- client/package.json | 2 +- client/yarn.lock | 52 ++++++++++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/client/package.json b/client/package.json index c5b734185c..dc1342ae86 100644 --- a/client/package.json +++ b/client/package.json @@ -113,7 +113,7 @@ "vite-plugin-html-config": "^1.0.11" }, "resolutions": { - "**/cypress": "12.17.3" + "**/cypress": "13.3.2" }, "engines": { "node": ">= 16.13.0 <17.0.0" diff --git a/client/yarn.lock b/client/yarn.lock index 68e34b8693..39e10466fa 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -958,10 +958,10 @@ js-yaml "4.1.0" nyc "15.1.0" -"@cypress/request@^2.88.11": - version "2.88.12" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.12.tgz#ba4911431738494a85e93fb04498cb38bc55d590" - integrity sha512-tOn+0mDZxASFM+cuAP9szGUGPI1HwWVSvdzm7V4cCsPdFTx6qMj29CwaQmRAMIEhORIUBFBsYROYJcveK4uOjA== +"@cypress/request@^3.0.0": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.1.tgz#72d7d5425236a2413bd3d8bb66d02d9dc3168960" + integrity sha512-TWivJlJi8ZDx2wGOw1dbLuHJKUYX7bWySw377nlnGOW3hP9/MUKIsEdXT/YngWxVdgNCHRBmFlBipE+5/2ZZlQ== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -976,7 +976,7 @@ json-stringify-safe "~5.0.1" mime-types "~2.1.19" performance-now "^2.1.0" - qs "~6.10.3" + qs "6.10.4" safe-buffer "^5.1.2" tough-cookie "^4.1.3" tunnel-agent "^0.6.0" @@ -3179,10 +3179,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== -"@types/node@^16.18.39": - version "16.18.78" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.18.78.tgz#3d97264128712f2eb59f1f8456bcfc5d56d8105c" - integrity sha512-2poPMDdsGfvhcLmgJZ85QrIfN6z3PijYRMiV0FWIEUiQW/t/lzH7BEm4vN+HMhjZXbtIKssMcAxTcgu4Rm83YA== +"@types/node@^18.17.5": + version "18.19.31" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd" + integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA== + dependencies: + undici-types "~5.26.4" "@types/prettier@^2.6.1": version "2.7.3" @@ -5574,14 +5576,14 @@ cypress-wait-until@^2.0.1: resolved "https://registry.yarnpkg.com/cypress-wait-until/-/cypress-wait-until-2.0.1.tgz#69c575c7207d83e4ae023e2aaecf2b66148c9fc0" integrity sha512-+IyVnYNiaX1+C+V/LazrJWAi/CqiwfNoRSrFviECQEyolW1gDRy765PZosL2alSSGK8V10Y7BGfOQyZUDgmnjQ== -cypress@*, cypress@12.17.3: - version "12.17.3" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-12.17.3.tgz#1e2b19037236fc60e4a4b3a14f0846be17a1fc0e" - integrity sha512-/R4+xdIDjUSLYkiQfwJd630S81KIgicmQOLXotFxVXkl+eTeVO+3bHXxdi5KBh/OgC33HWN33kHX+0tQR/ZWpg== +cypress@*, cypress@12.17.3, cypress@13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.3.2.tgz#b4baa64ce37d7874f6bdd8efbc28a9c722c0686f" + integrity sha512-ArLmZObcLC+xxCp7zJZZbhby9FUf5CueLej9dUM4+5j37FTS4iMSgHxQLDu01PydFUvDXcNoIVRCYrHHxD7Ybg== dependencies: - "@cypress/request" "^2.88.11" + "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" - "@types/node" "^16.18.39" + "@types/node" "^18.17.5" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" @@ -5614,6 +5616,7 @@ cypress@*, cypress@12.17.3: minimist "^1.2.8" ospath "^1.2.2" pretty-bytes "^5.6.0" + process "^0.11.10" proxy-from-env "1.0.0" request-progress "^3.0.0" semver "^7.5.3" @@ -11081,6 +11084,13 @@ qrcode@1.5.3, qrcode@^1.5.1: pngjs "^5.0.0" yargs "^15.3.1" +qs@6.10.4: + version "6.10.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.4.tgz#6a3003755add91c0ec9eacdc5f878b034e73f9e7" + integrity sha512-OQiU+C+Ds5qiH91qh/mg0w+8nwQuLjM4F4M/PbmhDOoYehPh+Fb0bDjtR1sOvy7YKxvj28Y/M0PhP5uVX0kB+g== + dependencies: + side-channel "^1.0.4" + qs@6.11.0: version "6.11.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" @@ -11095,13 +11105,6 @@ qs@^6.10.3: dependencies: side-channel "^1.0.4" -qs@~6.10.3: - version "6.10.5" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.5.tgz#974715920a80ff6a262264acd2c7e6c2a53282b4" - integrity sha512-O5RlPh0VFtR78y79rgcgKK4wbAI0C5zGVLztOIdpWX6ep368q5Hv6XRxDvXuZ9q3C6v+e3n8UfZZJw7IIG27eQ== - dependencies: - side-channel "^1.0.4" - query-string@7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" @@ -12739,6 +12742,11 @@ underscore@^1.13.6: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" From fdcb4dd09588bc029a1b45f7419b21109c99cf2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Fri, 12 Apr 2024 12:27:27 +0200 Subject: [PATCH 02/20] chore: change CY version to 13.7.3 --- client/package.json | 2 +- client/yarn.lock | 29 ++++++++--------------------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/client/package.json b/client/package.json index dc1342ae86..51c963c810 100644 --- a/client/package.json +++ b/client/package.json @@ -113,7 +113,7 @@ "vite-plugin-html-config": "^1.0.11" }, "resolutions": { - "**/cypress": "13.3.2" + "**/cypress": "13.7.3" }, "engines": { "node": ">= 16.13.0 <17.0.0" diff --git a/client/yarn.lock b/client/yarn.lock index 39e10466fa..1cb584c600 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -3179,13 +3179,6 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== -"@types/node@^18.17.5": - version "18.19.31" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.31.tgz#b7d4a00f7cb826b60a543cebdbda5d189aaecdcd" - integrity sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA== - dependencies: - undici-types "~5.26.4" - "@types/prettier@^2.6.1": version "2.7.3" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.3.tgz#3e51a17e291d01d17d3fc61422015a933af7a08f" @@ -4932,7 +4925,7 @@ buffer@6.0.3, buffer@^6.0.3, buffer@~6.0.3: base64-js "^1.3.1" ieee754 "^1.2.1" -buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: +buffer@^5.2.1, buffer@^5.5.0, buffer@^5.7.1: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== @@ -5576,20 +5569,19 @@ cypress-wait-until@^2.0.1: resolved "https://registry.yarnpkg.com/cypress-wait-until/-/cypress-wait-until-2.0.1.tgz#69c575c7207d83e4ae023e2aaecf2b66148c9fc0" integrity sha512-+IyVnYNiaX1+C+V/LazrJWAi/CqiwfNoRSrFviECQEyolW1gDRy765PZosL2alSSGK8V10Y7BGfOQyZUDgmnjQ== -cypress@*, cypress@12.17.3, cypress@13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.3.2.tgz#b4baa64ce37d7874f6bdd8efbc28a9c722c0686f" - integrity sha512-ArLmZObcLC+xxCp7zJZZbhby9FUf5CueLej9dUM4+5j37FTS4iMSgHxQLDu01PydFUvDXcNoIVRCYrHHxD7Ybg== +cypress@*, cypress@12.17.3, cypress@13.7.3: + version "13.7.3" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.7.3.tgz#3e7dcd32e007676a6c8e972293c50d6ef329d991" + integrity sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" - "@types/node" "^18.17.5" "@types/sinonjs__fake-timers" "8.1.1" "@types/sizzle" "^2.3.2" arch "^2.2.0" blob-util "^2.0.2" bluebird "^3.7.2" - buffer "^5.6.0" + buffer "^5.7.1" cachedir "^2.3.0" chalk "^4.1.0" check-more-types "^2.24.0" @@ -5607,7 +5599,7 @@ cypress@*, cypress@12.17.3, cypress@13.3.2: figures "^3.2.0" fs-extra "^9.1.0" getos "^3.2.1" - is-ci "^3.0.0" + is-ci "^3.0.1" is-installed-globally "~0.4.0" lazy-ass "^1.6.0" listr2 "^3.8.3" @@ -8451,7 +8443,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-ci@^3.0.0: +is-ci@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-3.0.1.tgz#db6ecbed1bd659c43dac0f45661e7674103d1867" integrity sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ== @@ -12742,11 +12734,6 @@ underscore@^1.13.6: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.6.tgz#04786a1f589dc6c09f761fc5f45b89e935136441" integrity sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A== -undici-types@~5.26.4: - version "5.26.5" - resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" - integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" From ec41ee525e74d6a1d7607ae219841c847f9f9d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Fri, 12 Apr 2024 22:43:08 +0200 Subject: [PATCH 03/20] test: debug, remove E2E --- client/cypress/e2e/allocationRewardsBox.cy.ts | 479 ------------ client/cypress/e2e/earn.cy.ts | 275 ------- client/cypress/e2e/layout.cy.ts | 126 --- client/cypress/e2e/metrics.cy.ts | 126 --- client/cypress/e2e/onboarding.cy.ts | 380 --------- client/cypress/e2e/patronMode.cy.ts | 724 ------------------ client/cypress/e2e/project.cy.ts | 181 ----- client/cypress/e2e/projects.cy.ts | 239 ------ client/cypress/e2e/projectsArchive.cy.ts | 111 --- client/cypress/e2e/rewardsCalculator.cy.ts | 252 ------ client/cypress/e2e/routes.cy.ts | 42 - client/cypress/e2e/settings.cy.ts | 188 ----- 12 files changed, 3123 deletions(-) delete mode 100644 client/cypress/e2e/allocationRewardsBox.cy.ts delete mode 100644 client/cypress/e2e/earn.cy.ts delete mode 100644 client/cypress/e2e/layout.cy.ts delete mode 100644 client/cypress/e2e/metrics.cy.ts delete mode 100644 client/cypress/e2e/onboarding.cy.ts delete mode 100644 client/cypress/e2e/patronMode.cy.ts delete mode 100644 client/cypress/e2e/project.cy.ts delete mode 100644 client/cypress/e2e/projects.cy.ts delete mode 100644 client/cypress/e2e/projectsArchive.cy.ts delete mode 100644 client/cypress/e2e/rewardsCalculator.cy.ts delete mode 100644 client/cypress/e2e/routes.cy.ts delete mode 100644 client/cypress/e2e/settings.cy.ts diff --git a/client/cypress/e2e/allocationRewardsBox.cy.ts b/client/cypress/e2e/allocationRewardsBox.cy.ts deleted file mode 100644 index 1259263a5e..0000000000 --- a/client/cypress/e2e/allocationRewardsBox.cy.ts +++ /dev/null @@ -1,479 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import chaiColors from 'chai-colors'; - -import { visitWithLoader, mockCoinPricesServer, connectWallet } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -chai.use(chaiColors); - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe( - `allocation rewards box (disabled): ${device}`, - { viewportHeight, viewportWidth }, - () => { - beforeEach(() => { - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.allocation.absolute); - }); - - it('is visible', () => { - cy.get('[data-test=AllocationRewardsBox]').should('be.visible'); - }); - - it('has each field with value 0 ETH', () => { - cy.get('[data-test=AllocationRewardsBox__title]').invoke('text').should('eq', '0 ETH'); - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '0 ETH'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '0 ETH'); - }); - - it('shows "No rewards yet" message below rewards value', () => { - cy.get('[data-test=AllocationRewardsBox__subtitle]') - .invoke('text') - .should('eq', 'No rewards yet'); - }); - - it('Clicking on `Donate` label or value doesn`t open modal to editing value', () => { - cy.get('[data-test=AllocationRewardsBox__section--0]').click(); - cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); - }); - - it('Clicking on `Personal` label or value doesn`t open modal to editing value', () => { - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); - }); - - it('slider is visible', () => { - cy.get('[data-test=AllocationRewardsBox__Slider]').should('be.visible'); - }); - - it('slider thumb exists but isn`t visible', () => { - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('exist'); - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('not.be.visible'); - }); - - it('slider track should be filled in 50%', () => { - cy.get('[data-test=AllocationRewardsBox__Slider]').then($sliderEl => { - const { width } = $sliderEl[0].getBoundingClientRect(); - - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( - 'have.css', - 'width', - `${width / 2}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]') - .then($el => $el.css('background-color')) - .should('be.colored', '#9ea39e'); - - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( - 'have.css', - 'width', - `${width / 2}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]') - .then($el => $el.css('background-color')) - .should('be.colored', '#cdd1cd'); - }); - }); - - it('Clicking on `Personal` label or value doesn`t open modal to editing value', () => { - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); - }); - - it('Clicking on `Personal` label or value doesn`t open modal to editing value', () => { - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); - }); - }, - ); - - describe(`allocation rewards box (enabled): ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.allocation.absolute); - cy.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget: '10000000000' } }); - connectWallet(true, false); - }); - - it('is visible', () => { - cy.get('[data-test=AllocationRewardsBox]').should('be.visible'); - }); - - it('shows "Available now" message below rewards value', () => { - cy.get('[data-test=AllocationRewardsBox__subtitle]') - .invoke('text') - .should('eq', 'Available now'); - }); - - it('user has 10 GWEI rewards', () => { - cy.get('[data-test=AllocationRewardsBox__title]').invoke('text').should('eq', '10 GWEI'); - }); - - it('slider thumb exists and is visible', () => { - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('exist'); - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('be.visible'); - }); - - it('Clicking on `Donate` label or value opens modal to editing value', () => { - cy.get('[data-test=AllocationRewardsBox__section--0]').click(); - cy.get('[data-test=ModalAllocationValuesEdit]').should('exist').should('be.visible'); - }); - - it('Clicking on `Personal` label or value opens modal to editing value', () => { - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - cy.get('[data-test=ModalAllocationValuesEdit]').should('exist').should('be.visible'); - }); - - it('user can split the value by using slider from 0/100 to 50/50 and then from 50/50 to 100/0', () => { - cy.get('[data-test=AllocationRewardsBox__title]').invoke('text').should('eq', '10 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '0 ETH'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '10 GWEI'); - - cy.get('[data-test=AllocationRewardsBox__Slider]').then($sliderEl => { - const { width: sliderElWidth } = $sliderEl[0].getBoundingClientRect(); - - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - // track 0 is hidden under the thumb - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( - 'have.css', - 'width', - `${sliderButtonDimensions.width}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( - 'have.css', - 'width', - `${sliderElWidth}px`, - ); - - const pageX0 = sliderButtonDimensions.left; - const pageX50 = - sliderButtonDimensions.left - sliderButtonDimensions.width / 2 + sliderElWidth / 2; - const pageX100 = - sliderButtonDimensions.left - sliderButtonDimensions.width / 2 + sliderElWidth; - - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]') - .trigger('mousedown', { - pageX: pageX0, - }) - .trigger('mousemove', { - pageX: pageX50, - }) - .trigger('mouseup', { - pageX: pageX50, - }); - - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( - 'have.css', - 'width', - `${(sliderButtonDimensions.width + sliderElWidth) / 2}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]') - .then($el => $el.css('background-color')) - .should('be.colored', '#2d9b87'); - - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( - 'have.css', - 'width', - `${(sliderButtonDimensions.width + sliderElWidth) / 2}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]') - .then($el => $el.css('background-color')) - .should('be.colored', '#ff9601'); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '5 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '5 GWEI'); - - cy.get('[data-test=AllocationRewardsBox__Slider__thumb]') - .trigger('mousedown', { - pageX: pageX50, - }) - .trigger('mousemove', { - pageX: pageX100, - }) - .trigger('mouseup', { - pageX: pageX100, - }); - - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( - 'have.css', - 'width', - `${sliderElWidth}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--0]') - .then($el => $el.css('background-color')) - .should('be.colored', '#2d9b87'); - - // track 1 is hidden under the thumb - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( - 'have.css', - 'width', - `${sliderButtonDimensions.width}px`, - ); - cy.get('[data-test=AllocationRewardsBox__Slider__track--1]') - .then($el => $el.css('background-color')) - .should('be.colored', '#ff9601'); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '10 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '0 ETH'); - }); - }); - }); - - it('user can change `Donate` value manually in modal', () => { - cy.get('[data-test=AllocationRewardsBox__section--0]').click(); - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Donate 0%'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('be.focused'); - cy.get('[data-test=AllocationInputs__InputText--crypto]') - .then($el => $el.css('border-color')) - .should('be.colored', '#2d9b87'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '0'); - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('not.be.focused'); - - // 10 GWEI - cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.00000001'); - cy.get('[data-test=AllocationInputs__InputText--crypto]') - .then($el => $el.css('border-color')) - .should('be.colored', '#2d9b87'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '10 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '0 ETH'); - - cy.get('[data-test=AllocationRewardsBox__section--0]').click(); - - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Donate 100%'); - - // 100 GWEI - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.0000001'); - cy.get('[data-test=AllocationInputs__InputText--crypto]') - .then($el => $el.css('border-color')) - .should('be.colored', '#FF6157'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); - - cy.get('[data-test=AllocationInputs__Button]').should('be.disabled'); - - cy.get('[data-test=AllocationInputs__InputText--crypto]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '0'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0'); - - // 50 % - cy.get('[data-test=AllocationInputs__InputText--percentage]').type('50'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.000000005'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '5 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '5 GWEI'); - - cy.get('[data-test=AllocationRewardsBox__section--0]').click(); - - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Donate 50%'); - - // 100 % - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').type('100'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '10 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '0 ETH'); - - cy.get('[data-test=AllocationRewardsBox__section--0]').click(); - - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Donate 100%'); - - // 1000 % - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').type('1000'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '10 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '0 ETH'); - }); - - it('user can change `Personal` value manually in modal', () => { - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Personal 100%'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('be.focused'); - cy.get('[data-test=AllocationInputs__InputText--crypto]') - .then($el => $el.css('border-color')) - .should('be.colored', '#2d9b87'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('not.be.focused'); - - // 10 GWEI - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.00000001'); - cy.get('[data-test=AllocationInputs__InputText--crypto]') - .then($el => $el.css('border-color')) - .should('be.colored', '#2d9b87'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '0 ETH'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '10 GWEI'); - - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Personal 100%'); - - // 100 GWEI - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.0000001'); - cy.get('[data-test=AllocationInputs__InputText--crypto]') - .then($el => $el.css('border-color')) - .should('be.colored', '#FF6157'); - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); - - cy.get('[data-test=AllocationInputs__Button]').should('be.disabled'); - - cy.get('[data-test=AllocationInputs__InputText--crypto]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '0'); - - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0'); - - // 50 % - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').type('50'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.000000005'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '5 GWEI'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '5 GWEI'); - - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Personal 50%'); - - // 100 % - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').type('100'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '0 ETH'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '10 GWEI'); - - cy.get('[data-test=AllocationRewardsBox__section--1]').click(); - - cy.get('[data-test=ModalAllocationValuesEdit__header]') - .invoke('text') - .should('eq', 'Personal 100%'); - - // 1000 % - cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); - cy.get('[data-test=AllocationInputs__InputText--percentage]').type('1000'); - cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); - - cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); - cy.get('[data-test=AllocationInputs__Button]').click(); - - cy.get('[data-test=AllocationRewardsBox__section__value--0]') - .invoke('text') - .should('eq', '0 ETH'); - cy.get('[data-test=AllocationRewardsBox__section__value--1]') - .invoke('text') - .should('eq', '10 GWEI'); - }); - }); -}); diff --git a/client/cypress/e2e/earn.cy.ts b/client/cypress/e2e/earn.cy.ts deleted file mode 100644 index c3bb07dc3a..0000000000 --- a/client/cypress/e2e/earn.cy.ts +++ /dev/null @@ -1,275 +0,0 @@ -import { visitWithLoader, mockCoinPricesServer } from 'cypress/utils/e2e'; -import { moveTime } from 'cypress/utils/moveTime'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -import Chainable = Cypress.Chainable; - -const connectWallet = (): Chainable => { - cy.intercept('GET', '/user/*/tos', { body: { accepted: true } }); - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').click(); - cy.acceptMetamaskAccess(); - return cy.switchToCypressWindow(); -}; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }, idx) => { - describe(`earn: ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - cy.disconnectMetamaskWalletFromAllDapps(); - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.earn.absolute); - }); - - it('renders "Locked balance" box', () => { - cy.get('[data-test=BoxGlmLock__BoxRounded]').should('be.visible'); - }); - - it('renders "Personal allocation" box', () => { - cy.get('[data-test=BoxPersonalAllocation]').should('be.visible'); - }); - - it('renders "History"', () => { - cy.get('[data-test=History]').should('be.visible'); - }); - - it('"Lock GLM" button is visible', () => { - cy.get('[data-test=BoxGlmLock__Button]').should('be.visible'); - }); - - it('"Lock GLM" button is disabled', () => { - cy.get('[data-test=BoxGlmLock__Button]').should('be.disabled'); - }); - - it('"Withdraw to wallet" button is visible', () => { - cy.get('[data-test=BoxPersonalAllocation__Button]').should('be.visible'); - }); - - it('"Withdraw to wallet" button is disabled', () => { - cy.get('[data-test=BoxPersonalAllocation__Button]').should('be.disabled'); - }); - - it('"Effective" section has tooltip', () => { - cy.get('[data-test=BoxGlmLock__Section--effective]').should('be.visible'); - }); - - if (!isDesktop) { - it('"Effective" section tooltip svg icon opens "TooltipEffectiveLockedBalance"', () => { - cy.get('[data-test=BoxGlmLock__Section--effective__Svg]').click(); - cy.get('[data-test=TooltipEffectiveLockedBalance]').should('be.visible'); - }); - } - - it('Wallet connected: "Lock GLM" / "Edit Locked GLM" button is active', () => { - connectWallet(); - cy.get('[data-test=BoxGlmLock__Button]').should('not.be.disabled'); - }); - - it('Wallet connected: "Lock GLM" / "Edit Locked GLM" button opens "ModalGlmLock"', () => { - connectWallet(); - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=ModalGlmLock]').should('be.visible'); - }); - - it('Wallet connected: "ModalGlmLock" has overflow', () => { - connectWallet(); - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=ModalGlmLock__overflow]').should('exist'); - }); - - it('Wallet connected: inputs allow to type multiple characters without focus problems', () => { - /** - * In EarnGlmLock there are multiple autofocus rules set. - * This test checks if user is still able to type without any autofocus disruption. - */ - connectWallet(); - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=ModalGlmLock]').should('be.visible'); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type('100'); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.value', '100'); - cy.get('[data-test=EarnGlmLockTabs__tab--1]').click(); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type('100'); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.value', '100'); - }); - - it('Wallet connected: "ModalGlmLock" - changing tabs keep focus on first input', () => { - connectWallet(); - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=ModalGlmLock]').should('be.visible'); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); - cy.get('[data-test=EarnGlmLockTabs__tab--1]').click(); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); - cy.get('[data-test=EarnGlmLockTabs__tab--0]').click(); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); - }); - - it('Wallet connected: Lock 1 GLM', () => { - connectWallet(); - - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]') - .invoke('text') - .then(text => { - const amountToLock = 1; - const lockedGlms = parseInt(text, 10); - - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type(`${amountToLock}`); - cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Lock'); - cy.get('[data-test=GlmLockTabs__Button]').click(); - cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Waiting for confirmation'); - cy.confirmMetamaskPermissionToSpend({ - spendLimit: '99999999999999999999', - }); - // Workaround for two notifications during first transaction. - // 1. Allow the third party to spend TKN from your current balance. - // 2. Confirm permission to spend - if (Cypress.env('CI') === 'true' && idx === 0) { - cy.wait(1000); - cy.confirmMetamaskPermissionToSpend({ - spendLimit: '99999999999999999999', - }); - } - cy.get('[data-test=GlmLockTabs__Button]', { timeout: 180000 }).should( - 'have.text', - 'Close', - ); - cy.get('[data-test=GlmLockNotification--success]').should('be.visible'); - cy.get('[data-test=GlmLockTabs__Button]').click(); - cy.get( - '[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', - // Small timeout ensures skeleton shows up quickly after the transaction. - { timeout: 1000 }, - ).should('be.visible'); - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]', { - timeout: 60000, - }) - .invoke('text') - .then(nextText => { - const lockedGlmsAfterLock = parseInt(nextText, 10); - expect(lockedGlms + amountToLock).to.be.eq(lockedGlmsAfterLock); - }); - cy.get('[data-test=HistoryItem__title]').first().should('have.text', 'Locked GLM'); - cy.get('[data-test=HistoryItem__DoubleValue__primary]') - .first() - .should('have.text', '1 GLM'); - }); - }); - - it('Wallet connected: Unlock 1 GLM', () => { - connectWallet(); - - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]') - .invoke('text') - .then(text => { - const amountToUnlock = 1; - const lockedGlms = parseInt(text, 10); - - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=EarnGlmLockTabs__tab--1]').click(); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]') - .clear() - .type(`${amountToUnlock}`); - cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Unlock'); - cy.get('[data-test=GlmLockTabs__Button]').click(); - cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Waiting for confirmation'); - cy.confirmMetamaskPermissionToSpend({ - shouldWaitForPopupClosure: true, - spendLimit: '99999999999999999999', - }); - cy.get('[data-test=GlmLockTabs__Button]', { timeout: 60000 }).should( - 'have.text', - 'Close', - ); - cy.get('[data-test=GlmLockNotification--success]').should('be.visible'); - cy.get('[data-test=GlmLockTabs__Button]').click(); - cy.get( - '[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', - // Small timeout ensures skeleton shows up quickly after the transaction. - { timeout: 1000 }, - ).should('be.visible'); - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]', { - timeout: 60000, - }) - .invoke('text') - .then(nextText => { - const lockedGlmsAfterUnlock = parseInt(nextText, 10); - expect(lockedGlms - amountToUnlock).to.be.eq(lockedGlmsAfterUnlock); - }); - cy.get('[data-test=HistoryItem__title]').first().should('have.text', 'Unlocked GLM'); - cy.get('[data-test=HistoryItem__DoubleValue__primary]') - .first() - .should('have.text', '1 GLM'); - }); - }); - - it('Wallet connected: Effective deposit after locking 1000 GLM and moving epoch is equal to current deposit', () => { - connectWallet(); - - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]') - .invoke('text') - .then(text => { - const amountToLock = 1000; - const lockedGlms = parseInt(text.replace(/\u200a/g, ''), 10); - - cy.get('[data-test=BoxGlmLock__Button]').click(); - cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type(`${amountToLock}`); - cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Lock'); - cy.get('[data-test=GlmLockTabs__Button]').click(); - cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Waiting for confirmation'); - cy.confirmMetamaskPermissionToSpend({ - spendLimit: '99999999999999999999', - }); - cy.get('[data-test=GlmLockTabs__Button]', { timeout: 180000 }).should( - 'have.text', - 'Close', - ); - cy.get('[data-test=GlmLockNotification--success]').should('be.visible'); - cy.get('[data-test=GlmLockTabs__Button]').click(); - cy.get( - '[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', - // Small timeout ensures skeleton shows up quickly after the transaction. - { timeout: 1000 }, - ).should('be.visible'); - // Waiting for skeletons to disappear ensures Graph indexed lock/unlock. - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', { - timeout: 60000, - }).should('not.exist'); - cy.window().then(async win => { - cy.wrap(null).then(() => { - return moveTime(win, 'nextEpochDecisionWindowClosed').then(() => { - cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]', { - timeout: 60000, - }) - .invoke('text') - .then(nextText => { - const lockedGlmsAfterLock = parseInt(nextText.replace(/\u200a/g, ''), 10); - expect(lockedGlms + amountToLock).to.be.eq(lockedGlmsAfterLock); - }); - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__primary]', { - timeout: 60000, - }) - .invoke('text') - .then(nextText => { - const lockedGlmsAfterLock = parseInt(nextText.replace(/\u200a/g, ''), 10); - expect(lockedGlms + amountToLock).to.be.eq(lockedGlmsAfterLock); - }); - }); - }); - }); - }); - }); - }); -}); diff --git a/client/cypress/e2e/layout.cy.ts b/client/cypress/e2e/layout.cy.ts deleted file mode 100644 index 9ea9954d24..0000000000 --- a/client/cypress/e2e/layout.cy.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { navigateWithCheck, mockCoinPricesServer } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { navigationTabs } from 'src/constants/navigationTabs/navigationTabs'; -import { ROOT, ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`layout: ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - cy.clearLocalStorage(); - cy.setupMetamask(); - }); - - beforeEach(() => { - mockCoinPricesServer(); - cy.disconnectMetamaskWalletFromAllDapps(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - cy.visit(ROOT.absolute); - }); - - it('renders top bar', () => { - cy.get('[data-test=MainLayout__Header]').should('be.visible'); - }); - - it('Clicking on Octant logo scrolls view to the top on logo click (projects view)', () => { - cy.scrollTo(0, 500); - cy.get('[data-test=MainLayout__Logo]').click(); - // waiting for scrolling to finish - cy.wait(2000); - cy.window().then(cyWindow => { - expect(cyWindow.scrollY).to.be.eq(0); - }); - }); - - it('Clicking on Octant logo redirects to projects view (outside projects view)', () => { - navigateWithCheck(ROOT_ROUTES.settings.absolute); - cy.get('[data-test=SettingsView]').should('be.visible'); - cy.get('[data-test=MainLayout__Logo]').click(); - cy.get('[data-test=ProjectsView]').should('be.visible'); - }); - - it('Clicking on Octant logo redirects to projects view (outside projects view) with memorized scrollY', () => { - cy.scrollTo(0, 500); - navigateWithCheck(ROOT_ROUTES.settings.absolute); - cy.get('[data-test=SettingsView]').should('be.visible'); - cy.get('[data-test=MainLayout__Logo]').click(); - cy.get('[data-test=ProjectsView]').should('be.visible'); - cy.window().then(cyWindow => { - expect(cyWindow.scrollY).to.be.eq(500); - }); - }); - - it('renders bottom navbar', () => { - cy.get('[data-test=Navbar]').should('be.visible'); - }); - - it('bottom navbar allows to change views', () => { - navigationTabs.forEach(({ to }) => { - navigateWithCheck(to); - }); - }); - - it('"Connect" button is visible when wallet is disconnected', () => { - cy.get('[data-test=MainLayout__Button--connect]').should('be.visible'); - cy.get('[data-test=MainLayout__Button--connect]').click(); - }); - - it('"Connect" button opens "ModalConnectWallet"', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet]').should('be.visible'); - }); - - it('"ModalConnectWallet" always shows "WalletConnect" option', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet]').within(() => { - cy.get('[data-test=ConnectWallet__BoxRounded--walletConnect]').should('be.visible'); - }); - }); - - it('"ModalConnectWallet" shows "Browser wallet" and "WalletConnect" options (MetaMask wallet detected)', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet]').within(() => { - cy.get('[data-test=ConnectWallet__BoxRounded--walletConnect]').should('be.visible'); - cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').should('be.visible'); - }); - }); - - it('"ModalConnectWallet" has overflow enabled', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet__overflow]').should('exist'); - }); - - it('Clicking background when "ModalConnectWallet" is open, closes Modal', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet__overflow]').click({ force: true }); - cy.get('[data-test=ModalConnectWallet]').should('not.exist'); - }); - - it('"ModalConnectWallet" has "cross" icon button in header', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet__Button]').should('be.visible'); - }); - - it('Clicking on "X" mark in "ModalConnectWallet", closes Modal', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ModalConnectWallet__Button]').click(); - cy.get('[data-test=ModalConnectWallet]').should('not.exist'); - }); - - it('Clicking on "WalletConnect" option, opens Web3Modal', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ConnectWallet__BoxRounded--walletConnect]').click(); - cy.get('w3m-modal').find('#w3m-modal', { includeShadowDom: true }).should('be.visible'); - }); - - it('Clicking on "Browser wallet" option connects with MetaMask wallet', () => { - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').click(); - cy.switchToMetamaskNotification(); - cy.acceptMetamaskAccess(); - cy.get('[data-test=MainLayout__Button--connect]').should('not.exist'); - cy.get('[data-test=ProfileInfo]').should('exist'); - }); - }); -}); diff --git a/client/cypress/e2e/metrics.cy.ts b/client/cypress/e2e/metrics.cy.ts deleted file mode 100644 index 829fbfbf1c..0000000000 --- a/client/cypress/e2e/metrics.cy.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { - describe(`metrics: ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.metrics.absolute); - }); - - it('renders total projects tile', () => { - cy.get('[data-test=MetricsGeneralGridTotalProjects]').should('be.visible'); - }); - - it('renders total eth staked tile', () => { - cy.get('[data-test=MetricsGeneralGridTotalEthStaked]').should('be.visible'); - }); - - it('renders tile with total glm locked and % of 1B total supply groups', () => { - cy.get('[data-test=MetricsGeneralGridTotalGlmLockedAndTotalSupply]').should('be.visible'); - cy.get('[data-test=MetricsGeneralGridTotalGlmLockedAndTotalSupply]') - .children() - .should('have.length', 2); - }); - - it('renders wallet with glm locked graph tile', () => { - cy.get('[data-test=MetricsGeneralGridWalletsWithGlmLocked]').should('be.visible'); - }); - - it('renders cumulative glm locked graph tile', () => { - cy.get('[data-test=MetricsGeneralGridCumulativeGlmLocked]').should('be.visible'); - }); - - it('renders tiles in correct order', () => { - const metricsEpochGridTilesDataTest = [ - 'MetricsEpochGridTopProjects', - 'MetricsEpochGridTotalDonationsAndPersonal', - 'MetricsEpochGridDonationsVsPersonalAllocations', - 'MetricsEpochGridFundsUsage', - 'MetricsEpochGridTotalUsers', - 'MetricsEpochGridPatrons', - 'MetricsEpochGridCurrentDonors', - 'MetricsEpochGridAverageLeverage', - 'MetricsEpochGridRewardsUnusedAndUnallocatedValue', - 'MetricsEpochGridBelowThreshold', - ]; - - const metricsGeneralGridTilesDataTest = [ - 'MetricsGeneralGridTotalGlmLockedAndTotalSupply', - 'MetricsGeneralGridTotalProjects', - 'MetricsGeneralGridTotalEthStaked', - 'MetricsGeneralGridCumulativeGlmLocked', - 'MetricsGeneralGridWalletsWithGlmLocked', - ]; - - cy.get('[data-test=MetricsEpoch__MetricsGrid]') - .children() - .should('have.length', metricsEpochGridTilesDataTest.length); - - for (let i = 0; i < metricsEpochGridTilesDataTest.length; i++) { - cy.get('[data-test=MetricsEpoch__MetricsGrid]') - .children() - .eq(i) - .invoke('data', 'test') - .should('eq', metricsEpochGridTilesDataTest[i]); - } - - cy.get('[data-test=MetricsGeneral__MetricsGrid]') - .children() - .should('have.length', metricsGeneralGridTilesDataTest.length); - - for (let i = 0; i < metricsGeneralGridTilesDataTest.length; i++) { - cy.get('[data-test=MetricsGeneral__MetricsGrid]') - .children() - .eq(i) - .invoke('data', 'test') - .should('eq', metricsGeneralGridTilesDataTest[i]); - } - }); - - it('renders grid with 4 columns on desktop or with 2 columns on other devices', () => { - cy.get('[data-test=MetricsEpoch__MetricsGrid]').then(el => { - const width = parseInt(el.css('width'), 10); - const rowGap = parseInt(el.css('rowGap'), 10); - - const columnWidth = isDesktop ? (width - 3 * rowGap) / 4 : (width - rowGap) / 2; - - cy.get('[data-test=MetricsEpoch__MetricsGrid]').should( - 'have.css', - 'grid-template-columns', - isDesktop - ? `${columnWidth}px ${columnWidth}px ${columnWidth}px ${columnWidth}px` - : `${columnWidth}px ${columnWidth}px`, - ); - }); - - cy.get('[data-test=MetricsGeneral__MetricsGrid]').then(el => { - const width = parseInt(el.css('width'), 10); - const rowGap = parseInt(el.css('rowGap'), 10); - - const columnWidth = isDesktop ? (width - 3 * rowGap) / 4 : (width - rowGap) / 2; - - cy.get('[data-test=MetricsGeneral__MetricsGrid]').should( - 'have.css', - 'grid-template-columns', - isDesktop - ? `${columnWidth}px ${columnWidth}px ${columnWidth}px ${columnWidth}px` - : `${columnWidth}px ${columnWidth}px`, - ); - }); - }); - }); -}); diff --git a/client/cypress/e2e/onboarding.cy.ts b/client/cypress/e2e/onboarding.cy.ts deleted file mode 100644 index 3dec91c7b6..0000000000 --- a/client/cypress/e2e/onboarding.cy.ts +++ /dev/null @@ -1,380 +0,0 @@ -import { visitWithLoader, navigateWithCheck, mockCoinPricesServer } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { getStepsDecisionWindowClosed } from 'src/hooks/helpers/useOnboardingSteps/steps'; -import { ROOT, ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -import Chainable = Cypress.Chainable; - -const connectWallet = ( - isTOSAccepted: boolean, - shouldVisit = true, - shouldReload = false, -): Chainable => { - cy.intercept('GET', '/user/*/tos', { body: { accepted: isTOSAccepted } }); - cy.disconnectMetamaskWalletFromAllDapps(); - if (shouldVisit) { - visitWithLoader(ROOT.absolute, ROOT_ROUTES.projects.absolute); - } - if (shouldReload) { - cy.reload(); - } - cy.get('[data-test=MainLayout__Button--connect]').click(); - cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').click(); - cy.switchToMetamaskNotification(); - return cy.acceptMetamaskAccess(); -}; - -const beforeSetup = () => { - mockCoinPricesServer(); - cy.clearLocalStorage(); - cy.setupMetamask(); - window.innerWidth = Cypress.config().viewportWidth; - window.innerHeight = Cypress.config().viewportHeight; -}; - -const checkCurrentElement = (el: number, isCurrent: boolean): Chainable => { - return cy - .get('[data-test=ModalOnboarding__ProgressStepperSlim__element]') - .eq(el) - .invoke('attr', 'data-iscurrent') - .should('eq', `${isCurrent}`); -}; - -const checkProgressStepperSlimIsCurrentAndClickNext = (index, isCurrent = true): Chainable => { - checkCurrentElement(index - 1, isCurrent); - return cy - .get('[data-test=ModalOnboarding__ProgressStepperSlim__element]') - .eq(index) - .click({ force: true }); -}; - -const checkChangeStepsWithArrowKeys = (isTOSAccepted: boolean) => { - checkCurrentElement(0, true); - - [ - { el: 1, key: 'ArrowRight' }, - { el: 2, key: 'ArrowRight' }, - // { el: 3, key: 'ArrowRight' }, - // { el: 3, key: 'ArrowRight' }, - // { el: 2, key: 'ArrowLeft' }, - { el: 1, key: 'ArrowLeft' }, - { el: 0, key: 'ArrowLeft' }, - { el: 0, key: 'ArrowLeft' }, - ].forEach(({ key, el }) => { - cy.get('body').trigger('keydown', { key }); - checkCurrentElement(el, isTOSAccepted || el === 0); - - if (!isTOSAccepted) { - checkCurrentElement(0, true); - } - }); -}; - -const checkChangeStepsByClickingEdgeOfTheScreenUpTo25px = (isTOSAccepted: boolean) => { - checkCurrentElement(0, true); - - cy.get('[data-test=ModalOnboarding]').then(element => { - const leftEdgeX = element.offsetParent().offset()?.left as number; - const rightEdgeX = (leftEdgeX as number) + element.innerWidth()!; - - [ - { clientX: rightEdgeX - 25, el: 1 }, - { clientX: rightEdgeX - 10, el: 2 }, - // { clientX: rightEdgeX - 5, el: 3 }, - // rightEdgeX === browser right frame - // { clientX: rightEdgeX - 1, el: 3 }, - // { clientX: leftEdgeX + 25, el: 2 }, - { clientX: leftEdgeX + 10, el: 1 }, - { clientX: leftEdgeX + 5, el: 0 }, - { clientX: leftEdgeX, el: 0 }, - ].forEach(({ clientX, el }) => { - cy.get('[data-test=ModalOnboarding]').click(clientX, element.height()! / 2); - checkCurrentElement(el, isTOSAccepted || el === 0); - - if (!isTOSAccepted) { - checkCurrentElement(0, true); - } - }); - }); -}; - -const checkChangeStepsByClickingEdgeOfTheScreenMoreThan25px = (isTOSAccepted: boolean) => { - checkCurrentElement(0, true); - - cy.get('[data-test=ModalOnboarding]').then(element => { - const leftEdgeX = element.offsetParent().offset()?.left as number; - const rightEdgeX = (leftEdgeX as number) + element.innerWidth()!; - - [ - { clientX: rightEdgeX - 25, el: 1 }, - { clientX: rightEdgeX - 26, el: 1 }, - { clientX: leftEdgeX + 26, el: 1 }, - { clientX: leftEdgeX + 25, el: 0 }, - ].forEach(({ clientX, el }) => { - cy.get('[data-test=ModalOnboarding]').click(clientX, element.height()! / 2); - checkCurrentElement(el, isTOSAccepted || el === 0); - - if (!isTOSAccepted) { - checkCurrentElement(0, true); - } - }); - }); -}; - -const checkChangeStepsBySwipingOnScreenDifferenceMoreThanOrEqual5px = (isTOSAccepted: boolean) => { - checkCurrentElement(0, true); - - [ - { - el: 1, - touchMoveClientX: window.innerWidth / 2 - 5, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 2, - touchMoveClientX: window.innerWidth / 2 - 5, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 2, - touchMoveClientX: window.innerWidth / 2 - 5, - touchStartClientX: window.innerWidth / 2, - }, - // { - // el: 3, - // touchMoveClientX: window.innerWidth / 2 - 5, - // touchStartClientX: window.innerWidth / 2, - // }, - { - el: 2, - touchMoveClientX: window.innerWidth / 2 + 5, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 1, - touchMoveClientX: window.innerWidth / 2 + 5, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 0, - touchMoveClientX: window.innerWidth / 2 + 5, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 0, - touchMoveClientX: window.innerWidth / 2 + 5, - touchStartClientX: window.innerWidth / 2, - }, - ].forEach(({ touchStartClientX, touchMoveClientX, el }) => { - cy.get('[data-test=ModalOnboarding]').trigger('touchstart', { - touches: [{ clientX: touchStartClientX }], - }); - cy.get('[data-test=ModalOnboarding]').trigger('touchmove', { - touches: [{ clientX: touchMoveClientX }], - }); - checkCurrentElement(el, isTOSAccepted || el === 0); - - if (!isTOSAccepted) { - checkCurrentElement(0, true); - } - }); -}; - -const checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px = (isTOSAccepted: boolean) => { - checkCurrentElement(0, true); - - [ - { - el: 1, - touchMoveClientX: window.innerWidth / 2 - 5, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 1, - touchMoveClientX: window.innerWidth / 2 - 4, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 1, - touchMoveClientX: window.innerWidth / 2 + 4, - touchStartClientX: window.innerWidth / 2, - }, - { - el: 0, - touchMoveClientX: window.innerWidth / 2 + 5, - touchStartClientX: window.innerWidth / 2, - }, - ].forEach(({ touchStartClientX, touchMoveClientX, el }) => { - cy.get('[data-test=ModalOnboarding]').trigger('touchstart', { - touches: [{ clientX: touchStartClientX }], - }); - cy.get('[data-test=ModalOnboarding]').trigger('touchmove', { - touches: [{ clientX: touchMoveClientX }], - }); - checkCurrentElement(el, isTOSAccepted || el === 0); - - if (!isTOSAccepted) { - checkCurrentElement(0, true); - } - }); -}; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`onboarding (TOS accepted): ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - beforeSetup(); - }); - - beforeEach(() => { - connectWallet(true); - }); - - it('user is able to click through entire onboarding flow', () => { - for (let i = 1; i < getStepsDecisionWindowClosed('2', '16 Jan').length - 1; i++) { - checkProgressStepperSlimIsCurrentAndClickNext(i); - } - - cy.get('[data-test=ModalOnboarding__ProgressStepperSlim__element]') - .eq(getStepsDecisionWindowClosed('2', '16 Jan').length - 1) - .click(); - cy.get('[data-test=ModalOnboarding__Button]').click(); - cy.get('[data-test=ModalOnboarding]').should('not.exist'); - cy.get('[data-test=ProjectsView__ProjectsList]').should('be.visible'); - }); - - it('user is able to close the modal by clicking button in the top-right', () => { - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - cy.get('[data-test=ModalOnboarding__Button]').click(); - cy.get('[data-test=ModalOnboarding]').should('not.exist'); - cy.get('[data-test=ProjectsView__ProjectsList]').should('be.visible'); - }); - - it('renders every time page is refreshed when "Always show Allocate onboarding" option is checked', () => { - cy.get('[data-test=ModalOnboarding__Button]').click(); - navigateWithCheck(ROOT_ROUTES.settings.absolute); - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').check().should('be.checked'); - cy.reload(); - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - }); - - it('renders only once when "Always show Allocate onboarding" option is not checked', () => { - cy.get('[data-test=ModalOnboarding__Button]').click(); - navigateWithCheck(ROOT_ROUTES.settings.absolute); - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('not.be.checked'); - cy.reload(); - cy.get('[data-test=ModalOnboarding]').should('not.exist'); - }); - - it('user can change steps with arrow keys (left, right)', () => { - checkChangeStepsWithArrowKeys(true); - }); - - it('user can change steps by clicking the edge of the screen (up to 25px from each edge)', () => { - checkChangeStepsByClickingEdgeOfTheScreenUpTo25px(true); - }); - - it('user cannot change steps by clicking the edge of the screen (more than 25px from each edge)', () => { - checkChangeStepsByClickingEdgeOfTheScreenMoreThan25px(true); - }); - - it('user can change steps by swiping on screen (difference more than or equal 5px)', () => { - checkChangeStepsBySwipingOnScreenDifferenceMoreThanOrEqual5px(true); - }); - - it('user cannot change steps by swiping on screen (difference less than 5px)', () => { - checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px(true); - }); - - it('user cannot change steps by swiping on screen (difference less than 5px)', () => { - checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px(true); - }); - - it('user is able to close the onboarding, and after disconnecting & connecting, onboarding does not show up again', () => { - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - cy.get('[data-test=ModalOnboarding__Button]').click(); - cy.get('[data-test=ModalOnboarding]').should('not.exist'); - connectWallet(true, false, true); - cy.get('[data-test=ModalOnboarding]').should('not.exist'); - }); - }); -}); - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`onboarding (TOS not accepted): ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - beforeSetup(); - }); - - beforeEach(() => { - cy.intercept( - { - method: 'POST', - url: '/user/*/tos', - }, - { body: { accepted: true }, statusCode: 200 }, - ); - connectWallet(false); - }); - - it('onboarding TOS step should be first and active', () => { - checkCurrentElement(0, true); - cy.get('[data-test=ModalOnboardingTOS]').should('be.visible'); - }); - - it('user is not able to click through entire onboarding flow', () => { - for (let i = 1; i < getStepsDecisionWindowClosed('2', '16 Jan').length; i++) { - checkProgressStepperSlimIsCurrentAndClickNext(i, i === 1); - } - }); - - it('user is not able to close the modal by clicking button in the top-right', () => { - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - cy.get('[data-test=ModalOnboarding__Button]').click({ force: true }); - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - }); - - it('renders every time page is refreshed', () => { - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - cy.reload(); - cy.get('[data-test=ModalOnboarding]').should('be.visible'); - }); - - it('user cannot change steps with arrow keys (left, right)', () => { - checkChangeStepsWithArrowKeys(false); - }); - - it('user can change steps by clicking the edge of the screen (up to 25px from each edge)', () => { - checkChangeStepsByClickingEdgeOfTheScreenUpTo25px(false); - }); - - it('user cannot change steps by clicking the edge of the screen (more than 25px from each edge)', () => { - checkChangeStepsByClickingEdgeOfTheScreenMoreThan25px(false); - }); - - it('user cannot change steps by swiping on screen (difference more than or equal 5px)', () => { - checkChangeStepsBySwipingOnScreenDifferenceMoreThanOrEqual5px(false); - }); - - it('user cannot change steps by swiping on screen (difference less than 5px)', () => { - checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px(false); - }); - - it('TOS acceptance changes onboarding step to next step', () => { - checkCurrentElement(0, true); - cy.get('[data-test=TOS_InputCheckbox]').check(); - cy.switchToMetamaskNotification(); - cy.confirmMetamaskSignatureRequest(); - checkCurrentElement(1, true); - }); - - it('TOS acceptance allows the user to close the modal by clicking button in the top-right', () => { - checkCurrentElement(0, true); - cy.get('[data-test=TOS_InputCheckbox]').check(); - cy.switchToMetamaskNotification(); - cy.confirmMetamaskSignatureRequest(); - checkCurrentElement(1, true); - cy.get('[data-test=ModalOnboarding__Button]').click(); - cy.get('[data-test=ModalOnboarding]').should('not.exist'); - }); - }); -}); diff --git a/client/cypress/e2e/patronMode.cy.ts b/client/cypress/e2e/patronMode.cy.ts deleted file mode 100644 index b5f3455c47..0000000000 --- a/client/cypress/e2e/patronMode.cy.ts +++ /dev/null @@ -1,724 +0,0 @@ -import { connectWallet, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { - describe(`patron mode (disabled): ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.settings.absolute); - connectWallet(true, false); - }); - - it('patron badge should not exist ', () => { - cy.get('[data-test=ProfileInfo__badge]').should('not.exist'); - }); - - it('Patron mode toggle is not checked', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').should('not.be.checked'); - }); - - if (isDesktop) { - it('Patron mode tooltip is visible on hover and has correct text', () => { - cy.get('[data-test=SettingsPatronModeBox__Tooltip]').trigger('mouseover'); - cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]').should('be.visible'); - cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]') - .invoke('text') - .should( - 'eq', - 'Patron mode is for token holders who want to support Octant. It disables allocation to yourself or projects. All rewards go directly to the matching fund with no action required by the patron.', - ); - }); - } - - it('Checking patron mode opens patron mode modal', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=ModalPatronMode]').should('be.visible'); - }); - - it('Patron mode modal last paragraph has correct text', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=SettingsPatronMode__fourthParagraph]') - .invoke('text') - .should('eq', 'Slide the switch below all the way to the right to enable patron mode.'); - }); - - it('Slider is visible', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=PatronModeSlider]').should('be.visible'); - }); - - it('Slider has correct label', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=PatronModeSlider__label]') - .invoke('text') - .should('eq', 'Slide right to confirm'); - }); - - it('Slider button is visible ', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=PatronModeSlider__button]').should('be.visible'); - }); - - it('Slider button has right arrow inside', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=PatronModeSlider__button__arrow]') - .should('be.visible') - .should('have.css', 'transform', 'none'); - }); - - it('Slider button is on the left side', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderLeftDistance = sliderEl[0].getBoundingClientRect().left; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonLeftDistance = sliderButtonEl[0].getBoundingClientRect().left; - - expect(sliderButtonLeftDistance).to.be.eq(sliderLeftDistance + sliderLeftPadding); - }); - }); - }); - - it('Slider button returns to the starting point if user drops it below or equal 50% of slider width', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderDimensions = sliderEl[0].getBoundingClientRect(); - const sliderWidth = sliderDimensions.width; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - const sliderButtonWidth = sliderButtonDimensions.width; - - const sliderTrackWidth = - sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; - - const pointerDownPageX = sliderButtonDimensions.x; - const pointerMovePageX = sliderButtonDimensions.x + sliderTrackWidth / 2; - - cy.get('[data-test=PatronModeSlider__button]') - .trigger('pointerdown', { - pageX: pointerDownPageX, - }) - .trigger('pointermove', { - pageX: pointerMovePageX, - }) - .wait(1000) - .then(sliderButtonElAfterPointerMove => { - const sliderButtonDimensionsAfterPointerMove = - sliderButtonElAfterPointerMove[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerMove.x).eq(pointerMovePageX); - }) - .trigger('pointerup') - .wait(1000) - .then(sliderButtonElAfterPointerUp => { - const sliderButtonDimensionsAfterPointerUp = - sliderButtonElAfterPointerUp[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerUp.x).eq(pointerDownPageX); - }); - }); - }); - }); - - it('Slider elements change color while moving slider button', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderDimensions = sliderEl[0].getBoundingClientRect(); - const sliderWidth = sliderDimensions.width; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - const sliderButtonWidth = sliderButtonDimensions.width; - - const sliderTrackWidth = - sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; - - const slider0PercentagePageX = sliderButtonDimensions.x; - const slider25PercentagePageX = sliderButtonDimensions.x + sliderTrackWidth / 4; - const slider50PercentagePageX = sliderButtonDimensions.x + sliderTrackWidth / 2; - const slider75PercentagePageX = sliderButtonDimensions.x + 3 * (sliderTrackWidth / 4); - const slider100PercentagePageX = sliderButtonDimensions.x + sliderTrackWidth; - - // 0% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointerdown', { - pageX: slider0PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgb(243, 243, 243)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(255, 255, 255)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(23, 23, 23)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '1'); - - // 25% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider25PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(212, 224, 221, 0.95)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(222, 234, 231)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(129, 129, 129)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0.75'); - - // 50% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider50PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(175, 204, 197, 0.9)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(183, 211, 204)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(181, 181, 181)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0.5'); - - // 75% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider75PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(128, 181, 169, 0.85)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(133, 185, 173)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(221, 221, 221)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0.25'); - - // 100% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider100PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(45, 155, 135, 0.8)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(45, 155, 135)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(255, 255, 255)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0'); - }); - }); - }); - - it('Slider button goes to the end of track if user drops it above 50% of slider width + animation after signature', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); - - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderDimensions = sliderEl[0].getBoundingClientRect(); - const sliderWidth = sliderDimensions.width; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - const sliderButtonWidth = sliderButtonDimensions.width; - - const sliderTrackWidth = - sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; - - const pointerDownPageX = sliderButtonDimensions.x; - const pointerMovePageX = sliderButtonDimensions.x + (sliderTrackWidth / 2 + 1); - const pointerUpPageX = sliderDimensions.right - sliderRightPadding - sliderButtonWidth; - - cy.get('[data-test=PatronModeSlider__button]') - .trigger('pointerdown', { - pageX: pointerDownPageX, - }) - .trigger('pointermove', { - pageX: pointerMovePageX, - }) - .wait(1000) - .then(sliderButtonElAfterPointerMove => { - const sliderButtonDimensionsAfterPointerMove = - sliderButtonElAfterPointerMove[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerMove.x).eq(pointerMovePageX); - }) - .trigger('pointerup') - .wait(1000) - .then(sliderButtonElAfterPointerUp => { - const sliderButtonDimensionsAfterPointerUp = - sliderButtonElAfterPointerUp[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerUp.x).eq(pointerUpPageX); - }); - - cy.confirmMetamaskSignatureRequest(); - cy.switchToCypressWindow(); - cy.get('[data-test=PatronModeSlider__button]').should('not.exist'); - cy.get('[data-test=PatronModeSlider__label]').should('not.exist'); - cy.get('[data-test=PatronModeSlider__status-label]') - .invoke('text') - .should('eq', 'Patron mode enabled'); - cy.wait(500); - cy.get('[data-test=ModalPatronMode]').should('not.exist'); - }); - }); - }); - }); - - describe(`patron mode (enabled): ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.settings.absolute); - connectWallet(true, true); - }); - - it('patron badge is visible and has correct label, background and text-transform prop', () => { - cy.get('[data-test=ProfileInfo__badge]').should('be.visible'); - cy.get('[data-test=ProfileInfo__badge]').invoke('text').should('eq', 'Patron'); - cy.get('[data-test=ProfileInfo__badge]') - .should('have.css', 'background-color', 'rgb(104, 91, 138)') - .should('have.css', 'text-transform', 'uppercase'); - }); - - it('Navbar has 4 items - projects, earn, metrics, settings', () => { - const navbarChildrenDataTest = [ - 'Navbar__Button--Projects', - 'Navbar__Button--Earn', - 'Navbar__Button--Metrics', - 'Navbar__Button--Settings', - ]; - - cy.get('[data-test=Navbar__buttons]') - .children() - .should('have.length', navbarChildrenDataTest.length); - - for (let i = 0; i < navbarChildrenDataTest.length; i++) { - cy.get('[data-test=Navbar__buttons]') - .children() - .eq(i) - .invoke('data', 'test') - .should('eq', navbarChildrenDataTest[i]); - } - }); - - it('route /allocate redirects to /projects', () => { - visitWithLoader(ROOT_ROUTES.allocation.absolute, ROOT_ROUTES.projects.absolute); - cy.get('[data-test=ProjectsView]').should('be.visible'); - }); - - it('BoxPersonalAllocation has correct title and sections labels', () => { - visitWithLoader(ROOT_ROUTES.earn.absolute); - cy.get('[data-test=BoxPersonalAllocation__title]') - .invoke('text') - .should('eq', 'Patron earnings'); - cy.get('[data-test=BoxPersonalAllocation__Section__label]') - .eq(0) - .invoke('text') - .should('eq', 'Current epoch'); - cy.get('[data-test=BoxPersonalAllocation__Section__label]') - .eq(1) - .invoke('text') - .should('eq', 'All time'); - }); - - it('Patron mode toggle is checked', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').should('be.checked'); - }); - - if (isDesktop) { - it('Patron mode tooltip is visible on hover and has correct text', () => { - cy.get('[data-test=SettingsPatronModeBox__Tooltip]').trigger('mouseover'); - cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]').should('be.visible'); - cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]') - .invoke('text') - .should( - 'eq', - 'Patron mode is for token holders who want to support Octant. It disables allocation to yourself or projects. All rewards go directly to the matching fund with no action required by the patron.', - ); - }); - } - - it('Unchecking patron mode opens patron mode modal', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=ModalPatronMode]').should('be.visible'); - }); - - it('Patron mode modal last paragraph has correct text', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=SettingsPatronMode__fourthParagraph]') - .invoke('text') - .should('eq', 'Slide the switch below all the way to the left to disable patron mode.'); - }); - - it('Slider is visible', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=PatronModeSlider]').should('be.visible'); - }); - - it('Slider has correct label', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=PatronModeSlider__label]') - .invoke('text') - .should('eq', 'Slide left to confirm'); - }); - - it('Slider button is visible', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=PatronModeSlider__button]').should('be.visible'); - }); - - it('Slider button has left arrow inside', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=PatronModeSlider__button__arrow]') - .should('be.visible') - .should('have.css', 'transform', 'matrix(-1, 0, 0, -1, 0, 0)'); - }); - - it('Slider button is on the right side', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderRightDistance = sliderEl[0].getBoundingClientRect().right; - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonRightDistance = sliderButtonEl[0].getBoundingClientRect().right; - - expect(sliderButtonRightDistance).to.be.eq(sliderRightDistance - sliderRightPadding); - }); - }); - }); - - it('Slider button returns to the starting point if user drops it below or equal 50% of slider width', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderDimensions = sliderEl[0].getBoundingClientRect(); - const sliderWidth = sliderDimensions.width; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - const sliderButtonWidth = sliderButtonDimensions.width; - - const sliderTrackWidth = - sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; - - const pointerDownPageX = sliderButtonDimensions.right; - const pointerMovePageX = sliderButtonDimensions.right - sliderTrackWidth / 2; - - cy.get('[data-test=PatronModeSlider__button]') - .trigger('pointerdown', { - pageX: pointerDownPageX, - }) - .trigger('pointermove', { - pageX: pointerMovePageX, - }) - .wait(1000) - .then(sliderButtonElAfterPointerMove => { - const sliderButtonDimensionsAfterPointerMove = - sliderButtonElAfterPointerMove[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerMove.right).eq(pointerMovePageX); - }) - .trigger('pointerup') - .wait(1000) - .then(sliderButtonElAfterPointerUp => { - const sliderButtonDimensionsAfterPointerUp = - sliderButtonElAfterPointerUp[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerUp.right).eq(pointerDownPageX); - }); - }); - }); - }); - - it('Slider elements change color while moving slider button', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderDimensions = sliderEl[0].getBoundingClientRect(); - const sliderWidth = sliderDimensions.width; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - const sliderButtonWidth = sliderButtonDimensions.width; - - const sliderTrackWidth = - sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; - - const slider0PercentagePageX = sliderButtonDimensions.right; - const slider25PercentagePageX = sliderButtonDimensions.right - sliderTrackWidth / 4; - const slider50PercentagePageX = sliderButtonDimensions.right - sliderTrackWidth / 2; - const slider75PercentagePageX = sliderButtonDimensions.right - 3 * (sliderTrackWidth / 4); - const slider100PercentagePageX = sliderButtonDimensions.right - sliderTrackWidth; - - // 0% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointerdown', { - pageX: slider0PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgb(243, 243, 243)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(255, 255, 255)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(23, 23, 23)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '1'); - - // 25% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider25PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(212, 224, 221, 0.95)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(222, 234, 231)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(129, 129, 129)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0.75'); - - // 50% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider50PercentagePageX, - waitForAnimations: true, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(175, 204, 197, 0.9)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(183, 211, 204)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(181, 181, 181)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0.5'); - - // 75% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider75PercentagePageX, - waitForAnimations: true, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(128, 181, 169, 0.85)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(133, 185, 173)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(221, 221, 221)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0.25'); - - // 100% - cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { - pageX: slider100PercentagePageX, - }); - cy.get('[data-test=PatronModeSlider]').should( - 'have.css', - 'background-color', - 'rgba(45, 155, 135, 0.8)', - ); - cy.get('[data-test=PatronModeSlider__button]').should( - 'have.css', - 'background-color', - 'rgb(45, 155, 135)', - ); - cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( - 'have.css', - 'fill', - 'rgb(255, 255, 255)', - ); - cy.get('[data-test=PatronModeSlider__label]') - .should('have.css', 'color', 'rgb(158, 163, 158)') - .should('have.css', 'opacity', '0'); - }); - }); - }); - - it('Slider button goes to the end of track if user drops it above 50% of slider width + animation after signature', () => { - cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); - - cy.get('[data-test=PatronModeSlider]').then(sliderEl => { - const sliderDimensions = sliderEl[0].getBoundingClientRect(); - const sliderWidth = sliderDimensions.width; - const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); - const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); - - cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { - const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); - - const sliderButtonWidth = sliderButtonDimensions.width; - - const sliderTrackWidth = - sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; - - const pointerDownPageX = sliderButtonDimensions.right; - const pointerMovePageX = sliderButtonDimensions.right - (sliderTrackWidth / 2 + 1); - const pointerUpPageX = sliderDimensions.left + sliderLeftPadding; - - cy.get('[data-test=PatronModeSlider__button]') - .trigger('pointerdown', { - pageX: pointerDownPageX, - }) - .trigger('pointermove', { - pageX: pointerMovePageX, - }) - .wait(1000) - .then(sliderButtonElAfterPointerMove => { - const sliderButtonDimensionsAfterPointerMove = - sliderButtonElAfterPointerMove[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerMove.right).eq(pointerMovePageX); - }) - .trigger('pointerup') - .wait(1000) - .then(sliderButtonElAfterPointerUp => { - const sliderButtonDimensionsAfterPointerUp = - sliderButtonElAfterPointerUp[0].getBoundingClientRect(); - expect(sliderButtonDimensionsAfterPointerUp.x).eq(pointerUpPageX); - }); - - cy.confirmMetamaskSignatureRequest(); - cy.switchToCypressWindow(); - cy.get('[data-test=PatronModeSlider__button]').should('not.exist'); - cy.get('[data-test=PatronModeSlider__label]').should('not.exist'); - cy.get('[data-test=PatronModeSlider__status-label]') - .invoke('text') - .should('eq', 'Patron mode disabled'); - cy.wait(500); - cy.get('[data-test=ModalPatronMode]').should('not.exist'); - }); - }); - }); - - it('when entering project view, button icon changes to chevronLeft', () => { - visitWithLoader(ROOT_ROUTES.projects.absolute); - cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); - cy.get('[data-test=Navbar__Button--Projects]') - .find('svg') - // HTML tag can't be self-closing in CY. - .should( - 'have.html', - '', - ); - }); - }); -}); diff --git a/client/cypress/e2e/project.cy.ts b/client/cypress/e2e/project.cy.ts deleted file mode 100644 index e811fb70aa..0000000000 --- a/client/cypress/e2e/project.cy.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { connectWallet, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; -import { getNamesOfProjects } from 'cypress/utils/projects'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -import Chainable = Cypress.Chainable; - -const getButtonAddToAllocate = (): Chainable => { - const projectListItemFirst = cy.get('[data-test=ProjectListItem').first(); - - return projectListItemFirst.find('[data-test=ProjectListItemHeader__ButtonAddToAllocate]'); -}; - -const checkProjectItemElements = (): Chainable => { - cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); - const projectListItemFirst = cy.get('[data-test=ProjectListItem').first(); - projectListItemFirst.get('[data-test=ProjectListItemHeader__Img]').should('be.visible'); - projectListItemFirst.get('[data-test=ProjectListItemHeader__name]').should('be.visible'); - getButtonAddToAllocate().should('be.visible'); - projectListItemFirst.get('[data-test=ProjectListItemHeader__Button]').should('be.visible'); - projectListItemFirst.get('[data-test=ProjectListItem__Description]').should('be.visible'); - - cy.get('[data-test=ProjectListItem__Donors]') - .first() - .scrollIntoView({ offset: { left: 0, top: 100 } }); - - cy.get('[data-test=ProjectListItem__Donors]').first().should('be.visible'); - cy.get('[data-test=ProjectListItem__Donors__DonorsHeader__count]') - .first() - .should('be.visible') - .should('have.text', '0'); - return cy.get('[data-test=ProjectListItem__Donors__noDonationsYet]').first().should('be.visible'); -}; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`project: ${device}`, { viewportHeight, viewportWidth }, () => { - let projectNames: string[] = []; - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.projects.absolute); - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - - /** - * This could be done in before hook, but CY wipes the state after each test - * (could be disabled, but creates other problems) - */ - if (projectNames.length === 0) { - projectNames = getNamesOfProjects(); - } - }); - - it('entering project view directly renders content', () => { - cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); - cy.reload(); - const projectListItemFirst = cy.get('[data-test=ProjectListItem').first(); - projectListItemFirst.get('[data-test=ProjectListItemHeader__Img]').should('be.visible'); - projectListItemFirst.get('[data-test=ProjectListItemHeader__name]').should('be.visible'); - }); - - it('entering project view renders all its elements', () => { - checkProjectItemElements(); - }); - - it('entering project view renders all its elements with fallback IPFS provider', () => { - cy.intercept('GET', '**/ipfs/**', req => { - if (req.url.includes('infura')) { - req.destroy(); - } - }); - - checkProjectItemElements(); - }); - - it('entering project view shows Toast with info about IPFS failure when all providers fail', () => { - cy.intercept('GET', '**/ipfs/**', req => { - req.destroy(); - }); - - cy.get('[data-test=Toast--ipfsMessage').should('be.visible'); - }); - - it('entering project view allows to add it to allocation and remove, triggering change of the icon, change of the number in navbar', () => { - cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); - - getButtonAddToAllocate().click(); - - // cy.get('@buttonAddToAllocate').click(); - cy.get('[data-test=Navbar__numberOfAllocations]').contains(1); - getButtonAddToAllocate().click(); - cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); - }); - - it('Entering project view allows scroll only to the last project', () => { - cy.get('[data-test^=ProjectsView__ProjectsListItem]').first().click(); - - for (let i = 0; i < projectNames.length; i++) { - cy.get('[data-test=ProjectListItem]').should( - 'have.length.greaterThan', - i === projectNames.length - 1 ? projectNames.length - 1 : i, - ); - cy.get('[data-test=ProjectListItemHeader__name]') - .eq(i) - .scrollIntoView({ offset: { left: 0, top: -150 } }) - .contains(projectNames[i]); - cy.get('[data-test=ProjectListItem__Donors]') - .eq(i) - .scrollIntoView({ offset: { left: 0, top: -150 } }) - .should('be.visible'); - } - }); - - it('"Back to top" button is displayed if the user has scrolled past the start of the final project description', () => { - cy.get('[data-test^=ProjectsView__ProjectsListItem]').first().click(); - - for (let i = 0; i < projectNames.length - 1; i++) { - cy.get('[data-test=ProjectListItem__Donors]') - .eq(i) - .scrollIntoView({ offset: { left: 0, top: 100 } }); - - if (i === projectNames.length - 1) { - cy.get('[data-test=ProjectBackToTopButton__Button]').should('be.visible'); - } - } - }); - - it('Clicking on "Back to top" button scrolls to the top of view (first project is visible)', () => { - cy.get('[data-test^=ProjectsView__ProjectsListItem]').first().click(); - - for (let i = 0; i < projectNames.length - 1; i++) { - cy.get('[data-test=ProjectListItem__Donors]') - .eq(i) - .scrollIntoView({ offset: { left: 0, top: 100 } }); - - if (i === projectNames.length - 1) { - cy.get('[data-test=ProjectBackToTopButton__Button]').click(); - cy.get('[data-test=ProjectListItem]').eq(0).should('be.visible'); - } - } - }); - }); - - describe(`project (patron mode): ${device}`, { viewportHeight, viewportWidth }, () => { - let projectNames: string[] = []; - - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.projects.absolute); - connectWallet(true, true); - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - - /** - * This could be done in before hook, but CY wipes the state after each test - * (could be disabled, but creates other problems) - */ - if (projectNames.length === 0) { - projectNames = getNamesOfProjects(); - } - }); - - it('button "add to allocate" is disabled', () => { - for (let i = 0; i < projectNames.length; i++) { - cy.get('[data-test^=ProjectsView__ProjectsListItem]').eq(i).click(); - getButtonAddToAllocate().should('be.visible').should('be.disabled'); - cy.go('back'); - } - }); - }); -}); diff --git a/client/cypress/e2e/projects.cy.ts b/client/cypress/e2e/projects.cy.ts deleted file mode 100644 index 9d096bcdf2..0000000000 --- a/client/cypress/e2e/projects.cy.ts +++ /dev/null @@ -1,239 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import chaiColors from 'chai-colors'; - -import { connectWallet, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; -import { getNamesOfProjects } from 'cypress/utils/projects'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import getMilestones from 'src/constants/milestones'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -import Chainable = Cypress.Chainable; - -chai.use(chaiColors); - -function checkProjectItemElements(index, name, isPatronMode = false): Chainable { - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__imageProfile]') - .should('be.visible'); - cy.get('[data-test^=ProjectsView__ProjectsListItem]') - .eq(index) - .should('be.visible') - .find('[data-test=ProjectsListItem__name]') - .should('be.visible') - .contains(name); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__IntroDescription]') - .should('be.visible'); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .should('be.visible'); - - if (isPatronMode) { - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .should('be.disabled'); - } - - return cy - .get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectRewards]') - .should('be.visible'); - // TODO OCT-663 Make CY check if rewards are available (Epoch 2, decision window open). - // return cy - // .get('[data-test^=ProjectsView__ProjectsListItem') - // .eq(index) - // .find('[data-test=ProjectRewards__currentTotal__label]') - // .should('be.visible'); -} - -function addProjectToAllocate(index, numberOfAddedProjects): Chainable { - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__imageProfile]') - .should('be.visible'); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__IntroDescription]') - .should('be.visible'); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .scrollIntoView(); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .click(); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .find('svg') - .find('path') - .then($el => $el.css('fill')) - .should('be.colored', '#FF6157'); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .find('svg') - .find('path') - .then($el => $el.css('stroke')) - .should('be.colored', '#FF6157'); - cy.get('[data-test=Navbar__numberOfAllocations]').contains(numberOfAddedProjects + 1); - visitWithLoader(ROOT_ROUTES.allocation.absolute); - cy.get('[data-test=AllocationItem]').should('have.length', numberOfAddedProjects + 1); - return cy.go('back'); -} - -function removeProjectFromAllocate(numberOfProjects, numberOfAddedProjects, index): Chainable { - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .scrollIntoView(); - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(index) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .click(); - visitWithLoader(ROOT_ROUTES.allocation.absolute); - cy.get('[data-test=AllocationItem]').should('have.length', numberOfAddedProjects - 1); - if (index < numberOfProjects - 1) { - cy.get('[data-test=Navbar__numberOfAllocations]').contains(numberOfAddedProjects - 1); - } else { - cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); - } - return cy.go('back'); -} - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`projects: ${device}`, { viewportHeight, viewportWidth }, () => { - let projectNames: string[] = []; - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.projects.absolute); - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - - /** - * This could be done in before hook, but CY wipes the state after each test - * (could be disabled, but creates other problems) - */ - if (projectNames.length === 0) { - projectNames = getNamesOfProjects(); - } - }); - - it('user is able to see all the projects in the view', () => { - for (let i = 0; i < projectNames.length; i++) { - cy.get('[data-test^=ProjectsView__ProjectsListItem]').eq(i).scrollIntoView(); - checkProjectItemElements(i, projectNames[i]); - } - }); - - it('user is able to add & remove the first and the last project to/from allocation, triggering change of the icon, change of the number in navbar', () => { - // This test checks the first and the last elements only to save time. - cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); - - addProjectToAllocate(0, 0); - addProjectToAllocate(projectNames.length - 1, 1); - removeProjectFromAllocate(projectNames.length, 2, 0); - removeProjectFromAllocate(projectNames.length, 1, projectNames.length - 1); - }); - - it('user is able to add project to allocation in ProjectsView and remove it from allocation in AllocationView', () => { - cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); - addProjectToAllocate(0, 0); - visitWithLoader(ROOT_ROUTES.allocation.absolute); - cy.get('[data-test=AllocationItemSkeleton]').should('not.exist'); - cy.get('[data-test=AllocationItem]').then(el => { - const { x } = el[0].getBoundingClientRect(); - cy.get('[data-test=AllocationItem]') - .trigger('pointerdown') - .trigger('pointermove', { pageX: x - 20 }) - .trigger('pointerup'); - cy.get('[data-test=AllocationItem__removeButton]').should('be.visible'); - cy.get('[data-test=AllocationItem__removeButton]').click(); - cy.get('[data-test=AllocationItem__removeButton]').should('not.exist'); - cy.get('[data-test=AllocationItem]').should('not.exist'); - cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); - }); - - it('ProjectsTimelineWidgetItem with href opens link when clicked without mouse movement', () => { - const milestones = getMilestones(); - cy.get('[data-test=ProjectsTimelineWidget]').should('be.visible'); - cy.get('[data-test=ProjectsTimelineWidgetItem]').should('have.length', milestones.length); - for (let i = 0; i < milestones.length; i++) { - if (milestones[i].href) { - cy.get('[data-test=ProjectsTimelineWidgetItem]') - .eq(i) - .within(() => { - cy.get('[data-test=ProjectsTimelineWidgetItem__Svg--arrowTopRight]').should( - 'be.visible', - ); - }); - - cy.get('[data-test=ProjectsTimelineWidgetItem]') - .eq(i) - .then(el => { - const { x } = el[0].getBoundingClientRect(); - cy.get('[data-test=ProjectsTimelineWidgetItem]') - .eq(i) - .trigger('mousedown') - .trigger('mouseup', { clientX: x + 10 }); - cy.location('pathname').should('eq', ROOT_ROUTES.projects.absolute); - - cy.get('[data-test=ProjectsTimelineWidgetItem]') - .eq(i) - .trigger('mousedown') - .trigger('mouseup'); - cy.location('pathname').should('not.eq', ROOT_ROUTES.projects.absolute); - }); - } - } - }); - }); - }); - - describe(`projects (patron mode): ${device}`, { viewportHeight, viewportWidth }, () => { - let projectNames: string[] = []; - - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.projects.absolute); - connectWallet(true, true); - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - /** - * This could be done in before hook, but CY wipes the state after each test - * (could be disabled, but creates other problems) - */ - if (projectNames.length === 0) { - projectNames = getNamesOfProjects(); - } - }); - - after(() => { - cy.disconnectMetamaskWalletFromAllDapps(); - }); - - it('button "add to allocate" is disabled', () => { - for (let i = 0; i < projectNames.length; i++) { - cy.get('[data-test^=ProjectsView__ProjectsListItem]').eq(i).scrollIntoView(); - checkProjectItemElements(i, projectNames[i], true); - } - }); - }); -}); diff --git a/client/cypress/e2e/projectsArchive.cy.ts b/client/cypress/e2e/projectsArchive.cy.ts deleted file mode 100644 index 237fcbd624..0000000000 --- a/client/cypress/e2e/projectsArchive.cy.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { checkLocationWithLoader, visitWithLoader } from 'cypress/utils/e2e'; -import { moveTime } from 'cypress/utils/moveTime'; -import viewports from 'cypress/utils/viewports'; -import { QUERY_KEYS } from 'src/api/queryKeys'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -let wasEpochMoved = false; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`projects archive: ${device}`, { viewportHeight, viewportWidth }, () => { - beforeEach(() => { - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.projects.absolute); - }); - - it('moves to the next epoch', () => { - // Move time only once, for the first device. - if (!wasEpochMoved) { - cy.window().then(async win => { - const currentEpochBefore = Number( - win.clientReactQuery.getQueryData(QUERY_KEYS.currentEpoch), - ); - - cy.wrap(null).then(() => { - return moveTime(win, 'nextEpochDecisionWindowClosed').then(() => { - const currentEpochAfter = Number( - win.clientReactQuery.getQueryData(QUERY_KEYS.currentEpoch), - ); - wasEpochMoved = true; - expect(currentEpochBefore + 1).to.eq(currentEpochAfter); - }); - }); - }); - } else { - expect(true).to.be.true; - } - }); - - it('renders archive elements + clicking on epoch archive ProjectsListItem opens ProjectView for particular epoch and project', () => { - cy.get('[data-test=MainLayout__body]').then(el => { - const mainLayoutPaddingTop = parseInt(el.css('paddingTop'), 10); - - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - cy.get('[data-test=ProjectsView__ProjectsList]') - .should('be.visible') - .children() - .then(children => { - children[children.length - 1].scrollIntoView(); - cy.window().then(window => window.scrollTo(0, window.scrollY - mainLayoutPaddingTop)); - cy.wait(1000); - // header test - cy.get('[data-test=ProjectsView__ProjectsList__header--archive]').should('be.visible'); - - // list test - cy.get('[data-test=ProjectsView__ProjectsList--archive]').first().should('be.visible'); - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - cy.get('[data-test=ProjectsView__ProjectsList--archive]') - .first() - .children() - .then(childrenArchive => { - const numberOfArchivedProjects = childrenArchive.length - 2; // archived projects tiles - (header + divider)[2] - for (let i = 0; i < numberOfArchivedProjects; i++) { - cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) - .first() - .scrollIntoView(); - cy.window().then(window => - window.scrollTo(0, window.scrollY - mainLayoutPaddingTop), - ); - // list item test - cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) - .first() - .should('be.visible') - .within(() => { - // rewards test - cy.get('[data-test=ProjectRewards]').should('be.visible'); - }); - - if (numberOfArchivedProjects - 1) { - cy.get('[data-test=ProjectsView__ProjectsList--archive]') - .first() - .should('have.length', 1); - } - - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) - .first() - .invoke('data', 'address') - .then(address => { - cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) - .first() - .invoke('data', 'epoch') - .then(epoch => { - cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) - .first() - .click(); - checkLocationWithLoader( - `${ROOT_ROUTES.project.absolute}/${epoch}/${address}`, - ); - cy.go('back'); - checkLocationWithLoader(ROOT_ROUTES.projects.absolute); - }); - }); - } - }); - }); - }); - }); - }); -}); diff --git a/client/cypress/e2e/rewardsCalculator.cy.ts b/client/cypress/e2e/rewardsCalculator.cy.ts deleted file mode 100644 index 361153ff90..0000000000 --- a/client/cypress/e2e/rewardsCalculator.cy.ts +++ /dev/null @@ -1,252 +0,0 @@ -import { ETH_USD, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; -import getFormattedEthValue from 'src/utils/getFormattedEthValue'; -import { parseUnitsBigInt } from 'src/utils/parseUnitsBigInt'; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { - describe(`rewards calculator: ${device}`, { viewportHeight, viewportWidth }, () => { - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.earn.absolute); - }); - - it('renders calculator icon inside box', () => { - cy.get('[data-test=Tooltip__rewardsCalculator__body]').should('be.visible'); - }); - - if (isDesktop) { - it('tooltip is visible on calculator icon hover and has correct text', () => { - cy.get('[data-test=Tooltip__rewardsCalculator').trigger('mouseover'); - cy.get('[data-test=Tooltip__rewardsCalculator__content') - .should('be.visible') - .invoke('text') - .should('eq', 'Calculate rewards'); - }); - } - - it('clicking on rewards calculator icon opens rewards calculator modal', () => { - cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); - cy.get('[data-test=ModalRewardsCalculator]').should('be.visible'); - }); - - it('default values in rewards calculator are 90 days and 5000 GLM', () => { - cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); - cy.get('[data-test=RewardsCalculator__InputText--crypto]').invoke('val').should('eq', '5000'); - cy.get('[data-test=RewardsCalculator__InputText--days]').invoke('val').should('eq', '90'); - }); - - it('calculator fetches rewards values in ETH and USD based on DAYS and GLM fields', () => { - cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewards'); - cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'be.visible', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'be.visible', - ); - cy.wait('@postEstimatedRewards'); - - cy.get('@postEstimatedRewards').then( - ({ - response: { - body: { budget }, - }, - }) => { - const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; - const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); - - cy.get( - '[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]', - ).should('not.exist'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', rewardsEth); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', rewardsUsd); - }, - ); - - cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewardsGlmValueChange'); - cy.get('[data-test=RewardsCalculator__InputText--crypto]').type('500000'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'be.visible', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'be.visible', - ); - cy.wait('@postEstimatedRewardsGlmValueChange'); - - cy.get('@postEstimatedRewardsGlmValueChange').then( - ({ - response: { - body: { budget }, - }, - }) => { - const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; - const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); - - cy.get( - '[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]', - ).should('not.exist'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', rewardsEth); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', rewardsUsd); - }, - ); - - cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewardsDaysValueChange'); - cy.get('[data-test=RewardsCalculator__InputText--days]').clear().type('900'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'be.visible', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'be.visible', - ); - cy.wait('@postEstimatedRewardsDaysValueChange'); - - cy.get('@postEstimatedRewardsDaysValueChange').then( - ({ - response: { - body: { budget }, - }, - }) => { - const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; - const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); - - cy.get( - '[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]', - ).should('not.exist'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', rewardsEth); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', rewardsUsd); - }, - ); - }); - - it('If DAYS or GLM input is empty rewards inputs are empty too', () => { - cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); - cy.get('[data-test=RewardsCalculator__InputText--crypto]').clear(); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', ''); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', ''); - - cy.get('[data-test=RewardsCalculator__InputText--days]').clear(); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', ''); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', ''); - - cy.get('[data-test=RewardsCalculator__InputText--crypto]').type('5000'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( - 'not.exist', - ); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', ''); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', ''); - }); - - it('Max GLM amount is 1000000000', () => { - cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewards'); - - cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); - - cy.get('[data-test=RewardsCalculator__InputText--crypto]').type('1000000000'); - cy.wait('@postEstimatedRewards'); - - cy.get('@postEstimatedRewards').then( - ({ - response: { - body: { budget }, - }, - }) => { - const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; - const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); - - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', rewardsEth); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', rewardsUsd); - - cy.get('[data-test=RewardsCalculator__InputText--crypto]') - .clear() - .type('1000000001') - .should('have.css', 'border-color', 'rgb(255, 97, 87)'); - cy.get('[data-test=RewardsCalculator__InputText--crypto__error]') - .should('be.visible') - .invoke('text') - .should('eq', 'That isn’t a valid amount'); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') - .invoke('val') - .should('eq', ''); - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') - .invoke('val') - .should('eq', ''); - }, - ); - }); - - it('Closing the modal successfully cancels the request /estimated_budget', () => { - cy.window().then(win => { - cy.spy(win.console, 'error').as('consoleErrSpy'); - }); - - cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); - - cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( - 'be.visible', - ); - - cy.get('[data-test=ModalRewardsCalculator__Button]').click(); - cy.get('[data-test=ModalRewardsCalculator').should('not.be.visible'); - - cy.on('uncaught:exception', error => { - expect(error.code).to.equal('ERR_CANCELED'); - }); - }); - }); -}); diff --git a/client/cypress/e2e/routes.cy.ts b/client/cypress/e2e/routes.cy.ts deleted file mode 100644 index 1d2dc113b8..0000000000 --- a/client/cypress/e2e/routes.cy.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { ROOT, ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`routes (wallet not connected): ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - mockCoinPricesServer(); - cy.clearLocalStorage(); - }); - - it('empty route redirects to projects view', () => { - visitWithLoader(ROOT.absolute, ROOT_ROUTES.projects.absolute); - cy.get('[data-test=ProjectsView]').should('be.visible'); - }); - - it('allocation route redirects to allocation view', () => { - visitWithLoader(ROOT_ROUTES.allocation.absolute); - cy.get('[data-test=AllocationView]').should('be.visible'); - }); - - it('earn route redirects to earn view', () => { - visitWithLoader(ROOT_ROUTES.earn.absolute); - cy.get('[data-test=EarnView]').should('be.visible'); - }); - - it('metrics route redirects to metrics view', () => { - visitWithLoader(ROOT_ROUTES.metrics.absolute); - cy.get('[data-test=MetricsView]').should('be.visible'); - }); - - it('projects route redirects to projects view', () => { - visitWithLoader(ROOT_ROUTES.projects.absolute); - cy.get('[data-test=ProjectsView]').should('be.visible'); - }); - - it('settings route redirects to settings view', () => { - visitWithLoader(ROOT_ROUTES.settings.absolute); - cy.get('[data-test=SettingsView]').should('be.visible'); - }); - }); -}); diff --git a/client/cypress/e2e/settings.cy.ts b/client/cypress/e2e/settings.cy.ts deleted file mode 100644 index 087b77329e..0000000000 --- a/client/cypress/e2e/settings.cy.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { visitWithLoader, navigateWithCheck, mockCoinPricesServer } from 'cypress/utils/e2e'; -import viewports from 'cypress/utils/viewports'; -import { FIAT_CURRENCIES_SYMBOLS, DISPLAY_CURRENCIES } from 'src/constants/currencies'; -import { - ARE_OCTANT_TIPS_ALWAYS_VISIBLE, - DISPLAY_CURRENCY, - IS_CRYPTO_MAIN_VALUE_DISPLAY, - IS_ONBOARDING_ALWAYS_VISIBLE, - IS_ONBOARDING_DONE, -} from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; -import getValueCryptoToDisplay from 'src/utils/getValueCryptoToDisplay'; - -Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { - describe(`settings: ${device}`, { viewportHeight, viewportWidth }, () => { - beforeEach(() => { - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - visitWithLoader(ROOT_ROUTES.settings.absolute); - }); - - it('"Always show Allocate onboarding" option toggle works', () => { - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').check(); - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_ONBOARDING_ALWAYS_VISIBLE)).eq('true'); - }); - - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').click(); - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('not.be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_ONBOARDING_ALWAYS_VISIBLE)).eq('false'); - }); - - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').click(); - cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_ONBOARDING_ALWAYS_VISIBLE)).eq('true'); - }); - }); - - it('"Use crypto as main value display" option is checked by default', () => { - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('true'); - }); - }); - - it('"Use crypto as main value display" option toggle works', () => { - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').check(); - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('true'); - }); - - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').click(); - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('not.be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('false'); - }); - - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').click(); - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('true'); - }); - }); - - it('"Use crypto as main value display" option by default displays crypto value as primary in DoubleValue component', () => { - navigateWithCheck(ROOT_ROUTES.earn.absolute); - - const cryptoValue = getValueCryptoToDisplay({ - cryptoCurrency: 'golem', - valueCrypto: BigInt(0), - }); - - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__primary]') - .invoke('text') - .should('eq', cryptoValue); - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]') - .invoke('text') - .should('not.eq', cryptoValue); - }); - - it('"Use crypto as main value display" option changes DoubleValue sections order', () => { - cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').uncheck(); - navigateWithCheck(ROOT_ROUTES.earn.absolute); - - const cryptoValue = getValueCryptoToDisplay({ - cryptoCurrency: 'golem', - valueCrypto: BigInt(0), - }); - - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__primary]') - .invoke('text') - .should('not.eq', cryptoValue); - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]') - .invoke('text') - .should('eq', cryptoValue); - }); - - it('"Choose a display currency" option works', () => { - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(DISPLAY_CURRENCY)).eq('"usd"'); - }); - - for (let i = 0; i < DISPLAY_CURRENCIES.length - 1; i++) { - const displayCurrency = DISPLAY_CURRENCIES[i]; - const displayCurrencyToUppercase = displayCurrency.toUpperCase(); - const nextDisplayCurrencyToUppercase = - i < DISPLAY_CURRENCIES.length - 1 ? DISPLAY_CURRENCIES[i + 1].toUpperCase() : undefined; - - cy.get('[data-test=SettingsCurrencyBox__InputSelect--currency__SingleValue]').contains( - displayCurrencyToUppercase, - ); - navigateWithCheck(ROOT_ROUTES.earn.absolute); - - if (FIAT_CURRENCIES_SYMBOLS[displayCurrency]) { - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]').contains( - FIAT_CURRENCIES_SYMBOLS[displayCurrency], - ); - } else { - cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]').contains( - displayCurrencyToUppercase, - ); - } - - navigateWithCheck(ROOT_ROUTES.settings.absolute); - cy.get('[data-test=SettingsCurrencyBox__InputSelect--currency]').click(); - cy.get( - `[data-test=SettingsCurrencyBox__InputSelect--currency__Option--${nextDisplayCurrencyToUppercase}]`, - ).click(); - } - }); - - it('"Always show Octant tips" option toggle works', () => { - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').check(); - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(ARE_OCTANT_TIPS_ALWAYS_VISIBLE)).eq('true'); - }); - - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').click(); - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').should('not.be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(ARE_OCTANT_TIPS_ALWAYS_VISIBLE)).eq('false'); - }); - - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').click(); - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').should('be.checked'); - cy.getAllLocalStorage().then(() => { - expect(localStorage.getItem(ARE_OCTANT_TIPS_ALWAYS_VISIBLE)).eq('true'); - }); - }); - - it('"Always show Octant tips" works (checked)', () => { - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').check(); - - navigateWithCheck(ROOT_ROUTES.earn.absolute); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('exist'); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('be.visible'); - - cy.get('[data-test=EarnView__TipTile--connectWallet__Button]').click(); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('not.exist'); - - cy.reload(); - - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('exist'); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('be.visible'); - }); - - it('"Always show Octant tips" works (unchecked)', () => { - cy.get('[data-test=SettingsShowTipsBox__InputToggle]').uncheck(); - - navigateWithCheck(ROOT_ROUTES.earn.absolute); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('exist'); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('be.visible'); - - cy.get('[data-test=EarnView__TipTile--connectWallet__Button]').click(); - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('not.exist'); - - cy.reload(); - - cy.get('[data-test=EarnView__TipTile--connectWallet]').should('not.exist'); - }); - }); -}); From a6bc80c2ee04df06a6a97abab9487bddd82d3cf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sat, 13 Apr 2024 00:17:11 +0200 Subject: [PATCH 04/20] test: call mutateAsyncMakeSnapshot --- client/cypress/e2e/_2makePendingSnapshot.cy.ts | 9 ++++----- client/cypress/utils/moveTime.ts | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index a1b96c0ee4..ce15c0e09d 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -1,9 +1,7 @@ -import axios from 'axios'; - import { mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import { mutateAsyncMakeSnapshot } from 'cypress/utils/moveTime'; import { QUERY_KEYS } from 'src/api/queryKeys'; import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; -import env from 'src/env'; import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; // In E2E snapshotter is disabled. Before the first test can be run, pending snapshot needs to be done. @@ -37,10 +35,11 @@ describe('Make pending snapshot', () => { } cy.wrap(null).then(() => { - axios.post(`${env.serverEndpoint}snapshots/pending`).then(() => { - cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); + return mutateAsyncMakeSnapshot(win, 'pending').then(str => { + expect(str).to.eq(true); }); }); + cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); }); }); }); diff --git a/client/cypress/utils/moveTime.ts b/client/cypress/utils/moveTime.ts index e8ca436043..c5e810b3a1 100644 --- a/client/cypress/utils/moveTime.ts +++ b/client/cypress/utils/moveTime.ts @@ -26,7 +26,7 @@ const mutateAsyncMoveToDecisionWindowClosed = (cypressWindow: Cypress.AUTWindow) }); }); -const mutateAsyncMakeSnapshot = ( +export const mutateAsyncMakeSnapshot = ( cypressWindow: Cypress.AUTWindow, type: 'finalized' | 'pending', ): Promise => From 5cf57277cecb66a030c74d8162fd23e1e70dc63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sat, 13 Apr 2024 21:48:18 +0200 Subject: [PATCH 05/20] feat: CY to make videos --- client/synpress.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/client/synpress.config.ts b/client/synpress.config.ts index 3d6311a099..780da56f26 100644 --- a/client/synpress.config.ts +++ b/client/synpress.config.ts @@ -29,6 +29,7 @@ export default defineConfig({ }, supportFile: 'cypress/support/index.ts', }, + video: true, viewportHeight: 1080, viewportWidth: 1920, }); From 0392fb024eede3efad2103e7450aae46972ee784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sat, 13 Apr 2024 21:51:43 +0200 Subject: [PATCH 06/20] test: add debug tools --- .../e2e/allocationItemWindowOpen.cy.ts | 139 ------------------ client/cypress/support/commands.ts | 2 + client/package.json | 1 + client/src/views/SyncView/SyncView.tsx | 28 +++- client/synpress.config.ts | 2 + client/yarn.lock | 18 ++- 6 files changed, 43 insertions(+), 147 deletions(-) delete mode 100644 client/cypress/e2e/allocationItemWindowOpen.cy.ts diff --git a/client/cypress/e2e/allocationItemWindowOpen.cy.ts b/client/cypress/e2e/allocationItemWindowOpen.cy.ts deleted file mode 100644 index 58a9360a9b..0000000000 --- a/client/cypress/e2e/allocationItemWindowOpen.cy.ts +++ /dev/null @@ -1,139 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import chaiColors from 'chai-colors'; - -import { - visitWithLoader, - mockCoinPricesServer, - navigateWithCheck, - connectWallet, -} from 'cypress/utils/e2e'; -import { moveTime, setupAndMoveToPlayground } from 'cypress/utils/moveTime'; -import viewports from 'cypress/utils/viewports'; -import { QUERY_KEYS } from 'src/api/queryKeys'; -import { - ALLOCATION_ITEMS_KEY, - IS_ONBOARDING_ALWAYS_VISIBLE, - IS_ONBOARDING_DONE, -} from 'src/constants/localStorageKeys'; -import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; - -chai.use(chaiColors); - -const budget = '10000000000'; // 10 GWEI. - -describe('allocation (allocation window open)', () => { - describe('move time', () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - it('allocation window is open, when it is not, move time', () => { - setupAndMoveToPlayground(); - - cy.window().then(async win => { - moveTime(win, 'nextEpochDecisionWindowOpen').then(() => { - const isDecisionWindowOpenAfter = win.clientReactQuery.getQueryData( - QUERY_KEYS.isDecisionWindowOpen, - ); - expect(isDecisionWindowOpenAfter).to.be.true; - }); - }); - }); - }); - - Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { - describe(`test cases: ${device}`, { viewportHeight, viewportWidth }, () => { - before(() => { - /** - * Global Metamask setup done by Synpress is not always done. - * Since Synpress needs to have valid provider to fetch the data from contracts, - * setupMetamask is required in each test suite. - */ - cy.setupMetamask(); - }); - - beforeEach(() => { - cy.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget } }); - cy.disconnectMetamaskWalletFromAllDapps(); - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - localStorage.setItem(ALLOCATION_ITEMS_KEY, '[]'); - visitWithLoader(ROOT_ROUTES.projects.absolute); - connectWallet(true, false); - - cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); - cy.get('[data-test^=ProjectsView__ProjectsListItem]') - .eq(0) - .should('be.visible') - .find('[data-test=ProjectsListItem__name]') - .then($text => { - cy.wrap($text.text()).as('projectName'); - }); - - cy.get('[data-test^=ProjectsView__ProjectsListItem') - .eq(0) - .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') - .click(); - navigateWithCheck(ROOT_ROUTES.allocation.absolute); - cy.get('[data-test=AllocationItemSkeleton]').should('not.exist'); - }); - - it('AllocationItem shows all the elements', () => { - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__name]') - .then($allocationItemName => { - cy.get('@projectName').then(projectName => { - expect(projectName).to.eq($allocationItemName.text()); - }); - }); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__imageProfile]') - .should(isDesktop ? 'be.visible' : 'not.be.visible'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .should('be.enabled'); - }); - - it('AllocationItem__InputText correctly changes background color on focus', () => { - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .focus(); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .should('have.focus'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .should('have.css', 'background-color') - .and('be.colored', '#f1faf8'); - }); - - it('AllocationItem__InputText correctly changes background color on error', () => { - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText__suffix]') - .contains('GWEI'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .type('99'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .should('have.css', 'background-color') - .and('be.colored', '#f1faf8'); - }); - }); - }); -}); diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index 573fd4a276..08d4355b56 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -1,3 +1,5 @@ /* eslint-disable import/no-extraneous-dependencies */ import '@synthetixio/synpress/support/commands'; /* eslint-enable import/no-extraneous-dependencies */ +/* eslint-disable */ +require('cypress-terminal-report/src/installLogsCollector')(); diff --git a/client/package.json b/client/package.json index 51c963c810..fd76602d6c 100644 --- a/client/package.json +++ b/client/package.json @@ -84,6 +84,7 @@ "@vitejs/plugin-react": "4.2.1", "chai-colors": "^1.0.1", "css-mediaquery": "^0.1.2", + "cypress-terminal-report": "^6.0.1", "cypress-vite": "^1.5.0", "eslint": "8.56.0", "eslint-config-airbnb": "^19.0.4", diff --git a/client/src/views/SyncView/SyncView.tsx b/client/src/views/SyncView/SyncView.tsx index 3972f5b889..ca7884cce2 100644 --- a/client/src/views/SyncView/SyncView.tsx +++ b/client/src/views/SyncView/SyncView.tsx @@ -3,16 +3,30 @@ import { Trans } from 'react-i18next'; import Img from 'components/ui/Img'; import Svg from 'components/ui/Svg'; +import useCurrentEpoch from 'hooks/queries/useCurrentEpoch'; +import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen'; +import useEpochsIndexedBySubgraph from 'hooks/subgraph/useEpochsIndexedBySubgraph'; import { octantSemiTransparent } from 'svg/logo'; import styles from './SyncView.module.scss'; -const SyncView = (): ReactElement => ( -
- - - -
-); +const SyncView = (): ReactElement => { + const { data: isDecisionWindowOpn } = useIsDecisionWindowOpen(); + const { data: currentEpoch } = useCurrentEpoch(); + const { data: epochs } = useEpochsIndexedBySubgraph(); + + return ( +
+
+ {isDecisionWindowOpn ? 'true' : 'false'} +
+
{currentEpoch}
+
{epochs}
+ + + +
+ ); +}; export default SyncView; diff --git a/client/synpress.config.ts b/client/synpress.config.ts index 780da56f26..965dca282f 100644 --- a/client/synpress.config.ts +++ b/client/synpress.config.ts @@ -10,6 +10,8 @@ export default defineConfig({ baseUrl: process.env.OCTANT_BASE_URL || 'http://localhost:5173', defaultCommandTimeout: 120 * 1000, setupNodeEvents(on, config) { + // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require,import/no-extraneous-dependencies + require('cypress-terminal-report/src/installLogsPrinter')(on); // eslint-disable-next-line no-param-reassign config.env = { ...config.env, CI: process.env.CI }; on( diff --git a/client/yarn.lock b/client/yarn.lock index 1cb584c600..394bb1edba 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -5556,6 +5556,17 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== +cypress-terminal-report@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/cypress-terminal-report/-/cypress-terminal-report-6.0.1.tgz#976522a49e3ba6a6580791559d5bdb8f6074a25b" + integrity sha512-I+kiUIJRwmdOATjqHGM84PfJZM3TpyPzcUgcpxX2PG04fIPXIWprSMAxc3hbFKP8gE8B7D9Nj9yzf7CwyOH5Iw== + dependencies: + chalk "^4.0.0" + fs-extra "^10.1.0" + process "^0.11.10" + semver "^7.5.4" + tv4 "^1.3.0" + cypress-vite@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/cypress-vite/-/cypress-vite-1.5.0.tgz#471ecc1175c7ab51b3b132c595dc3c7e222fe944" @@ -7514,7 +7525,7 @@ fs-extra@9.1.0, fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^10.0.0: +fs-extra@^10.0.0, fs-extra@^10.1.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -12588,6 +12599,11 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tv4@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" + integrity sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" From 91c4ca7c3094a6924500948d7da38a004ad792bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sat, 13 Apr 2024 22:14:06 +0200 Subject: [PATCH 07/20] test: add debug tools --- client/cypress/support/commands.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index 08d4355b56..5e47fa3e8d 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -1,5 +1,6 @@ /* eslint-disable import/no-extraneous-dependencies */ import '@synthetixio/synpress/support/commands'; -/* eslint-enable import/no-extraneous-dependencies */ /* eslint-disable */ -require('cypress-terminal-report/src/installLogsCollector')(); +import installLogsCollector from 'cypress-terminal-report/src/installLogsCollector'; +installLogsCollector(); +/* eslint-enable import/no-extraneous-dependencies */ From 48e78aa746709728d8a5e047bd80f5a7394317a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 00:02:07 +0200 Subject: [PATCH 08/20] test: debugging --- client/cypress/e2e/_2makePendingSnapshot.cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index ce15c0e09d..4832aeab5a 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -35,8 +35,8 @@ describe('Make pending snapshot', () => { } cy.wrap(null).then(() => { - return mutateAsyncMakeSnapshot(win, 'pending').then(str => { - expect(str).to.eq(true); + return mutateAsyncMakeSnapshot(win, 'pending').then(() => { + cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); }); }); cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); From e2b82ea6f672474240c5f514ef4c505e9669c559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 08:21:54 +0200 Subject: [PATCH 09/20] test: debugging --- client/cypress/e2e/_2makePendingSnapshot.cy.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index 4832aeab5a..ce15c0e09d 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -35,8 +35,8 @@ describe('Make pending snapshot', () => { } cy.wrap(null).then(() => { - return mutateAsyncMakeSnapshot(win, 'pending').then(() => { - cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); + return mutateAsyncMakeSnapshot(win, 'pending').then(str => { + expect(str).to.eq(true); }); }); cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); From 596fbde219697d0d1aa3d22e7d34205dbd86ccf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 09:55:06 +0200 Subject: [PATCH 10/20] test: debugging --- client/cypress/e2e/_2makePendingSnapshot.cy.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index ce15c0e09d..5d05c68c54 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -42,4 +42,8 @@ describe('Make pending snapshot', () => { cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); }); }); + + it('playground', () => { + cy.wait(30000); + }) }); From 41cccf6a58fec604ea0506f39e380d5b34282e36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 21:15:03 +0200 Subject: [PATCH 11/20] test: debugging --- client/src/hooks/helpers/useAppIsLoading.ts | 3 +++ client/src/views/SyncView/SyncView.tsx | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/client/src/hooks/helpers/useAppIsLoading.ts b/client/src/hooks/helpers/useAppIsLoading.ts index df2805438e..bb3b8b48cc 100644 --- a/client/src/hooks/helpers/useAppIsLoading.ts +++ b/client/src/hooks/helpers/useAppIsLoading.ts @@ -1,5 +1,6 @@ import useCurrentEpoch from 'hooks/queries/useCurrentEpoch'; import useIsContract from 'hooks/queries/useIsContract'; +import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen'; import useIsPatronMode from 'hooks/queries/useIsPatronMode'; import useUserTOS from 'hooks/queries/useUserTOS'; import useAllProjects from 'hooks/subgraph/useAllProjects'; @@ -11,6 +12,7 @@ import getIsPreLaunch from 'utils/getIsPreLaunch'; export default function useAppIsLoading(isFlushRequired: boolean): boolean { const { isFetching: isFetchingAllProjects } = useAllProjects(); + const { isLoading: isLoadingIsDecisionWindowOpen } = useIsDecisionWindowOpen(); const { isFetching: isFetchingPatronModeStatus } = useIsPatronMode(); const { isFetching: isFetchingUserTOS, isRefetching: isRefetchingUserTOS } = useUserTOS(); const { data: currentEpoch, isLoading: isLoadingCurrentEpoch } = useCurrentEpoch(); @@ -31,6 +33,7 @@ export default function useAppIsLoading(isFlushRequired: boolean): boolean { const { isFetching: isFetchingIsContract } = useIsContract(); return ( + isLoadingIsDecisionWindowOpen || isLoadingCurrentEpoch || (!isPreLaunch && !isAllocationsInitialized) || !isOnboardingInitialized || diff --git a/client/src/views/SyncView/SyncView.tsx b/client/src/views/SyncView/SyncView.tsx index ca7884cce2..44fbd1919f 100644 --- a/client/src/views/SyncView/SyncView.tsx +++ b/client/src/views/SyncView/SyncView.tsx @@ -11,14 +11,14 @@ import { octantSemiTransparent } from 'svg/logo'; import styles from './SyncView.module.scss'; const SyncView = (): ReactElement => { - const { data: isDecisionWindowOpn } = useIsDecisionWindowOpen(); + const { data: isDecisionWindowOpen } = useIsDecisionWindowOpen(); const { data: currentEpoch } = useCurrentEpoch(); const { data: epochs } = useEpochsIndexedBySubgraph(); return (
- {isDecisionWindowOpn ? 'true' : 'false'} + {isDecisionWindowOpen ? 'true' : 'false'}
{currentEpoch}
{epochs}
From 0f06234597cf1deeb14cf3ee9e99287f03d86c17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 21:53:01 +0200 Subject: [PATCH 12/20] test: debugging --- client/cypress/e2e/_2makePendingSnapshot.cy.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index 5d05c68c54..5a37f7d4ed 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -25,15 +25,6 @@ describe('Make pending snapshot', () => { it('make pending snapshot', () => { cy.window().then(async win => { - const isDecisionWindowOpen = win.clientReactQuery.getQueryData( - QUERY_KEYS.isDecisionWindowOpen, - ); - - if (!isDecisionWindowOpen) { - expect(true).to.be.true; - return; - } - cy.wrap(null).then(() => { return mutateAsyncMakeSnapshot(win, 'pending').then(str => { expect(str).to.eq(true); From add007569c200c8be4e3cf88fa91f65118cf7abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 22:07:05 +0200 Subject: [PATCH 13/20] test: debugging --- .../cypress/e2e/_2makePendingSnapshot.cy.ts | 1 - .../e2e/allocationItemWindowOpen.cy.ts | 139 ++++ client/cypress/e2e/allocationRewardsBox.cy.ts | 479 ++++++++++++ client/cypress/e2e/earn.cy.ts | 275 +++++++ client/cypress/e2e/layout.cy.ts | 126 +++ client/cypress/e2e/metrics.cy.ts | 126 +++ client/cypress/e2e/onboarding.cy.ts | 380 +++++++++ client/cypress/e2e/patronMode.cy.ts | 724 ++++++++++++++++++ client/cypress/e2e/project.cy.ts | 181 +++++ client/cypress/e2e/projects.cy.ts | 239 ++++++ client/cypress/e2e/projectsArchive.cy.ts | 111 +++ client/cypress/e2e/rewardsCalculator.cy.ts | 252 ++++++ client/cypress/e2e/routes.cy.ts | 42 + client/cypress/e2e/settings.cy.ts | 188 +++++ 14 files changed, 3262 insertions(+), 1 deletion(-) create mode 100644 client/cypress/e2e/allocationItemWindowOpen.cy.ts create mode 100644 client/cypress/e2e/allocationRewardsBox.cy.ts create mode 100644 client/cypress/e2e/earn.cy.ts create mode 100644 client/cypress/e2e/layout.cy.ts create mode 100644 client/cypress/e2e/metrics.cy.ts create mode 100644 client/cypress/e2e/onboarding.cy.ts create mode 100644 client/cypress/e2e/patronMode.cy.ts create mode 100644 client/cypress/e2e/project.cy.ts create mode 100644 client/cypress/e2e/projects.cy.ts create mode 100644 client/cypress/e2e/projectsArchive.cy.ts create mode 100644 client/cypress/e2e/rewardsCalculator.cy.ts create mode 100644 client/cypress/e2e/routes.cy.ts create mode 100644 client/cypress/e2e/settings.cy.ts diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index 5a37f7d4ed..d7eca0b0d2 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -1,6 +1,5 @@ import { mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; import { mutateAsyncMakeSnapshot } from 'cypress/utils/moveTime'; -import { QUERY_KEYS } from 'src/api/queryKeys'; import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; diff --git a/client/cypress/e2e/allocationItemWindowOpen.cy.ts b/client/cypress/e2e/allocationItemWindowOpen.cy.ts new file mode 100644 index 0000000000..58a9360a9b --- /dev/null +++ b/client/cypress/e2e/allocationItemWindowOpen.cy.ts @@ -0,0 +1,139 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import chaiColors from 'chai-colors'; + +import { + visitWithLoader, + mockCoinPricesServer, + navigateWithCheck, + connectWallet, +} from 'cypress/utils/e2e'; +import { moveTime, setupAndMoveToPlayground } from 'cypress/utils/moveTime'; +import viewports from 'cypress/utils/viewports'; +import { QUERY_KEYS } from 'src/api/queryKeys'; +import { + ALLOCATION_ITEMS_KEY, + IS_ONBOARDING_ALWAYS_VISIBLE, + IS_ONBOARDING_DONE, +} from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +chai.use(chaiColors); + +const budget = '10000000000'; // 10 GWEI. + +describe('allocation (allocation window open)', () => { + describe('move time', () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + it('allocation window is open, when it is not, move time', () => { + setupAndMoveToPlayground(); + + cy.window().then(async win => { + moveTime(win, 'nextEpochDecisionWindowOpen').then(() => { + const isDecisionWindowOpenAfter = win.clientReactQuery.getQueryData( + QUERY_KEYS.isDecisionWindowOpen, + ); + expect(isDecisionWindowOpenAfter).to.be.true; + }); + }); + }); + }); + + Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { + describe(`test cases: ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + cy.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget } }); + cy.disconnectMetamaskWalletFromAllDapps(); + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + localStorage.setItem(ALLOCATION_ITEMS_KEY, '[]'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + connectWallet(true, false); + + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + cy.get('[data-test^=ProjectsView__ProjectsListItem]') + .eq(0) + .should('be.visible') + .find('[data-test=ProjectsListItem__name]') + .then($text => { + cy.wrap($text.text()).as('projectName'); + }); + + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(0) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .click(); + navigateWithCheck(ROOT_ROUTES.allocation.absolute); + cy.get('[data-test=AllocationItemSkeleton]').should('not.exist'); + }); + + it('AllocationItem shows all the elements', () => { + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__name]') + .then($allocationItemName => { + cy.get('@projectName').then(projectName => { + expect(projectName).to.eq($allocationItemName.text()); + }); + }); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__imageProfile]') + .should(isDesktop ? 'be.visible' : 'not.be.visible'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .should('be.enabled'); + }); + + it('AllocationItem__InputText correctly changes background color on focus', () => { + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .focus(); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .should('have.focus'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .should('have.css', 'background-color') + .and('be.colored', '#f1faf8'); + }); + + it('AllocationItem__InputText correctly changes background color on error', () => { + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText__suffix]') + .contains('GWEI'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .type('99'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .should('have.css', 'background-color') + .and('be.colored', '#f1faf8'); + }); + }); + }); +}); diff --git a/client/cypress/e2e/allocationRewardsBox.cy.ts b/client/cypress/e2e/allocationRewardsBox.cy.ts new file mode 100644 index 0000000000..1259263a5e --- /dev/null +++ b/client/cypress/e2e/allocationRewardsBox.cy.ts @@ -0,0 +1,479 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import chaiColors from 'chai-colors'; + +import { visitWithLoader, mockCoinPricesServer, connectWallet } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +chai.use(chaiColors); + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe( + `allocation rewards box (disabled): ${device}`, + { viewportHeight, viewportWidth }, + () => { + beforeEach(() => { + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.allocation.absolute); + }); + + it('is visible', () => { + cy.get('[data-test=AllocationRewardsBox]').should('be.visible'); + }); + + it('has each field with value 0 ETH', () => { + cy.get('[data-test=AllocationRewardsBox__title]').invoke('text').should('eq', '0 ETH'); + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '0 ETH'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '0 ETH'); + }); + + it('shows "No rewards yet" message below rewards value', () => { + cy.get('[data-test=AllocationRewardsBox__subtitle]') + .invoke('text') + .should('eq', 'No rewards yet'); + }); + + it('Clicking on `Donate` label or value doesn`t open modal to editing value', () => { + cy.get('[data-test=AllocationRewardsBox__section--0]').click(); + cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); + }); + + it('Clicking on `Personal` label or value doesn`t open modal to editing value', () => { + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); + }); + + it('slider is visible', () => { + cy.get('[data-test=AllocationRewardsBox__Slider]').should('be.visible'); + }); + + it('slider thumb exists but isn`t visible', () => { + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('exist'); + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('not.be.visible'); + }); + + it('slider track should be filled in 50%', () => { + cy.get('[data-test=AllocationRewardsBox__Slider]').then($sliderEl => { + const { width } = $sliderEl[0].getBoundingClientRect(); + + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( + 'have.css', + 'width', + `${width / 2}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]') + .then($el => $el.css('background-color')) + .should('be.colored', '#9ea39e'); + + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( + 'have.css', + 'width', + `${width / 2}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]') + .then($el => $el.css('background-color')) + .should('be.colored', '#cdd1cd'); + }); + }); + + it('Clicking on `Personal` label or value doesn`t open modal to editing value', () => { + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); + }); + + it('Clicking on `Personal` label or value doesn`t open modal to editing value', () => { + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + cy.get('[data-test=ModalAllocationValuesEdit]').should('not.exist'); + }); + }, + ); + + describe(`allocation rewards box (enabled): ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.allocation.absolute); + cy.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget: '10000000000' } }); + connectWallet(true, false); + }); + + it('is visible', () => { + cy.get('[data-test=AllocationRewardsBox]').should('be.visible'); + }); + + it('shows "Available now" message below rewards value', () => { + cy.get('[data-test=AllocationRewardsBox__subtitle]') + .invoke('text') + .should('eq', 'Available now'); + }); + + it('user has 10 GWEI rewards', () => { + cy.get('[data-test=AllocationRewardsBox__title]').invoke('text').should('eq', '10 GWEI'); + }); + + it('slider thumb exists and is visible', () => { + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('exist'); + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').should('be.visible'); + }); + + it('Clicking on `Donate` label or value opens modal to editing value', () => { + cy.get('[data-test=AllocationRewardsBox__section--0]').click(); + cy.get('[data-test=ModalAllocationValuesEdit]').should('exist').should('be.visible'); + }); + + it('Clicking on `Personal` label or value opens modal to editing value', () => { + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + cy.get('[data-test=ModalAllocationValuesEdit]').should('exist').should('be.visible'); + }); + + it('user can split the value by using slider from 0/100 to 50/50 and then from 50/50 to 100/0', () => { + cy.get('[data-test=AllocationRewardsBox__title]').invoke('text').should('eq', '10 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '0 ETH'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '10 GWEI'); + + cy.get('[data-test=AllocationRewardsBox__Slider]').then($sliderEl => { + const { width: sliderElWidth } = $sliderEl[0].getBoundingClientRect(); + + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + // track 0 is hidden under the thumb + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( + 'have.css', + 'width', + `${sliderButtonDimensions.width}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( + 'have.css', + 'width', + `${sliderElWidth}px`, + ); + + const pageX0 = sliderButtonDimensions.left; + const pageX50 = + sliderButtonDimensions.left - sliderButtonDimensions.width / 2 + sliderElWidth / 2; + const pageX100 = + sliderButtonDimensions.left - sliderButtonDimensions.width / 2 + sliderElWidth; + + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]') + .trigger('mousedown', { + pageX: pageX0, + }) + .trigger('mousemove', { + pageX: pageX50, + }) + .trigger('mouseup', { + pageX: pageX50, + }); + + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( + 'have.css', + 'width', + `${(sliderButtonDimensions.width + sliderElWidth) / 2}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]') + .then($el => $el.css('background-color')) + .should('be.colored', '#2d9b87'); + + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( + 'have.css', + 'width', + `${(sliderButtonDimensions.width + sliderElWidth) / 2}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]') + .then($el => $el.css('background-color')) + .should('be.colored', '#ff9601'); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '5 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '5 GWEI'); + + cy.get('[data-test=AllocationRewardsBox__Slider__thumb]') + .trigger('mousedown', { + pageX: pageX50, + }) + .trigger('mousemove', { + pageX: pageX100, + }) + .trigger('mouseup', { + pageX: pageX100, + }); + + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]').should( + 'have.css', + 'width', + `${sliderElWidth}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--0]') + .then($el => $el.css('background-color')) + .should('be.colored', '#2d9b87'); + + // track 1 is hidden under the thumb + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]').should( + 'have.css', + 'width', + `${sliderButtonDimensions.width}px`, + ); + cy.get('[data-test=AllocationRewardsBox__Slider__track--1]') + .then($el => $el.css('background-color')) + .should('be.colored', '#ff9601'); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '10 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '0 ETH'); + }); + }); + }); + + it('user can change `Donate` value manually in modal', () => { + cy.get('[data-test=AllocationRewardsBox__section--0]').click(); + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Donate 0%'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('be.focused'); + cy.get('[data-test=AllocationInputs__InputText--crypto]') + .then($el => $el.css('border-color')) + .should('be.colored', '#2d9b87'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '0'); + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('not.be.focused'); + + // 10 GWEI + cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.00000001'); + cy.get('[data-test=AllocationInputs__InputText--crypto]') + .then($el => $el.css('border-color')) + .should('be.colored', '#2d9b87'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '10 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '0 ETH'); + + cy.get('[data-test=AllocationRewardsBox__section--0]').click(); + + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Donate 100%'); + + // 100 GWEI + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.0000001'); + cy.get('[data-test=AllocationInputs__InputText--crypto]') + .then($el => $el.css('border-color')) + .should('be.colored', '#FF6157'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); + + cy.get('[data-test=AllocationInputs__Button]').should('be.disabled'); + + cy.get('[data-test=AllocationInputs__InputText--crypto]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '0'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0'); + + // 50 % + cy.get('[data-test=AllocationInputs__InputText--percentage]').type('50'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.000000005'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '5 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '5 GWEI'); + + cy.get('[data-test=AllocationRewardsBox__section--0]').click(); + + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Donate 50%'); + + // 100 % + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').type('100'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '10 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '0 ETH'); + + cy.get('[data-test=AllocationRewardsBox__section--0]').click(); + + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Donate 100%'); + + // 1000 % + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').type('1000'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '10 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '0 ETH'); + }); + + it('user can change `Personal` value manually in modal', () => { + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Personal 100%'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('be.focused'); + cy.get('[data-test=AllocationInputs__InputText--crypto]') + .then($el => $el.css('border-color')) + .should('be.colored', '#2d9b87'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('not.be.focused'); + + // 10 GWEI + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.00000001'); + cy.get('[data-test=AllocationInputs__InputText--crypto]') + .then($el => $el.css('border-color')) + .should('be.colored', '#2d9b87'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '0 ETH'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '10 GWEI'); + + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Personal 100%'); + + // 100 GWEI + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--crypto]').type('0.0000001'); + cy.get('[data-test=AllocationInputs__InputText--crypto]') + .then($el => $el.css('border-color')) + .should('be.colored', '#FF6157'); + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '100'); + + cy.get('[data-test=AllocationInputs__Button]').should('be.disabled'); + + cy.get('[data-test=AllocationInputs__InputText--crypto]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').should('have.value', '0'); + + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0'); + + // 50 % + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').type('50'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.000000005'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '5 GWEI'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '5 GWEI'); + + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Personal 50%'); + + // 100 % + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').type('100'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '0 ETH'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '10 GWEI'); + + cy.get('[data-test=AllocationRewardsBox__section--1]').click(); + + cy.get('[data-test=ModalAllocationValuesEdit__header]') + .invoke('text') + .should('eq', 'Personal 100%'); + + // 1000 % + cy.get('[data-test=AllocationInputs__InputText--percentage]').clear(); + cy.get('[data-test=AllocationInputs__InputText--percentage]').type('1000'); + cy.get('[data-test=AllocationInputs__InputText--crypto]').should('have.value', '0.00000001'); + + cy.get('[data-test=AllocationInputs__Button]').should('not.be.disabled'); + cy.get('[data-test=AllocationInputs__Button]').click(); + + cy.get('[data-test=AllocationRewardsBox__section__value--0]') + .invoke('text') + .should('eq', '0 ETH'); + cy.get('[data-test=AllocationRewardsBox__section__value--1]') + .invoke('text') + .should('eq', '10 GWEI'); + }); + }); +}); diff --git a/client/cypress/e2e/earn.cy.ts b/client/cypress/e2e/earn.cy.ts new file mode 100644 index 0000000000..c3bb07dc3a --- /dev/null +++ b/client/cypress/e2e/earn.cy.ts @@ -0,0 +1,275 @@ +import { visitWithLoader, mockCoinPricesServer } from 'cypress/utils/e2e'; +import { moveTime } from 'cypress/utils/moveTime'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +import Chainable = Cypress.Chainable; + +const connectWallet = (): Chainable => { + cy.intercept('GET', '/user/*/tos', { body: { accepted: true } }); + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').click(); + cy.acceptMetamaskAccess(); + return cy.switchToCypressWindow(); +}; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }, idx) => { + describe(`earn: ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + cy.disconnectMetamaskWalletFromAllDapps(); + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.earn.absolute); + }); + + it('renders "Locked balance" box', () => { + cy.get('[data-test=BoxGlmLock__BoxRounded]').should('be.visible'); + }); + + it('renders "Personal allocation" box', () => { + cy.get('[data-test=BoxPersonalAllocation]').should('be.visible'); + }); + + it('renders "History"', () => { + cy.get('[data-test=History]').should('be.visible'); + }); + + it('"Lock GLM" button is visible', () => { + cy.get('[data-test=BoxGlmLock__Button]').should('be.visible'); + }); + + it('"Lock GLM" button is disabled', () => { + cy.get('[data-test=BoxGlmLock__Button]').should('be.disabled'); + }); + + it('"Withdraw to wallet" button is visible', () => { + cy.get('[data-test=BoxPersonalAllocation__Button]').should('be.visible'); + }); + + it('"Withdraw to wallet" button is disabled', () => { + cy.get('[data-test=BoxPersonalAllocation__Button]').should('be.disabled'); + }); + + it('"Effective" section has tooltip', () => { + cy.get('[data-test=BoxGlmLock__Section--effective]').should('be.visible'); + }); + + if (!isDesktop) { + it('"Effective" section tooltip svg icon opens "TooltipEffectiveLockedBalance"', () => { + cy.get('[data-test=BoxGlmLock__Section--effective__Svg]').click(); + cy.get('[data-test=TooltipEffectiveLockedBalance]').should('be.visible'); + }); + } + + it('Wallet connected: "Lock GLM" / "Edit Locked GLM" button is active', () => { + connectWallet(); + cy.get('[data-test=BoxGlmLock__Button]').should('not.be.disabled'); + }); + + it('Wallet connected: "Lock GLM" / "Edit Locked GLM" button opens "ModalGlmLock"', () => { + connectWallet(); + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=ModalGlmLock]').should('be.visible'); + }); + + it('Wallet connected: "ModalGlmLock" has overflow', () => { + connectWallet(); + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=ModalGlmLock__overflow]').should('exist'); + }); + + it('Wallet connected: inputs allow to type multiple characters without focus problems', () => { + /** + * In EarnGlmLock there are multiple autofocus rules set. + * This test checks if user is still able to type without any autofocus disruption. + */ + connectWallet(); + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=ModalGlmLock]').should('be.visible'); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type('100'); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.value', '100'); + cy.get('[data-test=EarnGlmLockTabs__tab--1]').click(); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type('100'); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.value', '100'); + }); + + it('Wallet connected: "ModalGlmLock" - changing tabs keep focus on first input', () => { + connectWallet(); + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=ModalGlmLock]').should('be.visible'); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); + cy.get('[data-test=EarnGlmLockTabs__tab--1]').click(); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); + cy.get('[data-test=EarnGlmLockTabs__tab--0]').click(); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').should('have.focus'); + }); + + it('Wallet connected: Lock 1 GLM', () => { + connectWallet(); + + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]') + .invoke('text') + .then(text => { + const amountToLock = 1; + const lockedGlms = parseInt(text, 10); + + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type(`${amountToLock}`); + cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Lock'); + cy.get('[data-test=GlmLockTabs__Button]').click(); + cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Waiting for confirmation'); + cy.confirmMetamaskPermissionToSpend({ + spendLimit: '99999999999999999999', + }); + // Workaround for two notifications during first transaction. + // 1. Allow the third party to spend TKN from your current balance. + // 2. Confirm permission to spend + if (Cypress.env('CI') === 'true' && idx === 0) { + cy.wait(1000); + cy.confirmMetamaskPermissionToSpend({ + spendLimit: '99999999999999999999', + }); + } + cy.get('[data-test=GlmLockTabs__Button]', { timeout: 180000 }).should( + 'have.text', + 'Close', + ); + cy.get('[data-test=GlmLockNotification--success]').should('be.visible'); + cy.get('[data-test=GlmLockTabs__Button]').click(); + cy.get( + '[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', + // Small timeout ensures skeleton shows up quickly after the transaction. + { timeout: 1000 }, + ).should('be.visible'); + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]', { + timeout: 60000, + }) + .invoke('text') + .then(nextText => { + const lockedGlmsAfterLock = parseInt(nextText, 10); + expect(lockedGlms + amountToLock).to.be.eq(lockedGlmsAfterLock); + }); + cy.get('[data-test=HistoryItem__title]').first().should('have.text', 'Locked GLM'); + cy.get('[data-test=HistoryItem__DoubleValue__primary]') + .first() + .should('have.text', '1 GLM'); + }); + }); + + it('Wallet connected: Unlock 1 GLM', () => { + connectWallet(); + + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]') + .invoke('text') + .then(text => { + const amountToUnlock = 1; + const lockedGlms = parseInt(text, 10); + + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=EarnGlmLockTabs__tab--1]').click(); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]') + .clear() + .type(`${amountToUnlock}`); + cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Unlock'); + cy.get('[data-test=GlmLockTabs__Button]').click(); + cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Waiting for confirmation'); + cy.confirmMetamaskPermissionToSpend({ + shouldWaitForPopupClosure: true, + spendLimit: '99999999999999999999', + }); + cy.get('[data-test=GlmLockTabs__Button]', { timeout: 60000 }).should( + 'have.text', + 'Close', + ); + cy.get('[data-test=GlmLockNotification--success]').should('be.visible'); + cy.get('[data-test=GlmLockTabs__Button]').click(); + cy.get( + '[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', + // Small timeout ensures skeleton shows up quickly after the transaction. + { timeout: 1000 }, + ).should('be.visible'); + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]', { + timeout: 60000, + }) + .invoke('text') + .then(nextText => { + const lockedGlmsAfterUnlock = parseInt(nextText, 10); + expect(lockedGlms - amountToUnlock).to.be.eq(lockedGlmsAfterUnlock); + }); + cy.get('[data-test=HistoryItem__title]').first().should('have.text', 'Unlocked GLM'); + cy.get('[data-test=HistoryItem__DoubleValue__primary]') + .first() + .should('have.text', '1 GLM'); + }); + }); + + it('Wallet connected: Effective deposit after locking 1000 GLM and moving epoch is equal to current deposit', () => { + connectWallet(); + + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]') + .invoke('text') + .then(text => { + const amountToLock = 1000; + const lockedGlms = parseInt(text.replace(/\u200a/g, ''), 10); + + cy.get('[data-test=BoxGlmLock__Button]').click(); + cy.get('[data-test=InputsCryptoFiat__InputText--crypto]').clear().type(`${amountToLock}`); + cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Lock'); + cy.get('[data-test=GlmLockTabs__Button]').click(); + cy.get('[data-test=GlmLockTabs__Button]').should('have.text', 'Waiting for confirmation'); + cy.confirmMetamaskPermissionToSpend({ + spendLimit: '99999999999999999999', + }); + cy.get('[data-test=GlmLockTabs__Button]', { timeout: 180000 }).should( + 'have.text', + 'Close', + ); + cy.get('[data-test=GlmLockNotification--success]').should('be.visible'); + cy.get('[data-test=GlmLockTabs__Button]').click(); + cy.get( + '[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', + // Small timeout ensures skeleton shows up quickly after the transaction. + { timeout: 1000 }, + ).should('be.visible'); + // Waiting for skeletons to disappear ensures Graph indexed lock/unlock. + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__DoubleValueSkeleton]', { + timeout: 60000, + }).should('not.exist'); + cy.window().then(async win => { + cy.wrap(null).then(() => { + return moveTime(win, 'nextEpochDecisionWindowClosed').then(() => { + cy.get('[data-test=BoxGlmLock__Section--current__DoubleValue__primary]', { + timeout: 60000, + }) + .invoke('text') + .then(nextText => { + const lockedGlmsAfterLock = parseInt(nextText.replace(/\u200a/g, ''), 10); + expect(lockedGlms + amountToLock).to.be.eq(lockedGlmsAfterLock); + }); + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__primary]', { + timeout: 60000, + }) + .invoke('text') + .then(nextText => { + const lockedGlmsAfterLock = parseInt(nextText.replace(/\u200a/g, ''), 10); + expect(lockedGlms + amountToLock).to.be.eq(lockedGlmsAfterLock); + }); + }); + }); + }); + }); + }); + }); +}); diff --git a/client/cypress/e2e/layout.cy.ts b/client/cypress/e2e/layout.cy.ts new file mode 100644 index 0000000000..9ea9954d24 --- /dev/null +++ b/client/cypress/e2e/layout.cy.ts @@ -0,0 +1,126 @@ +import { navigateWithCheck, mockCoinPricesServer } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { navigationTabs } from 'src/constants/navigationTabs/navigationTabs'; +import { ROOT, ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`layout: ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + cy.clearLocalStorage(); + cy.setupMetamask(); + }); + + beforeEach(() => { + mockCoinPricesServer(); + cy.disconnectMetamaskWalletFromAllDapps(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + cy.visit(ROOT.absolute); + }); + + it('renders top bar', () => { + cy.get('[data-test=MainLayout__Header]').should('be.visible'); + }); + + it('Clicking on Octant logo scrolls view to the top on logo click (projects view)', () => { + cy.scrollTo(0, 500); + cy.get('[data-test=MainLayout__Logo]').click(); + // waiting for scrolling to finish + cy.wait(2000); + cy.window().then(cyWindow => { + expect(cyWindow.scrollY).to.be.eq(0); + }); + }); + + it('Clicking on Octant logo redirects to projects view (outside projects view)', () => { + navigateWithCheck(ROOT_ROUTES.settings.absolute); + cy.get('[data-test=SettingsView]').should('be.visible'); + cy.get('[data-test=MainLayout__Logo]').click(); + cy.get('[data-test=ProjectsView]').should('be.visible'); + }); + + it('Clicking on Octant logo redirects to projects view (outside projects view) with memorized scrollY', () => { + cy.scrollTo(0, 500); + navigateWithCheck(ROOT_ROUTES.settings.absolute); + cy.get('[data-test=SettingsView]').should('be.visible'); + cy.get('[data-test=MainLayout__Logo]').click(); + cy.get('[data-test=ProjectsView]').should('be.visible'); + cy.window().then(cyWindow => { + expect(cyWindow.scrollY).to.be.eq(500); + }); + }); + + it('renders bottom navbar', () => { + cy.get('[data-test=Navbar]').should('be.visible'); + }); + + it('bottom navbar allows to change views', () => { + navigationTabs.forEach(({ to }) => { + navigateWithCheck(to); + }); + }); + + it('"Connect" button is visible when wallet is disconnected', () => { + cy.get('[data-test=MainLayout__Button--connect]').should('be.visible'); + cy.get('[data-test=MainLayout__Button--connect]').click(); + }); + + it('"Connect" button opens "ModalConnectWallet"', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet]').should('be.visible'); + }); + + it('"ModalConnectWallet" always shows "WalletConnect" option', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet]').within(() => { + cy.get('[data-test=ConnectWallet__BoxRounded--walletConnect]').should('be.visible'); + }); + }); + + it('"ModalConnectWallet" shows "Browser wallet" and "WalletConnect" options (MetaMask wallet detected)', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet]').within(() => { + cy.get('[data-test=ConnectWallet__BoxRounded--walletConnect]').should('be.visible'); + cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').should('be.visible'); + }); + }); + + it('"ModalConnectWallet" has overflow enabled', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet__overflow]').should('exist'); + }); + + it('Clicking background when "ModalConnectWallet" is open, closes Modal', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet__overflow]').click({ force: true }); + cy.get('[data-test=ModalConnectWallet]').should('not.exist'); + }); + + it('"ModalConnectWallet" has "cross" icon button in header', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet__Button]').should('be.visible'); + }); + + it('Clicking on "X" mark in "ModalConnectWallet", closes Modal', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ModalConnectWallet__Button]').click(); + cy.get('[data-test=ModalConnectWallet]').should('not.exist'); + }); + + it('Clicking on "WalletConnect" option, opens Web3Modal', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ConnectWallet__BoxRounded--walletConnect]').click(); + cy.get('w3m-modal').find('#w3m-modal', { includeShadowDom: true }).should('be.visible'); + }); + + it('Clicking on "Browser wallet" option connects with MetaMask wallet', () => { + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').click(); + cy.switchToMetamaskNotification(); + cy.acceptMetamaskAccess(); + cy.get('[data-test=MainLayout__Button--connect]').should('not.exist'); + cy.get('[data-test=ProfileInfo]').should('exist'); + }); + }); +}); diff --git a/client/cypress/e2e/metrics.cy.ts b/client/cypress/e2e/metrics.cy.ts new file mode 100644 index 0000000000..829fbfbf1c --- /dev/null +++ b/client/cypress/e2e/metrics.cy.ts @@ -0,0 +1,126 @@ +import { mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { + describe(`metrics: ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.metrics.absolute); + }); + + it('renders total projects tile', () => { + cy.get('[data-test=MetricsGeneralGridTotalProjects]').should('be.visible'); + }); + + it('renders total eth staked tile', () => { + cy.get('[data-test=MetricsGeneralGridTotalEthStaked]').should('be.visible'); + }); + + it('renders tile with total glm locked and % of 1B total supply groups', () => { + cy.get('[data-test=MetricsGeneralGridTotalGlmLockedAndTotalSupply]').should('be.visible'); + cy.get('[data-test=MetricsGeneralGridTotalGlmLockedAndTotalSupply]') + .children() + .should('have.length', 2); + }); + + it('renders wallet with glm locked graph tile', () => { + cy.get('[data-test=MetricsGeneralGridWalletsWithGlmLocked]').should('be.visible'); + }); + + it('renders cumulative glm locked graph tile', () => { + cy.get('[data-test=MetricsGeneralGridCumulativeGlmLocked]').should('be.visible'); + }); + + it('renders tiles in correct order', () => { + const metricsEpochGridTilesDataTest = [ + 'MetricsEpochGridTopProjects', + 'MetricsEpochGridTotalDonationsAndPersonal', + 'MetricsEpochGridDonationsVsPersonalAllocations', + 'MetricsEpochGridFundsUsage', + 'MetricsEpochGridTotalUsers', + 'MetricsEpochGridPatrons', + 'MetricsEpochGridCurrentDonors', + 'MetricsEpochGridAverageLeverage', + 'MetricsEpochGridRewardsUnusedAndUnallocatedValue', + 'MetricsEpochGridBelowThreshold', + ]; + + const metricsGeneralGridTilesDataTest = [ + 'MetricsGeneralGridTotalGlmLockedAndTotalSupply', + 'MetricsGeneralGridTotalProjects', + 'MetricsGeneralGridTotalEthStaked', + 'MetricsGeneralGridCumulativeGlmLocked', + 'MetricsGeneralGridWalletsWithGlmLocked', + ]; + + cy.get('[data-test=MetricsEpoch__MetricsGrid]') + .children() + .should('have.length', metricsEpochGridTilesDataTest.length); + + for (let i = 0; i < metricsEpochGridTilesDataTest.length; i++) { + cy.get('[data-test=MetricsEpoch__MetricsGrid]') + .children() + .eq(i) + .invoke('data', 'test') + .should('eq', metricsEpochGridTilesDataTest[i]); + } + + cy.get('[data-test=MetricsGeneral__MetricsGrid]') + .children() + .should('have.length', metricsGeneralGridTilesDataTest.length); + + for (let i = 0; i < metricsGeneralGridTilesDataTest.length; i++) { + cy.get('[data-test=MetricsGeneral__MetricsGrid]') + .children() + .eq(i) + .invoke('data', 'test') + .should('eq', metricsGeneralGridTilesDataTest[i]); + } + }); + + it('renders grid with 4 columns on desktop or with 2 columns on other devices', () => { + cy.get('[data-test=MetricsEpoch__MetricsGrid]').then(el => { + const width = parseInt(el.css('width'), 10); + const rowGap = parseInt(el.css('rowGap'), 10); + + const columnWidth = isDesktop ? (width - 3 * rowGap) / 4 : (width - rowGap) / 2; + + cy.get('[data-test=MetricsEpoch__MetricsGrid]').should( + 'have.css', + 'grid-template-columns', + isDesktop + ? `${columnWidth}px ${columnWidth}px ${columnWidth}px ${columnWidth}px` + : `${columnWidth}px ${columnWidth}px`, + ); + }); + + cy.get('[data-test=MetricsGeneral__MetricsGrid]').then(el => { + const width = parseInt(el.css('width'), 10); + const rowGap = parseInt(el.css('rowGap'), 10); + + const columnWidth = isDesktop ? (width - 3 * rowGap) / 4 : (width - rowGap) / 2; + + cy.get('[data-test=MetricsGeneral__MetricsGrid]').should( + 'have.css', + 'grid-template-columns', + isDesktop + ? `${columnWidth}px ${columnWidth}px ${columnWidth}px ${columnWidth}px` + : `${columnWidth}px ${columnWidth}px`, + ); + }); + }); + }); +}); diff --git a/client/cypress/e2e/onboarding.cy.ts b/client/cypress/e2e/onboarding.cy.ts new file mode 100644 index 0000000000..3dec91c7b6 --- /dev/null +++ b/client/cypress/e2e/onboarding.cy.ts @@ -0,0 +1,380 @@ +import { visitWithLoader, navigateWithCheck, mockCoinPricesServer } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { getStepsDecisionWindowClosed } from 'src/hooks/helpers/useOnboardingSteps/steps'; +import { ROOT, ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +import Chainable = Cypress.Chainable; + +const connectWallet = ( + isTOSAccepted: boolean, + shouldVisit = true, + shouldReload = false, +): Chainable => { + cy.intercept('GET', '/user/*/tos', { body: { accepted: isTOSAccepted } }); + cy.disconnectMetamaskWalletFromAllDapps(); + if (shouldVisit) { + visitWithLoader(ROOT.absolute, ROOT_ROUTES.projects.absolute); + } + if (shouldReload) { + cy.reload(); + } + cy.get('[data-test=MainLayout__Button--connect]').click(); + cy.get('[data-test=ConnectWallet__BoxRounded--browserWallet]').click(); + cy.switchToMetamaskNotification(); + return cy.acceptMetamaskAccess(); +}; + +const beforeSetup = () => { + mockCoinPricesServer(); + cy.clearLocalStorage(); + cy.setupMetamask(); + window.innerWidth = Cypress.config().viewportWidth; + window.innerHeight = Cypress.config().viewportHeight; +}; + +const checkCurrentElement = (el: number, isCurrent: boolean): Chainable => { + return cy + .get('[data-test=ModalOnboarding__ProgressStepperSlim__element]') + .eq(el) + .invoke('attr', 'data-iscurrent') + .should('eq', `${isCurrent}`); +}; + +const checkProgressStepperSlimIsCurrentAndClickNext = (index, isCurrent = true): Chainable => { + checkCurrentElement(index - 1, isCurrent); + return cy + .get('[data-test=ModalOnboarding__ProgressStepperSlim__element]') + .eq(index) + .click({ force: true }); +}; + +const checkChangeStepsWithArrowKeys = (isTOSAccepted: boolean) => { + checkCurrentElement(0, true); + + [ + { el: 1, key: 'ArrowRight' }, + { el: 2, key: 'ArrowRight' }, + // { el: 3, key: 'ArrowRight' }, + // { el: 3, key: 'ArrowRight' }, + // { el: 2, key: 'ArrowLeft' }, + { el: 1, key: 'ArrowLeft' }, + { el: 0, key: 'ArrowLeft' }, + { el: 0, key: 'ArrowLeft' }, + ].forEach(({ key, el }) => { + cy.get('body').trigger('keydown', { key }); + checkCurrentElement(el, isTOSAccepted || el === 0); + + if (!isTOSAccepted) { + checkCurrentElement(0, true); + } + }); +}; + +const checkChangeStepsByClickingEdgeOfTheScreenUpTo25px = (isTOSAccepted: boolean) => { + checkCurrentElement(0, true); + + cy.get('[data-test=ModalOnboarding]').then(element => { + const leftEdgeX = element.offsetParent().offset()?.left as number; + const rightEdgeX = (leftEdgeX as number) + element.innerWidth()!; + + [ + { clientX: rightEdgeX - 25, el: 1 }, + { clientX: rightEdgeX - 10, el: 2 }, + // { clientX: rightEdgeX - 5, el: 3 }, + // rightEdgeX === browser right frame + // { clientX: rightEdgeX - 1, el: 3 }, + // { clientX: leftEdgeX + 25, el: 2 }, + { clientX: leftEdgeX + 10, el: 1 }, + { clientX: leftEdgeX + 5, el: 0 }, + { clientX: leftEdgeX, el: 0 }, + ].forEach(({ clientX, el }) => { + cy.get('[data-test=ModalOnboarding]').click(clientX, element.height()! / 2); + checkCurrentElement(el, isTOSAccepted || el === 0); + + if (!isTOSAccepted) { + checkCurrentElement(0, true); + } + }); + }); +}; + +const checkChangeStepsByClickingEdgeOfTheScreenMoreThan25px = (isTOSAccepted: boolean) => { + checkCurrentElement(0, true); + + cy.get('[data-test=ModalOnboarding]').then(element => { + const leftEdgeX = element.offsetParent().offset()?.left as number; + const rightEdgeX = (leftEdgeX as number) + element.innerWidth()!; + + [ + { clientX: rightEdgeX - 25, el: 1 }, + { clientX: rightEdgeX - 26, el: 1 }, + { clientX: leftEdgeX + 26, el: 1 }, + { clientX: leftEdgeX + 25, el: 0 }, + ].forEach(({ clientX, el }) => { + cy.get('[data-test=ModalOnboarding]').click(clientX, element.height()! / 2); + checkCurrentElement(el, isTOSAccepted || el === 0); + + if (!isTOSAccepted) { + checkCurrentElement(0, true); + } + }); + }); +}; + +const checkChangeStepsBySwipingOnScreenDifferenceMoreThanOrEqual5px = (isTOSAccepted: boolean) => { + checkCurrentElement(0, true); + + [ + { + el: 1, + touchMoveClientX: window.innerWidth / 2 - 5, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 2, + touchMoveClientX: window.innerWidth / 2 - 5, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 2, + touchMoveClientX: window.innerWidth / 2 - 5, + touchStartClientX: window.innerWidth / 2, + }, + // { + // el: 3, + // touchMoveClientX: window.innerWidth / 2 - 5, + // touchStartClientX: window.innerWidth / 2, + // }, + { + el: 2, + touchMoveClientX: window.innerWidth / 2 + 5, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 1, + touchMoveClientX: window.innerWidth / 2 + 5, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 0, + touchMoveClientX: window.innerWidth / 2 + 5, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 0, + touchMoveClientX: window.innerWidth / 2 + 5, + touchStartClientX: window.innerWidth / 2, + }, + ].forEach(({ touchStartClientX, touchMoveClientX, el }) => { + cy.get('[data-test=ModalOnboarding]').trigger('touchstart', { + touches: [{ clientX: touchStartClientX }], + }); + cy.get('[data-test=ModalOnboarding]').trigger('touchmove', { + touches: [{ clientX: touchMoveClientX }], + }); + checkCurrentElement(el, isTOSAccepted || el === 0); + + if (!isTOSAccepted) { + checkCurrentElement(0, true); + } + }); +}; + +const checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px = (isTOSAccepted: boolean) => { + checkCurrentElement(0, true); + + [ + { + el: 1, + touchMoveClientX: window.innerWidth / 2 - 5, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 1, + touchMoveClientX: window.innerWidth / 2 - 4, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 1, + touchMoveClientX: window.innerWidth / 2 + 4, + touchStartClientX: window.innerWidth / 2, + }, + { + el: 0, + touchMoveClientX: window.innerWidth / 2 + 5, + touchStartClientX: window.innerWidth / 2, + }, + ].forEach(({ touchStartClientX, touchMoveClientX, el }) => { + cy.get('[data-test=ModalOnboarding]').trigger('touchstart', { + touches: [{ clientX: touchStartClientX }], + }); + cy.get('[data-test=ModalOnboarding]').trigger('touchmove', { + touches: [{ clientX: touchMoveClientX }], + }); + checkCurrentElement(el, isTOSAccepted || el === 0); + + if (!isTOSAccepted) { + checkCurrentElement(0, true); + } + }); +}; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`onboarding (TOS accepted): ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + beforeSetup(); + }); + + beforeEach(() => { + connectWallet(true); + }); + + it('user is able to click through entire onboarding flow', () => { + for (let i = 1; i < getStepsDecisionWindowClosed('2', '16 Jan').length - 1; i++) { + checkProgressStepperSlimIsCurrentAndClickNext(i); + } + + cy.get('[data-test=ModalOnboarding__ProgressStepperSlim__element]') + .eq(getStepsDecisionWindowClosed('2', '16 Jan').length - 1) + .click(); + cy.get('[data-test=ModalOnboarding__Button]').click(); + cy.get('[data-test=ModalOnboarding]').should('not.exist'); + cy.get('[data-test=ProjectsView__ProjectsList]').should('be.visible'); + }); + + it('user is able to close the modal by clicking button in the top-right', () => { + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + cy.get('[data-test=ModalOnboarding__Button]').click(); + cy.get('[data-test=ModalOnboarding]').should('not.exist'); + cy.get('[data-test=ProjectsView__ProjectsList]').should('be.visible'); + }); + + it('renders every time page is refreshed when "Always show Allocate onboarding" option is checked', () => { + cy.get('[data-test=ModalOnboarding__Button]').click(); + navigateWithCheck(ROOT_ROUTES.settings.absolute); + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').check().should('be.checked'); + cy.reload(); + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + }); + + it('renders only once when "Always show Allocate onboarding" option is not checked', () => { + cy.get('[data-test=ModalOnboarding__Button]').click(); + navigateWithCheck(ROOT_ROUTES.settings.absolute); + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('not.be.checked'); + cy.reload(); + cy.get('[data-test=ModalOnboarding]').should('not.exist'); + }); + + it('user can change steps with arrow keys (left, right)', () => { + checkChangeStepsWithArrowKeys(true); + }); + + it('user can change steps by clicking the edge of the screen (up to 25px from each edge)', () => { + checkChangeStepsByClickingEdgeOfTheScreenUpTo25px(true); + }); + + it('user cannot change steps by clicking the edge of the screen (more than 25px from each edge)', () => { + checkChangeStepsByClickingEdgeOfTheScreenMoreThan25px(true); + }); + + it('user can change steps by swiping on screen (difference more than or equal 5px)', () => { + checkChangeStepsBySwipingOnScreenDifferenceMoreThanOrEqual5px(true); + }); + + it('user cannot change steps by swiping on screen (difference less than 5px)', () => { + checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px(true); + }); + + it('user cannot change steps by swiping on screen (difference less than 5px)', () => { + checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px(true); + }); + + it('user is able to close the onboarding, and after disconnecting & connecting, onboarding does not show up again', () => { + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + cy.get('[data-test=ModalOnboarding__Button]').click(); + cy.get('[data-test=ModalOnboarding]').should('not.exist'); + connectWallet(true, false, true); + cy.get('[data-test=ModalOnboarding]').should('not.exist'); + }); + }); +}); + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`onboarding (TOS not accepted): ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + beforeSetup(); + }); + + beforeEach(() => { + cy.intercept( + { + method: 'POST', + url: '/user/*/tos', + }, + { body: { accepted: true }, statusCode: 200 }, + ); + connectWallet(false); + }); + + it('onboarding TOS step should be first and active', () => { + checkCurrentElement(0, true); + cy.get('[data-test=ModalOnboardingTOS]').should('be.visible'); + }); + + it('user is not able to click through entire onboarding flow', () => { + for (let i = 1; i < getStepsDecisionWindowClosed('2', '16 Jan').length; i++) { + checkProgressStepperSlimIsCurrentAndClickNext(i, i === 1); + } + }); + + it('user is not able to close the modal by clicking button in the top-right', () => { + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + cy.get('[data-test=ModalOnboarding__Button]').click({ force: true }); + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + }); + + it('renders every time page is refreshed', () => { + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + cy.reload(); + cy.get('[data-test=ModalOnboarding]').should('be.visible'); + }); + + it('user cannot change steps with arrow keys (left, right)', () => { + checkChangeStepsWithArrowKeys(false); + }); + + it('user can change steps by clicking the edge of the screen (up to 25px from each edge)', () => { + checkChangeStepsByClickingEdgeOfTheScreenUpTo25px(false); + }); + + it('user cannot change steps by clicking the edge of the screen (more than 25px from each edge)', () => { + checkChangeStepsByClickingEdgeOfTheScreenMoreThan25px(false); + }); + + it('user cannot change steps by swiping on screen (difference more than or equal 5px)', () => { + checkChangeStepsBySwipingOnScreenDifferenceMoreThanOrEqual5px(false); + }); + + it('user cannot change steps by swiping on screen (difference less than 5px)', () => { + checkChangeStepsBySwipingOnScreenDifferenceLessThanl5px(false); + }); + + it('TOS acceptance changes onboarding step to next step', () => { + checkCurrentElement(0, true); + cy.get('[data-test=TOS_InputCheckbox]').check(); + cy.switchToMetamaskNotification(); + cy.confirmMetamaskSignatureRequest(); + checkCurrentElement(1, true); + }); + + it('TOS acceptance allows the user to close the modal by clicking button in the top-right', () => { + checkCurrentElement(0, true); + cy.get('[data-test=TOS_InputCheckbox]').check(); + cy.switchToMetamaskNotification(); + cy.confirmMetamaskSignatureRequest(); + checkCurrentElement(1, true); + cy.get('[data-test=ModalOnboarding__Button]').click(); + cy.get('[data-test=ModalOnboarding]').should('not.exist'); + }); + }); +}); diff --git a/client/cypress/e2e/patronMode.cy.ts b/client/cypress/e2e/patronMode.cy.ts new file mode 100644 index 0000000000..b5f3455c47 --- /dev/null +++ b/client/cypress/e2e/patronMode.cy.ts @@ -0,0 +1,724 @@ +import { connectWallet, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { + describe(`patron mode (disabled): ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.settings.absolute); + connectWallet(true, false); + }); + + it('patron badge should not exist ', () => { + cy.get('[data-test=ProfileInfo__badge]').should('not.exist'); + }); + + it('Patron mode toggle is not checked', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').should('not.be.checked'); + }); + + if (isDesktop) { + it('Patron mode tooltip is visible on hover and has correct text', () => { + cy.get('[data-test=SettingsPatronModeBox__Tooltip]').trigger('mouseover'); + cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]').should('be.visible'); + cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]') + .invoke('text') + .should( + 'eq', + 'Patron mode is for token holders who want to support Octant. It disables allocation to yourself or projects. All rewards go directly to the matching fund with no action required by the patron.', + ); + }); + } + + it('Checking patron mode opens patron mode modal', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=ModalPatronMode]').should('be.visible'); + }); + + it('Patron mode modal last paragraph has correct text', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=SettingsPatronMode__fourthParagraph]') + .invoke('text') + .should('eq', 'Slide the switch below all the way to the right to enable patron mode.'); + }); + + it('Slider is visible', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=PatronModeSlider]').should('be.visible'); + }); + + it('Slider has correct label', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=PatronModeSlider__label]') + .invoke('text') + .should('eq', 'Slide right to confirm'); + }); + + it('Slider button is visible ', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=PatronModeSlider__button]').should('be.visible'); + }); + + it('Slider button has right arrow inside', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=PatronModeSlider__button__arrow]') + .should('be.visible') + .should('have.css', 'transform', 'none'); + }); + + it('Slider button is on the left side', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderLeftDistance = sliderEl[0].getBoundingClientRect().left; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonLeftDistance = sliderButtonEl[0].getBoundingClientRect().left; + + expect(sliderButtonLeftDistance).to.be.eq(sliderLeftDistance + sliderLeftPadding); + }); + }); + }); + + it('Slider button returns to the starting point if user drops it below or equal 50% of slider width', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderDimensions = sliderEl[0].getBoundingClientRect(); + const sliderWidth = sliderDimensions.width; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + const sliderButtonWidth = sliderButtonDimensions.width; + + const sliderTrackWidth = + sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; + + const pointerDownPageX = sliderButtonDimensions.x; + const pointerMovePageX = sliderButtonDimensions.x + sliderTrackWidth / 2; + + cy.get('[data-test=PatronModeSlider__button]') + .trigger('pointerdown', { + pageX: pointerDownPageX, + }) + .trigger('pointermove', { + pageX: pointerMovePageX, + }) + .wait(1000) + .then(sliderButtonElAfterPointerMove => { + const sliderButtonDimensionsAfterPointerMove = + sliderButtonElAfterPointerMove[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerMove.x).eq(pointerMovePageX); + }) + .trigger('pointerup') + .wait(1000) + .then(sliderButtonElAfterPointerUp => { + const sliderButtonDimensionsAfterPointerUp = + sliderButtonElAfterPointerUp[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerUp.x).eq(pointerDownPageX); + }); + }); + }); + }); + + it('Slider elements change color while moving slider button', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderDimensions = sliderEl[0].getBoundingClientRect(); + const sliderWidth = sliderDimensions.width; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + const sliderButtonWidth = sliderButtonDimensions.width; + + const sliderTrackWidth = + sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; + + const slider0PercentagePageX = sliderButtonDimensions.x; + const slider25PercentagePageX = sliderButtonDimensions.x + sliderTrackWidth / 4; + const slider50PercentagePageX = sliderButtonDimensions.x + sliderTrackWidth / 2; + const slider75PercentagePageX = sliderButtonDimensions.x + 3 * (sliderTrackWidth / 4); + const slider100PercentagePageX = sliderButtonDimensions.x + sliderTrackWidth; + + // 0% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointerdown', { + pageX: slider0PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgb(243, 243, 243)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(255, 255, 255)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(23, 23, 23)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '1'); + + // 25% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider25PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(212, 224, 221, 0.95)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(222, 234, 231)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(129, 129, 129)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0.75'); + + // 50% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider50PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(175, 204, 197, 0.9)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(183, 211, 204)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(181, 181, 181)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0.5'); + + // 75% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider75PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(128, 181, 169, 0.85)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(133, 185, 173)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(221, 221, 221)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0.25'); + + // 100% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider100PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(45, 155, 135, 0.8)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(45, 155, 135)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(255, 255, 255)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0'); + }); + }); + }); + + it('Slider button goes to the end of track if user drops it above 50% of slider width + animation after signature', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').check(); + + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderDimensions = sliderEl[0].getBoundingClientRect(); + const sliderWidth = sliderDimensions.width; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + const sliderButtonWidth = sliderButtonDimensions.width; + + const sliderTrackWidth = + sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; + + const pointerDownPageX = sliderButtonDimensions.x; + const pointerMovePageX = sliderButtonDimensions.x + (sliderTrackWidth / 2 + 1); + const pointerUpPageX = sliderDimensions.right - sliderRightPadding - sliderButtonWidth; + + cy.get('[data-test=PatronModeSlider__button]') + .trigger('pointerdown', { + pageX: pointerDownPageX, + }) + .trigger('pointermove', { + pageX: pointerMovePageX, + }) + .wait(1000) + .then(sliderButtonElAfterPointerMove => { + const sliderButtonDimensionsAfterPointerMove = + sliderButtonElAfterPointerMove[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerMove.x).eq(pointerMovePageX); + }) + .trigger('pointerup') + .wait(1000) + .then(sliderButtonElAfterPointerUp => { + const sliderButtonDimensionsAfterPointerUp = + sliderButtonElAfterPointerUp[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerUp.x).eq(pointerUpPageX); + }); + + cy.confirmMetamaskSignatureRequest(); + cy.switchToCypressWindow(); + cy.get('[data-test=PatronModeSlider__button]').should('not.exist'); + cy.get('[data-test=PatronModeSlider__label]').should('not.exist'); + cy.get('[data-test=PatronModeSlider__status-label]') + .invoke('text') + .should('eq', 'Patron mode enabled'); + cy.wait(500); + cy.get('[data-test=ModalPatronMode]').should('not.exist'); + }); + }); + }); + }); + + describe(`patron mode (enabled): ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.settings.absolute); + connectWallet(true, true); + }); + + it('patron badge is visible and has correct label, background and text-transform prop', () => { + cy.get('[data-test=ProfileInfo__badge]').should('be.visible'); + cy.get('[data-test=ProfileInfo__badge]').invoke('text').should('eq', 'Patron'); + cy.get('[data-test=ProfileInfo__badge]') + .should('have.css', 'background-color', 'rgb(104, 91, 138)') + .should('have.css', 'text-transform', 'uppercase'); + }); + + it('Navbar has 4 items - projects, earn, metrics, settings', () => { + const navbarChildrenDataTest = [ + 'Navbar__Button--Projects', + 'Navbar__Button--Earn', + 'Navbar__Button--Metrics', + 'Navbar__Button--Settings', + ]; + + cy.get('[data-test=Navbar__buttons]') + .children() + .should('have.length', navbarChildrenDataTest.length); + + for (let i = 0; i < navbarChildrenDataTest.length; i++) { + cy.get('[data-test=Navbar__buttons]') + .children() + .eq(i) + .invoke('data', 'test') + .should('eq', navbarChildrenDataTest[i]); + } + }); + + it('route /allocate redirects to /projects', () => { + visitWithLoader(ROOT_ROUTES.allocation.absolute, ROOT_ROUTES.projects.absolute); + cy.get('[data-test=ProjectsView]').should('be.visible'); + }); + + it('BoxPersonalAllocation has correct title and sections labels', () => { + visitWithLoader(ROOT_ROUTES.earn.absolute); + cy.get('[data-test=BoxPersonalAllocation__title]') + .invoke('text') + .should('eq', 'Patron earnings'); + cy.get('[data-test=BoxPersonalAllocation__Section__label]') + .eq(0) + .invoke('text') + .should('eq', 'Current epoch'); + cy.get('[data-test=BoxPersonalAllocation__Section__label]') + .eq(1) + .invoke('text') + .should('eq', 'All time'); + }); + + it('Patron mode toggle is checked', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').should('be.checked'); + }); + + if (isDesktop) { + it('Patron mode tooltip is visible on hover and has correct text', () => { + cy.get('[data-test=SettingsPatronModeBox__Tooltip]').trigger('mouseover'); + cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]').should('be.visible'); + cy.get('[data-test=SettingsPatronModeBox__Tooltip__content]') + .invoke('text') + .should( + 'eq', + 'Patron mode is for token holders who want to support Octant. It disables allocation to yourself or projects. All rewards go directly to the matching fund with no action required by the patron.', + ); + }); + } + + it('Unchecking patron mode opens patron mode modal', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=ModalPatronMode]').should('be.visible'); + }); + + it('Patron mode modal last paragraph has correct text', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=SettingsPatronMode__fourthParagraph]') + .invoke('text') + .should('eq', 'Slide the switch below all the way to the left to disable patron mode.'); + }); + + it('Slider is visible', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=PatronModeSlider]').should('be.visible'); + }); + + it('Slider has correct label', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=PatronModeSlider__label]') + .invoke('text') + .should('eq', 'Slide left to confirm'); + }); + + it('Slider button is visible', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=PatronModeSlider__button]').should('be.visible'); + }); + + it('Slider button has left arrow inside', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=PatronModeSlider__button__arrow]') + .should('be.visible') + .should('have.css', 'transform', 'matrix(-1, 0, 0, -1, 0, 0)'); + }); + + it('Slider button is on the right side', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderRightDistance = sliderEl[0].getBoundingClientRect().right; + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonRightDistance = sliderButtonEl[0].getBoundingClientRect().right; + + expect(sliderButtonRightDistance).to.be.eq(sliderRightDistance - sliderRightPadding); + }); + }); + }); + + it('Slider button returns to the starting point if user drops it below or equal 50% of slider width', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderDimensions = sliderEl[0].getBoundingClientRect(); + const sliderWidth = sliderDimensions.width; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + const sliderButtonWidth = sliderButtonDimensions.width; + + const sliderTrackWidth = + sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; + + const pointerDownPageX = sliderButtonDimensions.right; + const pointerMovePageX = sliderButtonDimensions.right - sliderTrackWidth / 2; + + cy.get('[data-test=PatronModeSlider__button]') + .trigger('pointerdown', { + pageX: pointerDownPageX, + }) + .trigger('pointermove', { + pageX: pointerMovePageX, + }) + .wait(1000) + .then(sliderButtonElAfterPointerMove => { + const sliderButtonDimensionsAfterPointerMove = + sliderButtonElAfterPointerMove[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerMove.right).eq(pointerMovePageX); + }) + .trigger('pointerup') + .wait(1000) + .then(sliderButtonElAfterPointerUp => { + const sliderButtonDimensionsAfterPointerUp = + sliderButtonElAfterPointerUp[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerUp.right).eq(pointerDownPageX); + }); + }); + }); + }); + + it('Slider elements change color while moving slider button', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderDimensions = sliderEl[0].getBoundingClientRect(); + const sliderWidth = sliderDimensions.width; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + const sliderButtonWidth = sliderButtonDimensions.width; + + const sliderTrackWidth = + sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; + + const slider0PercentagePageX = sliderButtonDimensions.right; + const slider25PercentagePageX = sliderButtonDimensions.right - sliderTrackWidth / 4; + const slider50PercentagePageX = sliderButtonDimensions.right - sliderTrackWidth / 2; + const slider75PercentagePageX = sliderButtonDimensions.right - 3 * (sliderTrackWidth / 4); + const slider100PercentagePageX = sliderButtonDimensions.right - sliderTrackWidth; + + // 0% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointerdown', { + pageX: slider0PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgb(243, 243, 243)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(255, 255, 255)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(23, 23, 23)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '1'); + + // 25% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider25PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(212, 224, 221, 0.95)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(222, 234, 231)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(129, 129, 129)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0.75'); + + // 50% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider50PercentagePageX, + waitForAnimations: true, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(175, 204, 197, 0.9)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(183, 211, 204)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(181, 181, 181)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0.5'); + + // 75% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider75PercentagePageX, + waitForAnimations: true, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(128, 181, 169, 0.85)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(133, 185, 173)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(221, 221, 221)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0.25'); + + // 100% + cy.get('[data-test=PatronModeSlider__button]').trigger('pointermove', { + pageX: slider100PercentagePageX, + }); + cy.get('[data-test=PatronModeSlider]').should( + 'have.css', + 'background-color', + 'rgba(45, 155, 135, 0.8)', + ); + cy.get('[data-test=PatronModeSlider__button]').should( + 'have.css', + 'background-color', + 'rgb(45, 155, 135)', + ); + cy.get('[data-test=PatronModeSlider__button__arrow__path]').should( + 'have.css', + 'fill', + 'rgb(255, 255, 255)', + ); + cy.get('[data-test=PatronModeSlider__label]') + .should('have.css', 'color', 'rgb(158, 163, 158)') + .should('have.css', 'opacity', '0'); + }); + }); + }); + + it('Slider button goes to the end of track if user drops it above 50% of slider width + animation after signature', () => { + cy.get('[data-test=SettingsPatronModeBox__InputToggle]').uncheck(); + + cy.get('[data-test=PatronModeSlider]').then(sliderEl => { + const sliderDimensions = sliderEl[0].getBoundingClientRect(); + const sliderWidth = sliderDimensions.width; + const sliderLeftPadding = parseInt(sliderEl.css('paddingLeft'), 10); + const sliderRightPadding = parseInt(sliderEl.css('paddingRight'), 10); + + cy.get('[data-test=PatronModeSlider__button]').then(sliderButtonEl => { + const sliderButtonDimensions = sliderButtonEl[0].getBoundingClientRect(); + + const sliderButtonWidth = sliderButtonDimensions.width; + + const sliderTrackWidth = + sliderWidth - sliderLeftPadding - sliderRightPadding - sliderButtonWidth; + + const pointerDownPageX = sliderButtonDimensions.right; + const pointerMovePageX = sliderButtonDimensions.right - (sliderTrackWidth / 2 + 1); + const pointerUpPageX = sliderDimensions.left + sliderLeftPadding; + + cy.get('[data-test=PatronModeSlider__button]') + .trigger('pointerdown', { + pageX: pointerDownPageX, + }) + .trigger('pointermove', { + pageX: pointerMovePageX, + }) + .wait(1000) + .then(sliderButtonElAfterPointerMove => { + const sliderButtonDimensionsAfterPointerMove = + sliderButtonElAfterPointerMove[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerMove.right).eq(pointerMovePageX); + }) + .trigger('pointerup') + .wait(1000) + .then(sliderButtonElAfterPointerUp => { + const sliderButtonDimensionsAfterPointerUp = + sliderButtonElAfterPointerUp[0].getBoundingClientRect(); + expect(sliderButtonDimensionsAfterPointerUp.x).eq(pointerUpPageX); + }); + + cy.confirmMetamaskSignatureRequest(); + cy.switchToCypressWindow(); + cy.get('[data-test=PatronModeSlider__button]').should('not.exist'); + cy.get('[data-test=PatronModeSlider__label]').should('not.exist'); + cy.get('[data-test=PatronModeSlider__status-label]') + .invoke('text') + .should('eq', 'Patron mode disabled'); + cy.wait(500); + cy.get('[data-test=ModalPatronMode]').should('not.exist'); + }); + }); + }); + + it('when entering project view, button icon changes to chevronLeft', () => { + visitWithLoader(ROOT_ROUTES.projects.absolute); + cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); + cy.get('[data-test=Navbar__Button--Projects]') + .find('svg') + // HTML tag can't be self-closing in CY. + .should( + 'have.html', + '', + ); + }); + }); +}); diff --git a/client/cypress/e2e/project.cy.ts b/client/cypress/e2e/project.cy.ts new file mode 100644 index 0000000000..e811fb70aa --- /dev/null +++ b/client/cypress/e2e/project.cy.ts @@ -0,0 +1,181 @@ +import { connectWallet, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import { getNamesOfProjects } from 'cypress/utils/projects'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +import Chainable = Cypress.Chainable; + +const getButtonAddToAllocate = (): Chainable => { + const projectListItemFirst = cy.get('[data-test=ProjectListItem').first(); + + return projectListItemFirst.find('[data-test=ProjectListItemHeader__ButtonAddToAllocate]'); +}; + +const checkProjectItemElements = (): Chainable => { + cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); + const projectListItemFirst = cy.get('[data-test=ProjectListItem').first(); + projectListItemFirst.get('[data-test=ProjectListItemHeader__Img]').should('be.visible'); + projectListItemFirst.get('[data-test=ProjectListItemHeader__name]').should('be.visible'); + getButtonAddToAllocate().should('be.visible'); + projectListItemFirst.get('[data-test=ProjectListItemHeader__Button]').should('be.visible'); + projectListItemFirst.get('[data-test=ProjectListItem__Description]').should('be.visible'); + + cy.get('[data-test=ProjectListItem__Donors]') + .first() + .scrollIntoView({ offset: { left: 0, top: 100 } }); + + cy.get('[data-test=ProjectListItem__Donors]').first().should('be.visible'); + cy.get('[data-test=ProjectListItem__Donors__DonorsHeader__count]') + .first() + .should('be.visible') + .should('have.text', '0'); + return cy.get('[data-test=ProjectListItem__Donors__noDonationsYet]').first().should('be.visible'); +}; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`project: ${device}`, { viewportHeight, viewportWidth }, () => { + let projectNames: string[] = []; + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + + /** + * This could be done in before hook, but CY wipes the state after each test + * (could be disabled, but creates other problems) + */ + if (projectNames.length === 0) { + projectNames = getNamesOfProjects(); + } + }); + + it('entering project view directly renders content', () => { + cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); + cy.reload(); + const projectListItemFirst = cy.get('[data-test=ProjectListItem').first(); + projectListItemFirst.get('[data-test=ProjectListItemHeader__Img]').should('be.visible'); + projectListItemFirst.get('[data-test=ProjectListItemHeader__name]').should('be.visible'); + }); + + it('entering project view renders all its elements', () => { + checkProjectItemElements(); + }); + + it('entering project view renders all its elements with fallback IPFS provider', () => { + cy.intercept('GET', '**/ipfs/**', req => { + if (req.url.includes('infura')) { + req.destroy(); + } + }); + + checkProjectItemElements(); + }); + + it('entering project view shows Toast with info about IPFS failure when all providers fail', () => { + cy.intercept('GET', '**/ipfs/**', req => { + req.destroy(); + }); + + cy.get('[data-test=Toast--ipfsMessage').should('be.visible'); + }); + + it('entering project view allows to add it to allocation and remove, triggering change of the icon, change of the number in navbar', () => { + cy.get('[data-test^=ProjectsView__ProjectsListItem').first().click(); + + getButtonAddToAllocate().click(); + + // cy.get('@buttonAddToAllocate').click(); + cy.get('[data-test=Navbar__numberOfAllocations]').contains(1); + getButtonAddToAllocate().click(); + cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); + }); + + it('Entering project view allows scroll only to the last project', () => { + cy.get('[data-test^=ProjectsView__ProjectsListItem]').first().click(); + + for (let i = 0; i < projectNames.length; i++) { + cy.get('[data-test=ProjectListItem]').should( + 'have.length.greaterThan', + i === projectNames.length - 1 ? projectNames.length - 1 : i, + ); + cy.get('[data-test=ProjectListItemHeader__name]') + .eq(i) + .scrollIntoView({ offset: { left: 0, top: -150 } }) + .contains(projectNames[i]); + cy.get('[data-test=ProjectListItem__Donors]') + .eq(i) + .scrollIntoView({ offset: { left: 0, top: -150 } }) + .should('be.visible'); + } + }); + + it('"Back to top" button is displayed if the user has scrolled past the start of the final project description', () => { + cy.get('[data-test^=ProjectsView__ProjectsListItem]').first().click(); + + for (let i = 0; i < projectNames.length - 1; i++) { + cy.get('[data-test=ProjectListItem__Donors]') + .eq(i) + .scrollIntoView({ offset: { left: 0, top: 100 } }); + + if (i === projectNames.length - 1) { + cy.get('[data-test=ProjectBackToTopButton__Button]').should('be.visible'); + } + } + }); + + it('Clicking on "Back to top" button scrolls to the top of view (first project is visible)', () => { + cy.get('[data-test^=ProjectsView__ProjectsListItem]').first().click(); + + for (let i = 0; i < projectNames.length - 1; i++) { + cy.get('[data-test=ProjectListItem__Donors]') + .eq(i) + .scrollIntoView({ offset: { left: 0, top: 100 } }); + + if (i === projectNames.length - 1) { + cy.get('[data-test=ProjectBackToTopButton__Button]').click(); + cy.get('[data-test=ProjectListItem]').eq(0).should('be.visible'); + } + } + }); + }); + + describe(`project (patron mode): ${device}`, { viewportHeight, viewportWidth }, () => { + let projectNames: string[] = []; + + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + connectWallet(true, true); + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + + /** + * This could be done in before hook, but CY wipes the state after each test + * (could be disabled, but creates other problems) + */ + if (projectNames.length === 0) { + projectNames = getNamesOfProjects(); + } + }); + + it('button "add to allocate" is disabled', () => { + for (let i = 0; i < projectNames.length; i++) { + cy.get('[data-test^=ProjectsView__ProjectsListItem]').eq(i).click(); + getButtonAddToAllocate().should('be.visible').should('be.disabled'); + cy.go('back'); + } + }); + }); +}); diff --git a/client/cypress/e2e/projects.cy.ts b/client/cypress/e2e/projects.cy.ts new file mode 100644 index 0000000000..9d096bcdf2 --- /dev/null +++ b/client/cypress/e2e/projects.cy.ts @@ -0,0 +1,239 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import chaiColors from 'chai-colors'; + +import { connectWallet, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import { getNamesOfProjects } from 'cypress/utils/projects'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import getMilestones from 'src/constants/milestones'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +import Chainable = Cypress.Chainable; + +chai.use(chaiColors); + +function checkProjectItemElements(index, name, isPatronMode = false): Chainable { + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__imageProfile]') + .should('be.visible'); + cy.get('[data-test^=ProjectsView__ProjectsListItem]') + .eq(index) + .should('be.visible') + .find('[data-test=ProjectsListItem__name]') + .should('be.visible') + .contains(name); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__IntroDescription]') + .should('be.visible'); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .should('be.visible'); + + if (isPatronMode) { + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .should('be.disabled'); + } + + return cy + .get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectRewards]') + .should('be.visible'); + // TODO OCT-663 Make CY check if rewards are available (Epoch 2, decision window open). + // return cy + // .get('[data-test^=ProjectsView__ProjectsListItem') + // .eq(index) + // .find('[data-test=ProjectRewards__currentTotal__label]') + // .should('be.visible'); +} + +function addProjectToAllocate(index, numberOfAddedProjects): Chainable { + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__imageProfile]') + .should('be.visible'); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__IntroDescription]') + .should('be.visible'); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .scrollIntoView(); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .click(); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .find('svg') + .find('path') + .then($el => $el.css('fill')) + .should('be.colored', '#FF6157'); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .find('svg') + .find('path') + .then($el => $el.css('stroke')) + .should('be.colored', '#FF6157'); + cy.get('[data-test=Navbar__numberOfAllocations]').contains(numberOfAddedProjects + 1); + visitWithLoader(ROOT_ROUTES.allocation.absolute); + cy.get('[data-test=AllocationItem]').should('have.length', numberOfAddedProjects + 1); + return cy.go('back'); +} + +function removeProjectFromAllocate(numberOfProjects, numberOfAddedProjects, index): Chainable { + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .scrollIntoView(); + cy.get('[data-test^=ProjectsView__ProjectsListItem') + .eq(index) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .click(); + visitWithLoader(ROOT_ROUTES.allocation.absolute); + cy.get('[data-test=AllocationItem]').should('have.length', numberOfAddedProjects - 1); + if (index < numberOfProjects - 1) { + cy.get('[data-test=Navbar__numberOfAllocations]').contains(numberOfAddedProjects - 1); + } else { + cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); + } + return cy.go('back'); +} + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`projects: ${device}`, { viewportHeight, viewportWidth }, () => { + let projectNames: string[] = []; + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + + /** + * This could be done in before hook, but CY wipes the state after each test + * (could be disabled, but creates other problems) + */ + if (projectNames.length === 0) { + projectNames = getNamesOfProjects(); + } + }); + + it('user is able to see all the projects in the view', () => { + for (let i = 0; i < projectNames.length; i++) { + cy.get('[data-test^=ProjectsView__ProjectsListItem]').eq(i).scrollIntoView(); + checkProjectItemElements(i, projectNames[i]); + } + }); + + it('user is able to add & remove the first and the last project to/from allocation, triggering change of the icon, change of the number in navbar', () => { + // This test checks the first and the last elements only to save time. + cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); + + addProjectToAllocate(0, 0); + addProjectToAllocate(projectNames.length - 1, 1); + removeProjectFromAllocate(projectNames.length, 2, 0); + removeProjectFromAllocate(projectNames.length, 1, projectNames.length - 1); + }); + + it('user is able to add project to allocation in ProjectsView and remove it from allocation in AllocationView', () => { + cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); + addProjectToAllocate(0, 0); + visitWithLoader(ROOT_ROUTES.allocation.absolute); + cy.get('[data-test=AllocationItemSkeleton]').should('not.exist'); + cy.get('[data-test=AllocationItem]').then(el => { + const { x } = el[0].getBoundingClientRect(); + cy.get('[data-test=AllocationItem]') + .trigger('pointerdown') + .trigger('pointermove', { pageX: x - 20 }) + .trigger('pointerup'); + cy.get('[data-test=AllocationItem__removeButton]').should('be.visible'); + cy.get('[data-test=AllocationItem__removeButton]').click(); + cy.get('[data-test=AllocationItem__removeButton]').should('not.exist'); + cy.get('[data-test=AllocationItem]').should('not.exist'); + cy.get('[data-test=Navbar__numberOfAllocations]').should('not.exist'); + }); + + it('ProjectsTimelineWidgetItem with href opens link when clicked without mouse movement', () => { + const milestones = getMilestones(); + cy.get('[data-test=ProjectsTimelineWidget]').should('be.visible'); + cy.get('[data-test=ProjectsTimelineWidgetItem]').should('have.length', milestones.length); + for (let i = 0; i < milestones.length; i++) { + if (milestones[i].href) { + cy.get('[data-test=ProjectsTimelineWidgetItem]') + .eq(i) + .within(() => { + cy.get('[data-test=ProjectsTimelineWidgetItem__Svg--arrowTopRight]').should( + 'be.visible', + ); + }); + + cy.get('[data-test=ProjectsTimelineWidgetItem]') + .eq(i) + .then(el => { + const { x } = el[0].getBoundingClientRect(); + cy.get('[data-test=ProjectsTimelineWidgetItem]') + .eq(i) + .trigger('mousedown') + .trigger('mouseup', { clientX: x + 10 }); + cy.location('pathname').should('eq', ROOT_ROUTES.projects.absolute); + + cy.get('[data-test=ProjectsTimelineWidgetItem]') + .eq(i) + .trigger('mousedown') + .trigger('mouseup'); + cy.location('pathname').should('not.eq', ROOT_ROUTES.projects.absolute); + }); + } + } + }); + }); + }); + + describe(`projects (patron mode): ${device}`, { viewportHeight, viewportWidth }, () => { + let projectNames: string[] = []; + + before(() => { + /** + * Global Metamask setup done by Synpress is not always done. + * Since Synpress needs to have valid provider to fetch the data from contracts, + * setupMetamask is required in each test suite. + */ + cy.setupMetamask(); + }); + + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + connectWallet(true, true); + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + /** + * This could be done in before hook, but CY wipes the state after each test + * (could be disabled, but creates other problems) + */ + if (projectNames.length === 0) { + projectNames = getNamesOfProjects(); + } + }); + + after(() => { + cy.disconnectMetamaskWalletFromAllDapps(); + }); + + it('button "add to allocate" is disabled', () => { + for (let i = 0; i < projectNames.length; i++) { + cy.get('[data-test^=ProjectsView__ProjectsListItem]').eq(i).scrollIntoView(); + checkProjectItemElements(i, projectNames[i], true); + } + }); + }); +}); diff --git a/client/cypress/e2e/projectsArchive.cy.ts b/client/cypress/e2e/projectsArchive.cy.ts new file mode 100644 index 0000000000..237fcbd624 --- /dev/null +++ b/client/cypress/e2e/projectsArchive.cy.ts @@ -0,0 +1,111 @@ +import { checkLocationWithLoader, visitWithLoader } from 'cypress/utils/e2e'; +import { moveTime } from 'cypress/utils/moveTime'; +import viewports from 'cypress/utils/viewports'; +import { QUERY_KEYS } from 'src/api/queryKeys'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +let wasEpochMoved = false; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`projects archive: ${device}`, { viewportHeight, viewportWidth }, () => { + beforeEach(() => { + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + }); + + it('moves to the next epoch', () => { + // Move time only once, for the first device. + if (!wasEpochMoved) { + cy.window().then(async win => { + const currentEpochBefore = Number( + win.clientReactQuery.getQueryData(QUERY_KEYS.currentEpoch), + ); + + cy.wrap(null).then(() => { + return moveTime(win, 'nextEpochDecisionWindowClosed').then(() => { + const currentEpochAfter = Number( + win.clientReactQuery.getQueryData(QUERY_KEYS.currentEpoch), + ); + wasEpochMoved = true; + expect(currentEpochBefore + 1).to.eq(currentEpochAfter); + }); + }); + }); + } else { + expect(true).to.be.true; + } + }); + + it('renders archive elements + clicking on epoch archive ProjectsListItem opens ProjectView for particular epoch and project', () => { + cy.get('[data-test=MainLayout__body]').then(el => { + const mainLayoutPaddingTop = parseInt(el.css('paddingTop'), 10); + + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + cy.get('[data-test=ProjectsView__ProjectsList]') + .should('be.visible') + .children() + .then(children => { + children[children.length - 1].scrollIntoView(); + cy.window().then(window => window.scrollTo(0, window.scrollY - mainLayoutPaddingTop)); + cy.wait(1000); + // header test + cy.get('[data-test=ProjectsView__ProjectsList__header--archive]').should('be.visible'); + + // list test + cy.get('[data-test=ProjectsView__ProjectsList--archive]').first().should('be.visible'); + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + cy.get('[data-test=ProjectsView__ProjectsList--archive]') + .first() + .children() + .then(childrenArchive => { + const numberOfArchivedProjects = childrenArchive.length - 2; // archived projects tiles - (header + divider)[2] + for (let i = 0; i < numberOfArchivedProjects; i++) { + cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) + .first() + .scrollIntoView(); + cy.window().then(window => + window.scrollTo(0, window.scrollY - mainLayoutPaddingTop), + ); + // list item test + cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) + .first() + .should('be.visible') + .within(() => { + // rewards test + cy.get('[data-test=ProjectRewards]').should('be.visible'); + }); + + if (numberOfArchivedProjects - 1) { + cy.get('[data-test=ProjectsView__ProjectsList--archive]') + .first() + .should('have.length', 1); + } + + cy.get('[data-test^=ProjectItemSkeleton').should('not.exist'); + cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) + .first() + .invoke('data', 'address') + .then(address => { + cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) + .first() + .invoke('data', 'epoch') + .then(epoch => { + cy.get(`[data-test=ProjectsView__ProjectsListItem--archive--${i}]`) + .first() + .click(); + checkLocationWithLoader( + `${ROOT_ROUTES.project.absolute}/${epoch}/${address}`, + ); + cy.go('back'); + checkLocationWithLoader(ROOT_ROUTES.projects.absolute); + }); + }); + } + }); + }); + }); + }); + }); +}); diff --git a/client/cypress/e2e/rewardsCalculator.cy.ts b/client/cypress/e2e/rewardsCalculator.cy.ts new file mode 100644 index 0000000000..361153ff90 --- /dev/null +++ b/client/cypress/e2e/rewardsCalculator.cy.ts @@ -0,0 +1,252 @@ +import { ETH_USD, mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { IS_ONBOARDING_ALWAYS_VISIBLE, IS_ONBOARDING_DONE } from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; +import getFormattedEthValue from 'src/utils/getFormattedEthValue'; +import { parseUnitsBigInt } from 'src/utils/parseUnitsBigInt'; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { + describe(`rewards calculator: ${device}`, { viewportHeight, viewportWidth }, () => { + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.earn.absolute); + }); + + it('renders calculator icon inside box', () => { + cy.get('[data-test=Tooltip__rewardsCalculator__body]').should('be.visible'); + }); + + if (isDesktop) { + it('tooltip is visible on calculator icon hover and has correct text', () => { + cy.get('[data-test=Tooltip__rewardsCalculator').trigger('mouseover'); + cy.get('[data-test=Tooltip__rewardsCalculator__content') + .should('be.visible') + .invoke('text') + .should('eq', 'Calculate rewards'); + }); + } + + it('clicking on rewards calculator icon opens rewards calculator modal', () => { + cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); + cy.get('[data-test=ModalRewardsCalculator]').should('be.visible'); + }); + + it('default values in rewards calculator are 90 days and 5000 GLM', () => { + cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); + cy.get('[data-test=RewardsCalculator__InputText--crypto]').invoke('val').should('eq', '5000'); + cy.get('[data-test=RewardsCalculator__InputText--days]').invoke('val').should('eq', '90'); + }); + + it('calculator fetches rewards values in ETH and USD based on DAYS and GLM fields', () => { + cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewards'); + cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'be.visible', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'be.visible', + ); + cy.wait('@postEstimatedRewards'); + + cy.get('@postEstimatedRewards').then( + ({ + response: { + body: { budget }, + }, + }) => { + const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; + const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); + + cy.get( + '[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]', + ).should('not.exist'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', rewardsEth); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', rewardsUsd); + }, + ); + + cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewardsGlmValueChange'); + cy.get('[data-test=RewardsCalculator__InputText--crypto]').type('500000'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'be.visible', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'be.visible', + ); + cy.wait('@postEstimatedRewardsGlmValueChange'); + + cy.get('@postEstimatedRewardsGlmValueChange').then( + ({ + response: { + body: { budget }, + }, + }) => { + const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; + const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); + + cy.get( + '[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]', + ).should('not.exist'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', rewardsEth); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', rewardsUsd); + }, + ); + + cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewardsDaysValueChange'); + cy.get('[data-test=RewardsCalculator__InputText--days]').clear().type('900'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'be.visible', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'be.visible', + ); + cy.wait('@postEstimatedRewardsDaysValueChange'); + + cy.get('@postEstimatedRewardsDaysValueChange').then( + ({ + response: { + body: { budget }, + }, + }) => { + const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; + const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); + + cy.get( + '[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]', + ).should('not.exist'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', rewardsEth); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', rewardsUsd); + }, + ); + }); + + it('If DAYS or GLM input is empty rewards inputs are empty too', () => { + cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); + cy.get('[data-test=RewardsCalculator__InputText--crypto]').clear(); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', ''); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', ''); + + cy.get('[data-test=RewardsCalculator__InputText--days]').clear(); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', ''); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', ''); + + cy.get('[data-test=RewardsCalculator__InputText--crypto]').type('5000'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat__Loader]').should( + 'not.exist', + ); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', ''); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', ''); + }); + + it('Max GLM amount is 1000000000', () => { + cy.intercept('POST', '/rewards/estimated_budget').as('postEstimatedRewards'); + + cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); + + cy.get('[data-test=RewardsCalculator__InputText--crypto]').type('1000000000'); + cy.wait('@postEstimatedRewards'); + + cy.get('@postEstimatedRewards').then( + ({ + response: { + body: { budget }, + }, + }) => { + const rewardsEth = getFormattedEthValue(parseUnitsBigInt(budget, 'wei')).value; + const rewardsUsd = (parseFloat(rewardsEth) * ETH_USD).toFixed(2); + + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', rewardsEth); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', rewardsUsd); + + cy.get('[data-test=RewardsCalculator__InputText--crypto]') + .clear() + .type('1000000001') + .should('have.css', 'border-color', 'rgb(255, 97, 87)'); + cy.get('[data-test=RewardsCalculator__InputText--crypto__error]') + .should('be.visible') + .invoke('text') + .should('eq', 'That isn’t a valid amount'); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto]') + .invoke('val') + .should('eq', ''); + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--fiat]') + .invoke('val') + .should('eq', ''); + }, + ); + }); + + it('Closing the modal successfully cancels the request /estimated_budget', () => { + cy.window().then(win => { + cy.spy(win.console, 'error').as('consoleErrSpy'); + }); + + cy.get('[data-test=Tooltip__rewardsCalculator__body]').click(); + + cy.get('[data-test=RewardsCalculator__InputText--estimatedRewards--crypto__Loader]').should( + 'be.visible', + ); + + cy.get('[data-test=ModalRewardsCalculator__Button]').click(); + cy.get('[data-test=ModalRewardsCalculator').should('not.be.visible'); + + cy.on('uncaught:exception', error => { + expect(error.code).to.equal('ERR_CANCELED'); + }); + }); + }); +}); diff --git a/client/cypress/e2e/routes.cy.ts b/client/cypress/e2e/routes.cy.ts new file mode 100644 index 0000000000..1d2dc113b8 --- /dev/null +++ b/client/cypress/e2e/routes.cy.ts @@ -0,0 +1,42 @@ +import { mockCoinPricesServer, visitWithLoader } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { ROOT, ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`routes (wallet not connected): ${device}`, { viewportHeight, viewportWidth }, () => { + before(() => { + mockCoinPricesServer(); + cy.clearLocalStorage(); + }); + + it('empty route redirects to projects view', () => { + visitWithLoader(ROOT.absolute, ROOT_ROUTES.projects.absolute); + cy.get('[data-test=ProjectsView]').should('be.visible'); + }); + + it('allocation route redirects to allocation view', () => { + visitWithLoader(ROOT_ROUTES.allocation.absolute); + cy.get('[data-test=AllocationView]').should('be.visible'); + }); + + it('earn route redirects to earn view', () => { + visitWithLoader(ROOT_ROUTES.earn.absolute); + cy.get('[data-test=EarnView]').should('be.visible'); + }); + + it('metrics route redirects to metrics view', () => { + visitWithLoader(ROOT_ROUTES.metrics.absolute); + cy.get('[data-test=MetricsView]').should('be.visible'); + }); + + it('projects route redirects to projects view', () => { + visitWithLoader(ROOT_ROUTES.projects.absolute); + cy.get('[data-test=ProjectsView]').should('be.visible'); + }); + + it('settings route redirects to settings view', () => { + visitWithLoader(ROOT_ROUTES.settings.absolute); + cy.get('[data-test=SettingsView]').should('be.visible'); + }); + }); +}); diff --git a/client/cypress/e2e/settings.cy.ts b/client/cypress/e2e/settings.cy.ts new file mode 100644 index 0000000000..087b77329e --- /dev/null +++ b/client/cypress/e2e/settings.cy.ts @@ -0,0 +1,188 @@ +import { visitWithLoader, navigateWithCheck, mockCoinPricesServer } from 'cypress/utils/e2e'; +import viewports from 'cypress/utils/viewports'; +import { FIAT_CURRENCIES_SYMBOLS, DISPLAY_CURRENCIES } from 'src/constants/currencies'; +import { + ARE_OCTANT_TIPS_ALWAYS_VISIBLE, + DISPLAY_CURRENCY, + IS_CRYPTO_MAIN_VALUE_DISPLAY, + IS_ONBOARDING_ALWAYS_VISIBLE, + IS_ONBOARDING_DONE, +} from 'src/constants/localStorageKeys'; +import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; +import getValueCryptoToDisplay from 'src/utils/getValueCryptoToDisplay'; + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe(`settings: ${device}`, { viewportHeight, viewportWidth }, () => { + beforeEach(() => { + mockCoinPricesServer(); + localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); + localStorage.setItem(IS_ONBOARDING_DONE, 'true'); + visitWithLoader(ROOT_ROUTES.settings.absolute); + }); + + it('"Always show Allocate onboarding" option toggle works', () => { + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').check(); + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_ONBOARDING_ALWAYS_VISIBLE)).eq('true'); + }); + + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').click(); + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('not.be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_ONBOARDING_ALWAYS_VISIBLE)).eq('false'); + }); + + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').click(); + cy.get('[data-test=SettingsShowOnboardingBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_ONBOARDING_ALWAYS_VISIBLE)).eq('true'); + }); + }); + + it('"Use crypto as main value display" option is checked by default', () => { + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('true'); + }); + }); + + it('"Use crypto as main value display" option toggle works', () => { + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').check(); + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('true'); + }); + + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').click(); + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('not.be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('false'); + }); + + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').click(); + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(IS_CRYPTO_MAIN_VALUE_DISPLAY)).eq('true'); + }); + }); + + it('"Use crypto as main value display" option by default displays crypto value as primary in DoubleValue component', () => { + navigateWithCheck(ROOT_ROUTES.earn.absolute); + + const cryptoValue = getValueCryptoToDisplay({ + cryptoCurrency: 'golem', + valueCrypto: BigInt(0), + }); + + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__primary]') + .invoke('text') + .should('eq', cryptoValue); + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]') + .invoke('text') + .should('not.eq', cryptoValue); + }); + + it('"Use crypto as main value display" option changes DoubleValue sections order', () => { + cy.get('[data-test=SettingsCryptoMainValueBox__InputToggle]').uncheck(); + navigateWithCheck(ROOT_ROUTES.earn.absolute); + + const cryptoValue = getValueCryptoToDisplay({ + cryptoCurrency: 'golem', + valueCrypto: BigInt(0), + }); + + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__primary]') + .invoke('text') + .should('not.eq', cryptoValue); + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]') + .invoke('text') + .should('eq', cryptoValue); + }); + + it('"Choose a display currency" option works', () => { + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(DISPLAY_CURRENCY)).eq('"usd"'); + }); + + for (let i = 0; i < DISPLAY_CURRENCIES.length - 1; i++) { + const displayCurrency = DISPLAY_CURRENCIES[i]; + const displayCurrencyToUppercase = displayCurrency.toUpperCase(); + const nextDisplayCurrencyToUppercase = + i < DISPLAY_CURRENCIES.length - 1 ? DISPLAY_CURRENCIES[i + 1].toUpperCase() : undefined; + + cy.get('[data-test=SettingsCurrencyBox__InputSelect--currency__SingleValue]').contains( + displayCurrencyToUppercase, + ); + navigateWithCheck(ROOT_ROUTES.earn.absolute); + + if (FIAT_CURRENCIES_SYMBOLS[displayCurrency]) { + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]').contains( + FIAT_CURRENCIES_SYMBOLS[displayCurrency], + ); + } else { + cy.get('[data-test=BoxGlmLock__Section--effective__DoubleValue__secondary]').contains( + displayCurrencyToUppercase, + ); + } + + navigateWithCheck(ROOT_ROUTES.settings.absolute); + cy.get('[data-test=SettingsCurrencyBox__InputSelect--currency]').click(); + cy.get( + `[data-test=SettingsCurrencyBox__InputSelect--currency__Option--${nextDisplayCurrencyToUppercase}]`, + ).click(); + } + }); + + it('"Always show Octant tips" option toggle works', () => { + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').check(); + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(ARE_OCTANT_TIPS_ALWAYS_VISIBLE)).eq('true'); + }); + + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').click(); + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').should('not.be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(ARE_OCTANT_TIPS_ALWAYS_VISIBLE)).eq('false'); + }); + + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').click(); + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').should('be.checked'); + cy.getAllLocalStorage().then(() => { + expect(localStorage.getItem(ARE_OCTANT_TIPS_ALWAYS_VISIBLE)).eq('true'); + }); + }); + + it('"Always show Octant tips" works (checked)', () => { + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').check(); + + navigateWithCheck(ROOT_ROUTES.earn.absolute); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('exist'); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('be.visible'); + + cy.get('[data-test=EarnView__TipTile--connectWallet__Button]').click(); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('not.exist'); + + cy.reload(); + + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('exist'); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('be.visible'); + }); + + it('"Always show Octant tips" works (unchecked)', () => { + cy.get('[data-test=SettingsShowTipsBox__InputToggle]').uncheck(); + + navigateWithCheck(ROOT_ROUTES.earn.absolute); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('exist'); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('be.visible'); + + cy.get('[data-test=EarnView__TipTile--connectWallet__Button]').click(); + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('not.exist'); + + cy.reload(); + + cy.get('[data-test=EarnView__TipTile--connectWallet]').should('not.exist'); + }); + }); +}); From 5e107de3ae26602111454fec16c20daade5e5daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 22:47:06 +0200 Subject: [PATCH 14/20] test: debugging --- client/cypress/support/commands.ts | 3 --- client/package.json | 1 - client/src/views/SyncView/SyncView.tsx | 28 +++++++------------------- client/synpress.config.ts | 2 -- client/yarn.lock | 18 +---------------- 5 files changed, 8 insertions(+), 44 deletions(-) diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index 5e47fa3e8d..573fd4a276 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -1,6 +1,3 @@ /* eslint-disable import/no-extraneous-dependencies */ import '@synthetixio/synpress/support/commands'; -/* eslint-disable */ -import installLogsCollector from 'cypress-terminal-report/src/installLogsCollector'; -installLogsCollector(); /* eslint-enable import/no-extraneous-dependencies */ diff --git a/client/package.json b/client/package.json index fd76602d6c..51c963c810 100644 --- a/client/package.json +++ b/client/package.json @@ -84,7 +84,6 @@ "@vitejs/plugin-react": "4.2.1", "chai-colors": "^1.0.1", "css-mediaquery": "^0.1.2", - "cypress-terminal-report": "^6.0.1", "cypress-vite": "^1.5.0", "eslint": "8.56.0", "eslint-config-airbnb": "^19.0.4", diff --git a/client/src/views/SyncView/SyncView.tsx b/client/src/views/SyncView/SyncView.tsx index 44fbd1919f..3972f5b889 100644 --- a/client/src/views/SyncView/SyncView.tsx +++ b/client/src/views/SyncView/SyncView.tsx @@ -3,30 +3,16 @@ import { Trans } from 'react-i18next'; import Img from 'components/ui/Img'; import Svg from 'components/ui/Svg'; -import useCurrentEpoch from 'hooks/queries/useCurrentEpoch'; -import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen'; -import useEpochsIndexedBySubgraph from 'hooks/subgraph/useEpochsIndexedBySubgraph'; import { octantSemiTransparent } from 'svg/logo'; import styles from './SyncView.module.scss'; -const SyncView = (): ReactElement => { - const { data: isDecisionWindowOpen } = useIsDecisionWindowOpen(); - const { data: currentEpoch } = useCurrentEpoch(); - const { data: epochs } = useEpochsIndexedBySubgraph(); - - return ( -
-
- {isDecisionWindowOpen ? 'true' : 'false'} -
-
{currentEpoch}
-
{epochs}
- - - -
- ); -}; +const SyncView = (): ReactElement => ( +
+ + + +
+); export default SyncView; diff --git a/client/synpress.config.ts b/client/synpress.config.ts index 965dca282f..780da56f26 100644 --- a/client/synpress.config.ts +++ b/client/synpress.config.ts @@ -10,8 +10,6 @@ export default defineConfig({ baseUrl: process.env.OCTANT_BASE_URL || 'http://localhost:5173', defaultCommandTimeout: 120 * 1000, setupNodeEvents(on, config) { - // eslint-disable-next-line @typescript-eslint/no-var-requires,global-require,import/no-extraneous-dependencies - require('cypress-terminal-report/src/installLogsPrinter')(on); // eslint-disable-next-line no-param-reassign config.env = { ...config.env, CI: process.env.CI }; on( diff --git a/client/yarn.lock b/client/yarn.lock index 394bb1edba..1cb584c600 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -5556,17 +5556,6 @@ csstype@^3.0.2: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== -cypress-terminal-report@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/cypress-terminal-report/-/cypress-terminal-report-6.0.1.tgz#976522a49e3ba6a6580791559d5bdb8f6074a25b" - integrity sha512-I+kiUIJRwmdOATjqHGM84PfJZM3TpyPzcUgcpxX2PG04fIPXIWprSMAxc3hbFKP8gE8B7D9Nj9yzf7CwyOH5Iw== - dependencies: - chalk "^4.0.0" - fs-extra "^10.1.0" - process "^0.11.10" - semver "^7.5.4" - tv4 "^1.3.0" - cypress-vite@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/cypress-vite/-/cypress-vite-1.5.0.tgz#471ecc1175c7ab51b3b132c595dc3c7e222fe944" @@ -7525,7 +7514,7 @@ fs-extra@9.1.0, fs-extra@^9.1.0: jsonfile "^6.0.1" universalify "^2.0.0" -fs-extra@^10.0.0, fs-extra@^10.1.0: +fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ== @@ -12599,11 +12588,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tv4@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" - integrity sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw== - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" From 92acd20bf6bc8fab818a4425d553573147a492e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Sun, 14 Apr 2024 22:47:16 +0200 Subject: [PATCH 15/20] test: debugging --- client/src/hooks/helpers/useAppIsLoading.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/src/hooks/helpers/useAppIsLoading.ts b/client/src/hooks/helpers/useAppIsLoading.ts index bb3b8b48cc..df2805438e 100644 --- a/client/src/hooks/helpers/useAppIsLoading.ts +++ b/client/src/hooks/helpers/useAppIsLoading.ts @@ -1,6 +1,5 @@ import useCurrentEpoch from 'hooks/queries/useCurrentEpoch'; import useIsContract from 'hooks/queries/useIsContract'; -import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen'; import useIsPatronMode from 'hooks/queries/useIsPatronMode'; import useUserTOS from 'hooks/queries/useUserTOS'; import useAllProjects from 'hooks/subgraph/useAllProjects'; @@ -12,7 +11,6 @@ import getIsPreLaunch from 'utils/getIsPreLaunch'; export default function useAppIsLoading(isFlushRequired: boolean): boolean { const { isFetching: isFetchingAllProjects } = useAllProjects(); - const { isLoading: isLoadingIsDecisionWindowOpen } = useIsDecisionWindowOpen(); const { isFetching: isFetchingPatronModeStatus } = useIsPatronMode(); const { isFetching: isFetchingUserTOS, isRefetching: isRefetchingUserTOS } = useUserTOS(); const { data: currentEpoch, isLoading: isLoadingCurrentEpoch } = useCurrentEpoch(); @@ -33,7 +31,6 @@ export default function useAppIsLoading(isFlushRequired: boolean): boolean { const { isFetching: isFetchingIsContract } = useIsContract(); return ( - isLoadingIsDecisionWindowOpen || isLoadingCurrentEpoch || (!isPreLaunch && !isAllocationsInitialized) || !isOnboardingInitialized || From 473b450ab8aeb44ac15d73bc1d396dcd9e4d2ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Mon, 15 Apr 2024 14:06:10 +0200 Subject: [PATCH 16/20] test: remove debug playground test --- client/cypress/e2e/_2makePendingSnapshot.cy.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/client/cypress/e2e/_2makePendingSnapshot.cy.ts b/client/cypress/e2e/_2makePendingSnapshot.cy.ts index d7eca0b0d2..5a08ab5ac0 100644 --- a/client/cypress/e2e/_2makePendingSnapshot.cy.ts +++ b/client/cypress/e2e/_2makePendingSnapshot.cy.ts @@ -32,8 +32,4 @@ describe('Make pending snapshot', () => { cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); }); }); - - it('playground', () => { - cy.wait(30000); - }) }); From dc520a47691b93caaee11f8639d5e68ae731c6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Tue, 16 Apr 2024 18:51:01 +0200 Subject: [PATCH 17/20] test: add debugs --- .github/workflows/ci-run.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci-run.yml b/.github/workflows/ci-run.yml index 24f8a8faec..f498b6cff2 100644 --- a/.github/workflows/ci-run.yml +++ b/.github/workflows/ci-run.yml @@ -64,6 +64,7 @@ jobs: SYMBOL: ETH IS_TESTNET: true VITE_NETWORK: Local + DEBUG: cypress-verbose:server:browsers:cri-client:send:*,cypress-verbose:server:browsers:cri-client:recv:*,cypress:server:browsers* steps: - uses: actions/checkout@v4.1.0 From def8637999685bdedfa3d9cf31c0284fd23203a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Wed, 24 Apr 2024 10:11:50 +0200 Subject: [PATCH 18/20] chore: CY 13.18.1 --- client/package.json | 2 +- client/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/package.json b/client/package.json index 51c963c810..af7ee80f83 100644 --- a/client/package.json +++ b/client/package.json @@ -113,7 +113,7 @@ "vite-plugin-html-config": "^1.0.11" }, "resolutions": { - "**/cypress": "13.7.3" + "**/cypress": "13.8.1" }, "engines": { "node": ">= 16.13.0 <17.0.0" diff --git a/client/yarn.lock b/client/yarn.lock index 1cb584c600..de7e41a9d8 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -5569,10 +5569,10 @@ cypress-wait-until@^2.0.1: resolved "https://registry.yarnpkg.com/cypress-wait-until/-/cypress-wait-until-2.0.1.tgz#69c575c7207d83e4ae023e2aaecf2b66148c9fc0" integrity sha512-+IyVnYNiaX1+C+V/LazrJWAi/CqiwfNoRSrFviECQEyolW1gDRy765PZosL2alSSGK8V10Y7BGfOQyZUDgmnjQ== -cypress@*, cypress@12.17.3, cypress@13.7.3: - version "13.7.3" - resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.7.3.tgz#3e7dcd32e007676a6c8e972293c50d6ef329d991" - integrity sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag== +cypress@*, cypress@12.17.3, cypress@13.8.1: + version "13.8.1" + resolved "https://registry.yarnpkg.com/cypress/-/cypress-13.8.1.tgz#f558e51b770a409e2360031bbd36c3f4fb3f2db4" + integrity sha512-Uk6ovhRbTg6FmXjeZW/TkbRM07KPtvM5gah1BIMp4Y2s+i/NMxgaLw0+PbYTOdw1+egE0FP3mWRiGcRkjjmhzA== dependencies: "@cypress/request" "^3.0.0" "@cypress/xvfb" "^1.2.4" From 8cdbbb9f9cfebd165c49e6b174f88e832995c212 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Wed, 24 Apr 2024 11:22:32 +0200 Subject: [PATCH 19/20] style: DEBUG flag removed --- .github/workflows/ci-run.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci-run.yml b/.github/workflows/ci-run.yml index f498b6cff2..24f8a8faec 100644 --- a/.github/workflows/ci-run.yml +++ b/.github/workflows/ci-run.yml @@ -64,7 +64,6 @@ jobs: SYMBOL: ETH IS_TESTNET: true VITE_NETWORK: Local - DEBUG: cypress-verbose:server:browsers:cri-client:send:*,cypress-verbose:server:browsers:cri-client:recv:*,cypress:server:browsers* steps: - uses: actions/checkout@v4.1.0 From 948ff9089a7e362732ec85aa3625c58c573a05b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Zi=C3=B3=C5=82ek?= Date: Thu, 2 May 2024 11:30:19 +0200 Subject: [PATCH 20/20] fix: project IPFS failure test to destroy request before route load --- client/cypress/e2e/project.cy.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/client/cypress/e2e/project.cy.ts b/client/cypress/e2e/project.cy.ts index 87e44a94d0..987d29ed04 100644 --- a/client/cypress/e2e/project.cy.ts +++ b/client/cypress/e2e/project.cy.ts @@ -150,15 +150,16 @@ Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => }); beforeEach(() => { + cy.intercept('GET', '**/ipfs/**', req => { + req.destroy(); + }); + mockCoinPricesServer(); localStorage.setItem(IS_ONBOARDING_DONE, 'true'); visitWithLoader(ROOT_ROUTES.projects.absolute); }); it('entering project view shows Toast with info about IPFS failure when all providers fail', () => { - cy.intercept('GET', '**/ipfs/**', req => { - req.destroy(); - }); cy.get('[data-test=Toast--ipfsMessage').should('be.visible'); });