diff --git a/.github/workflows/ci-run.yml b/.github/workflows/ci-run.yml index ce41dc0ead..c428a6c065 100644 --- a/.github/workflows/ci-run.yml +++ b/.github/workflows/ci-run.yml @@ -249,7 +249,6 @@ jobs: secrets: inherit with: image-tag: ${{ github.sha }} - git-ref: ${{ github.ref }} # +------------------------- # | Tests: NodeJS diff --git a/.github/workflows/deploy-master.yml b/.github/workflows/deploy-master.yml index b86928c320..3dee356789 100644 --- a/.github/workflows/deploy-master.yml +++ b/.github/workflows/deploy-master.yml @@ -15,7 +15,6 @@ jobs: secrets: inherit with: image-tag: ${{ github.sha }} - git-ref: ${{ github.ref }} run: name: Run uses: ./.github/workflows/tpl-start-env.yml diff --git a/.github/workflows/deploy-pr.yml b/.github/workflows/deploy-pr.yml index ad5d5cabeb..fd0d45862c 100644 --- a/.github/workflows/deploy-pr.yml +++ b/.github/workflows/deploy-pr.yml @@ -13,7 +13,7 @@ jobs: secrets: inherit with: image-tag: ${{ needs.run.outputs.sha }} - git-ref: ${{ needs.run.outputs.ref }} + deploy: name: Deploy needs: diff --git a/.github/workflows/deploy-prod.yml b/.github/workflows/deploy-prod.yml index 085aa6e5ab..558bf7786d 100644 --- a/.github/workflows/deploy-prod.yml +++ b/.github/workflows/deploy-prod.yml @@ -5,29 +5,31 @@ on: tags: - v[0-9]+.[0-9]+.[0-9]+ +env: + IMAGE_TAG: "${{ github.sha }}" + GIT_TAG: "${{ github.ref }}" + jobs: - run-deployment: - name: Run Deployment + build: + name: Build + uses: ./.github/workflows/tpl-images.yml + secrets: inherit + with: + image-tag: ${{ github.sha }} + output: + name: Output Variables + needs: + - build runs-on: - metal steps: - - name: Sanitise variables - id: sanitise-vars - env: - IMAGE_TAG: "${{ github.ref }}" - GIT_REF: "${{ github.sha }}" - run: | - set -ex - - echo "IMAGE_TAG=${IMAGE_TAG##*/}" >> $GITHUB_OUTPUT - echo "GIT_REF=${GIT_REF}" >> $GITHUB_OUTPUT + - name: Show variables for the CI pipeline + run : | + echo '================================' + echo ' Use the following variables' + echo '' + echo "CI_PIPELINE_SOURCE: pipeline" + echo "DEV_IMAGE_TAG: ${{ github.sha }}" + echo "PROD_IMAGE_TAG: ${GIT_TAG##*/}" shell: bash - - name: Invoke octant-pipelines workflow - uses: benc-uk/workflow-dispatch@v1 - with: - workflow: deploy-prod.yml - repo: golemfoundation/octant-pipelines - ref: master - inputs: '{"image_tag":"${{ steps.sanitise-vars.outputs.IMAGE_TAG }}","git_ref":"${{ steps.sanitise-vars.outputs.GIT_REF }}"}' - token: "${{ secrets.GH_BOT_TOKEN }}" diff --git a/.github/workflows/deploy-rc.yml b/.github/workflows/deploy-rc.yml index ad9bfd258a..78cb6ef7bb 100644 --- a/.github/workflows/deploy-rc.yml +++ b/.github/workflows/deploy-rc.yml @@ -19,7 +19,6 @@ jobs: secrets: inherit with: image-tag: ${{ github.sha }} - git-ref: ${{ github.ref }} deploy: name: Deploy needs: diff --git a/.github/workflows/deploy-uat.yml b/.github/workflows/deploy-uat.yml index a7033ec632..a125af0bb5 100644 --- a/.github/workflows/deploy-uat.yml +++ b/.github/workflows/deploy-uat.yml @@ -15,7 +15,6 @@ jobs: secrets: inherit with: image-tag: ${{ github.sha }} - git-ref: ${{ github.ref }} run: name: Run uses: ./.github/workflows/tpl-start-env.yml diff --git a/.github/workflows/tpl-images.yml b/.github/workflows/tpl-images.yml index c9e29953fd..a395cbb510 100644 --- a/.github/workflows/tpl-images.yml +++ b/.github/workflows/tpl-images.yml @@ -6,9 +6,6 @@ on: image-tag: required: true type: string - git-ref: - required: true - type: string concurrency: group: "${{ github.ref }}-images" cancel-in-progress: true @@ -34,8 +31,6 @@ jobs: # account # see: https://github.com/actions/checkout/issues/211 path: __local - ref: ${{ inputs.git-ref }} - - name: Login to Docker registry uses: docker/login-action@v3 with: diff --git a/backend/app/infrastructure/__init__.py b/backend/app/infrastructure/__init__.py index d205ec2d6d..5e61c8c8e9 100644 --- a/backend/app/infrastructure/__init__.py +++ b/backend/app/infrastructure/__init__.py @@ -22,7 +22,7 @@ def __init__(self, *args, **kwargs): Resource.__init__(self, *args, *kwargs) @classmethod - def canonize_address(cls, field_name: str, force=True): + def canonize_address(cls, field_name: str = "user_address", force=True): def _add_address_canonization(handler): def _decorated(*args, **kwargs): field_value = kwargs.get(field_name) @@ -36,22 +36,14 @@ def _decorated(*args, **kwargs): return _add_address_canonization - @classmethod - def _default_address_canonizer(cls, attr): - user_address_canonizer = OctantResource.canonize_address( - field_name="user_address", force=False - ) - proposal_address_canonizer = OctantResource.canonize_address( - field_name="proposal_address", force=False - ) - return user_address_canonizer(proposal_address_canonizer(attr)) - def __getattribute__(self, name): + user_address_canonizer = OctantResource.canonize_address(force=False) + attr = object.__getattribute__(self, name) decorator = default_decorators.get(name) if decorator is not None: - attr = OctantResource._default_address_canonizer(decorator(attr)) + attr = user_address_canonizer(decorator(attr)) return attr diff --git a/backend/app/infrastructure/routes/allocations.py b/backend/app/infrastructure/routes/allocations.py index 24ed532173..3893fc2700 100644 --- a/backend/app/infrastructure/routes/allocations.py +++ b/backend/app/infrastructure/routes/allocations.py @@ -1,12 +1,10 @@ import dataclasses from flask import current_app as app -from flask import request from flask_restx import Namespace, fields from app.extensions import api from app.infrastructure import OctantResource -from app.modules.common import parse_bool from app.modules.user.allocations import controller ns = Namespace("allocations", description="Octant allocations") @@ -247,22 +245,12 @@ def post(self, user_address: str): }, ) class EpochAllocations(OctantResource): - @ns.param( - "includeZeroAllocations", - description="Include zero allocations to projects. Defaults to false.", - _in="query", - ) @ns.marshal_with(epoch_allocations_model) @ns.response(200, "Epoch allocations successfully retrieved") def get(self, epoch: int): - include_zero_allocations = request.args.get( - "includeZeroAllocations", default=False, type=parse_bool - ) app.logger.debug(f"Getting latest allocations in epoch {epoch}") - allocs = controller.get_all_allocations(epoch, include_zero_allocations) - app.logger.debug( - f"Allocations for epoch {epoch} (with zero allocations: {include_zero_allocations}): {allocs}" - ) + allocs = controller.get_all_allocations(epoch) + app.logger.debug(f"Allocations for epoch {epoch}: {allocs}") return {"allocations": allocs} diff --git a/backend/app/infrastructure/routes/deposits.py b/backend/app/infrastructure/routes/deposits.py index 87a2adfd70..00e17dd050 100644 --- a/backend/app/infrastructure/routes/deposits.py +++ b/backend/app/infrastructure/routes/deposits.py @@ -1,3 +1,4 @@ +from eth_utils import to_checksum_address from flask import current_app as app from flask_restx import Namespace, fields @@ -91,48 +92,44 @@ def get(self, epoch): return {"lockedRatio": locked_ratio} -@ns.route("/users//") +@ns.route("/users//") @ns.doc( description="Returns user's effective deposit for a finialized or pending epoch.", params={ "epoch": "Epoch number", - "user_address": "User ethereum address in hexadecimal form (case-insensitive, prefixed with 0x)", + "address": "User ethereum address in hexadecimal form (case-insensitive, prefixed with 0x)", }, ) class UserEffectiveDeposit(OctantResource): @ns.marshal_with(user_effective_deposit_model) @ns.response(200, "User effective deposit successfully retrieved") - def get(self, user_address: str, epoch: int): - app.logger.debug( - f"Getting user {user_address} effective deposit in epoch {epoch}" - ) - result = get_user_effective_deposit(user_address, epoch) - app.logger.debug( - f"User {user_address} effective deposit in epoch {epoch}: {result}" - ) + def get(self, address: str, epoch: int): + app.logger.debug(f"Getting user {address} effective deposit in epoch {epoch}") + result = get_user_effective_deposit(to_checksum_address(address), epoch) + app.logger.debug(f"User {address} effective deposit in epoch {epoch}: {result}") return { "effectiveDeposit": result, } -@ns.route("/users//estimated_effective_deposit") +@ns.route("/users//estimated_effective_deposit") @ns.doc( description="Returns user's estimated effective deposit for the current epoch.", params={ - "user_address": "User ethereum address in hexadecimal form (case-insensitive, prefixed with 0x)", + "address": "User ethereum address in hexadecimal form (case-insensitive, prefixed with 0x)", }, ) class UserEstimatedEffectiveDeposit(OctantResource): @ns.marshal_with(user_effective_deposit_model) @ns.response(200, "User estimated effective deposit successfully retrieved") - def get(self, user_address: str): + def get(self, address: str): app.logger.debug( - f"Getting user {user_address} estimated effective deposit in the current epoch" + f"Getting user {address} estimated effective deposit in the current epoch" ) - result = estimate_user_effective_deposit(user_address) + result = estimate_user_effective_deposit(to_checksum_address(address)) app.logger.debug( - f"User {user_address} estimated effective deposit in the current epoch: {result}" + f"User {address} estimated effective deposit in the current epoch: {result}" ) return { diff --git a/backend/app/infrastructure/routes/rewards.py b/backend/app/infrastructure/routes/rewards.py index 7ee5aa887d..06922a7cf3 100644 --- a/backend/app/infrastructure/routes/rewards.py +++ b/backend/app/infrastructure/routes/rewards.py @@ -1,3 +1,4 @@ +from eth_utils import to_checksum_address from flask import current_app as app from flask_restx import Namespace, fields @@ -190,9 +191,10 @@ class UserBudget(OctantResource): @ns.marshal_with(user_budget_model) @ns.response(200, "Budget successfully retrieved") def get(self, user_address, epoch): - app.logger.debug(f"Getting user {user_address} budget in epoch {epoch}") - budget = get_budget(user_address, epoch) - app.logger.debug(f"User {user_address} budget in epoch {epoch}: {budget}") + checksum_address = to_checksum_address(user_address) + app.logger.debug(f"Getting user {checksum_address} budget in epoch {epoch}") + budget = get_budget(checksum_address, epoch) + app.logger.debug(f"User {checksum_address} budget in epoch {epoch}: {budget}") return {"budget": budget} diff --git a/backend/app/infrastructure/routes/user.py b/backend/app/infrastructure/routes/user.py index b5c88844ca..c6a59d0141 100644 --- a/backend/app/infrastructure/routes/user.py +++ b/backend/app/infrastructure/routes/user.py @@ -1,3 +1,4 @@ +from eth_utils import to_checksum_address from flask import current_app as app, request from flask_restx import Namespace, fields from flask_restx import reqparse @@ -125,6 +126,8 @@ class PatronMode(OctantResource): @ns.marshal_with(user_patron_mode_status_model) @ns.response(200, "User's patron mode status retrieved") def get(self, user_address: str): + user_address = to_checksum_address(user_address) + app.logger.debug(f"Getting user {user_address} patron mode status") patron_mode_status = user_controller.get_patron_mode_status(user_address) app.logger.debug( @@ -141,6 +144,7 @@ def get(self, user_address: str): @ns.response(204, "User's patron mode status updated.") @ns.response(400, "Could not update patron mode status.") def patch(self, user_address: str): + user_address = to_checksum_address(user_address) signature = ns.payload.get("signature") app.logger.info(f"Updating user {user_address} patron mode status") diff --git a/backend/app/modules/common/__init__.py b/backend/app/modules/common/__init__.py deleted file mode 100644 index a1f5a27336..0000000000 --- a/backend/app/modules/common/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -def parse_bool(value: str) -> bool: - return value.lower() in ["true", "1"] if value is not None else False diff --git a/backend/app/modules/multisig_signatures/controller.py b/backend/app/modules/multisig_signatures/controller.py index 9f5b9abc9e..5459ea034a 100644 --- a/backend/app/modules/multisig_signatures/controller.py +++ b/backend/app/modules/multisig_signatures/controller.py @@ -3,7 +3,6 @@ from app.modules.dto import SignatureOpType from app.modules.multisig_signatures.dto import Signature, ApprovedSignatureTypes from app.modules.registry import get_services -from app.exceptions import InvalidEpoch def get_last_pending_signature( @@ -44,22 +43,14 @@ def apply_pending_allocation_signature(signature_id: int): def _apply(op_type: SignatureOpType, signature_id): - try: - context = _get_context(op_type) - except InvalidEpoch: - return None - + context = _get_context(op_type) service = get_services(context.epoch_state).multisig_signatures_service service.apply_staged_signatures(context, signature_id) def _approve(op_type: SignatureOpType) -> list[Signature]: - try: - context = _get_context(op_type) - except InvalidEpoch: - return [] - + context = _get_context(op_type) service = get_services(context.epoch_state).multisig_signatures_service return service.approve_pending_signatures(context) diff --git a/backend/app/modules/user/allocations/controller.py b/backend/app/modules/user/allocations/controller.py index 81e51b94e9..5fa26076a8 100644 --- a/backend/app/modules/user/allocations/controller.py +++ b/backend/app/modules/user/allocations/controller.py @@ -23,14 +23,12 @@ def get_user_next_nonce(user_address: str) -> int: return service.get_user_next_nonce(user_address) -def get_all_allocations( - epoch_num: int, include_zero_allocations=True -) -> List[ProposalDonationDTO]: +def get_all_allocations(epoch_num: int) -> List[ProposalDonationDTO]: context = epoch_context(epoch_num) if context.epoch_state > EpochState.PENDING: raise NotImplementedForGivenEpochState() service = get_services(context.epoch_state).user_allocations_service - return service.get_all_allocations(context, include_zero_allocations) + return service.get_all_allocations(context) def get_all_donations_by_project( diff --git a/backend/app/modules/user/allocations/service/saved.py b/backend/app/modules/user/allocations/service/saved.py index 947cd9ccd5..7bc1809b25 100644 --- a/backend/app/modules/user/allocations/service/saved.py +++ b/backend/app/modules/user/allocations/service/saved.py @@ -56,9 +56,7 @@ def get_user_allocations_by_timestamp( ) ] - def get_all_allocations( - self, context: Context, include_zero_allocations=True - ) -> List[ProposalDonationDTO]: + def get_all_allocations(self, context: Context) -> List[ProposalDonationDTO]: allocations = database.allocations.get_all(context.epoch_details.epoch_num) return [ ProposalDonationDTO( @@ -67,7 +65,6 @@ def get_all_allocations( proposal=alloc.proposal_address, ) for alloc in allocations - if include_zero_allocations or alloc.amount > 0 ] def get_allocations_by_project( diff --git a/backend/app/settings.py b/backend/app/settings.py index 658be0d02f..b36313eb14 100644 --- a/backend/app/settings.py +++ b/backend/app/settings.py @@ -3,12 +3,14 @@ from dotenv import load_dotenv from web3 import Web3 -from app.modules.common import parse_bool - # Load environment variables from the .env file load_dotenv() +def _parse_bool(value: str) -> bool: + return value.lower() in ["true", "1"] if value is not None else False + + class Config(object): """Base configuration.""" @@ -23,7 +25,7 @@ class Config(object): ETHERSCAN_API_KEY = os.getenv("ETHERSCAN_API_KEY") BITQUERY_API_KEY = os.getenv("BITQUERY_API_KEY") BITQUERY_BEARER = os.getenv("BITQUERY_BEARER") - SCHEDULER_ENABLED = parse_bool(os.getenv("SCHEDULER_ENABLED")) + SCHEDULER_ENABLED = _parse_bool(os.getenv("SCHEDULER_ENABLED")) CACHE_TYPE = "SimpleCache" # Smart contract addresses @@ -44,12 +46,12 @@ class Config(object): TESTNET_MULTISIG_PRIVATE_KEY = os.getenv("TESTNET_MULTISIG_PRIVATE_KEY") # Confirming withdrawals in Vault - VAULT_CONFIRM_WITHDRAWALS_ENABLED = parse_bool( + VAULT_CONFIRM_WITHDRAWALS_ENABLED = _parse_bool( os.getenv("VAULT_CONFIRM_WITHDRAWALS_ENABLED") ) # GLM claiming - GLM_CLAIM_ENABLED = parse_bool(os.getenv("GLM_CLAIM_ENABLED")) + GLM_CLAIM_ENABLED = _parse_bool(os.getenv("GLM_CLAIM_ENABLED")) GLM_WITHDRAWAL_AMOUNT = int( os.getenv("GLM_WITHDRAWAL_AMOUNT", 1000_000000000_000000000) ) @@ -81,7 +83,7 @@ class ProdConfig(Config): "pool_size": SQLALCHEMY_CONNECTION_POOL_SIZE, "max_overflow": SQLALCHEMY_CONNECTION_POOL_MAX_OVERFLOW, } - X_REAL_IP_REQUIRED = parse_bool(os.getenv("X_REAL_IP_REQUIRED", "true")) + X_REAL_IP_REQUIRED = _parse_bool(os.getenv("X_REAL_IP_REQUIRED", "true")) class DevConfig(Config): @@ -95,7 +97,7 @@ class DevConfig(Config): # Put the db file in project root DB_PATH = os.path.join(Config.PROJECT_ROOT, DB_NAME) SQLALCHEMY_DATABASE_URI = f"sqlite:///{DB_PATH}" - X_REAL_IP_REQUIRED = parse_bool(os.getenv("X_REAL_IP_REQUIRED", "false")) + X_REAL_IP_REQUIRED = _parse_bool(os.getenv("X_REAL_IP_REQUIRED", "false")) class ComposeConfig(Config): @@ -104,7 +106,7 @@ class ComposeConfig(Config): ENV = "dev" DEBUG = True SQLALCHEMY_DATABASE_URI = os.getenv("DB_URI") - X_REAL_IP_REQUIRED = parse_bool(os.getenv("X_REAL_IP_REQUIRED", "false")) + X_REAL_IP_REQUIRED = _parse_bool(os.getenv("X_REAL_IP_REQUIRED", "false")) class TestConfig(Config): diff --git a/backend/tests/modules/user/allocations/test_saved_allocations.py b/backend/tests/modules/user/allocations/test_saved_allocations.py index 84aa4db0e8..b8e3f77dd9 100644 --- a/backend/tests/modules/user/allocations/test_saved_allocations.py +++ b/backend/tests/modules/user/allocations/test_saved_allocations.py @@ -257,55 +257,6 @@ def test_get_all_allocations_returns_list_of_allocations( assert i in expected_results -def test_get_all_allocations_returns_list_of_allocations_with_zero_allocations( - service, context, mock_users_db -): - user1, user2, user3 = mock_users_db - - user1_allocations = make_user_allocation(context, user1, allocations=2) - user2_allocations = make_user_allocation(context, user2, allocations=2) - user3_allocations = make_user_allocation( - context, - user3, - allocation_items=[AllocationItem(context.projects_details.projects[0], 0)], - ) - - user1_donations = [_alloc_item_to_donation(a, user1) for a in user1_allocations] - user2_donations = [_alloc_item_to_donation(a, user2) for a in user2_allocations] - user3_donations = [_alloc_item_to_donation(a, user3) for a in user3_allocations] - expected_results = user1_donations + user2_donations + user3_donations - - result = service.get_all_allocations(context) - - assert len(result) == 5 - for i in result: - assert i in expected_results - - -def test_get_all_allocations_returns_list_of_allocations_without_zero_allocations( - service, context, mock_users_db -): - user1, user2, user3 = mock_users_db - - user1_allocations = make_user_allocation(context, user1, allocations=2) - user2_allocations = make_user_allocation(context, user2, allocations=2) - make_user_allocation( - context, - user3, - allocation_items=[AllocationItem(context.projects_details.projects[0], 0)], - ) - - user1_donations = [_alloc_item_to_donation(a, user1) for a in user1_allocations] - user2_donations = [_alloc_item_to_donation(a, user2) for a in user2_allocations] - expected_results = user1_donations + user2_donations - - result = service.get_all_allocations(context, include_zero_allocations=False) - - assert len(result) == 4 - for i in result: - assert i in expected_results - - def test_get_all_allocations_does_not_include_revoked_allocations_in_returned_list( service, context, mock_users_db ): diff --git a/client/cypress/e2e/allocationAllocationWindowClosed.cy.ts b/client/cypress/e2e/allocationAllocationWindowClosed.cy.ts index 2958e50b71..190445cf27 100644 --- a/client/cypress/e2e/allocationAllocationWindowClosed.cy.ts +++ b/client/cypress/e2e/allocationAllocationWindowClosed.cy.ts @@ -16,9 +16,8 @@ import { ROOT_ROUTES } from 'src/routes/RootRoutes/routes'; let wasTimeMoved = false; -[Object.values(viewports)[0]].forEach(({ isDesktop }) => { - - describe('move time', { testIsolation: false }, () => { +[Object.values(viewports)[0]].forEach(({ device, viewportWidth, viewportHeight, isDesktop }) => { + describe('move time', () => { before(() => { /** * Global Metamask setup done by Synpress is not always done. @@ -26,17 +25,18 @@ let wasTimeMoved = false; * setupMetamask is required in each test suite. */ cy.setupMetamask(); - cy.clock(); }); - it('allocation window is closed, when it is not, move time', () => { + beforeEach(() => { cy.disconnectMetamaskWalletFromAllDapps(); mockCoinPricesServer(); localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); localStorage.setItem(IS_ONBOARDING_DONE, 'true'); localStorage.setItem(ALLOCATION_ITEMS_KEY, '[]'); visitWithLoader(ROOT_ROUTES.playground.absolute); + }); + it('allocation window is closed, when it is not, move time', () => { cy.window().then(async win => { const isDecisionWindowOpen = win.clientReactQuery.getQueryData( QUERY_KEYS.isDecisionWindowOpen, @@ -66,115 +66,72 @@ let wasTimeMoved = false; } }); }); + }); + describe( + `allocation (allocation window closed): ${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(); + }); - it('AllocationItem shows all the elements', () => { - 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); + beforeEach(() => { + 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); - 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^=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^=ProjectsView__ProjectsListItem') + .eq(0) + .find('[data-test=ProjectsListItem__ButtonAddToAllocate]') + .click(); + navigateWithCheck(ROOT_ROUTES.allocation.absolute); + }); - connectWallet(true, false); - 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()); + it('AllocationItem shows all the elements', () => { + connectWallet(true, false); + 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=AllocationItemRewards]') - .contains(isDesktop ? 'Threshold data unavailable' : 'No threshold data'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .should('be.disabled'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText__suffix]') - .contains('GWEI'); - }); - }); - // eslint-disable-next-line jest/no-disabled-tests - // describe( - // `allocation (allocation window closed): ${device}`, - // { viewportHeight, viewportWidth }, - // () => { - // beforeEach(() => { - // 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); - // - // 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); - // }); - // - // it('AllocationItem shows all the elements', () => { - // connectWallet(true, false); - // 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=AllocationItemRewards]') - // .contains(isDesktop ? 'Threshold data unavailable' : 'No threshold data'); - // cy.get('[data-test=AllocationItem]') - // .eq(0) - // .find('[data-test=AllocationItem__InputText]') - // .should('be.disabled'); - // 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__imageProfile]') + .should(isDesktop ? 'be.visible' : 'not.be.visible'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItemRewards]') + .contains(isDesktop ? 'Threshold data unavailable' : 'No threshold data'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .should('be.disabled'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText__suffix]') + .contains('GWEI'); + }); + }, + ); }); diff --git a/client/cypress/e2e/allocationsAllocationWindowOpen.cy.ts b/client/cypress/e2e/allocationsAllocationWindowOpen.cy.ts deleted file mode 100644 index 928a311e64..0000000000 --- a/client/cypress/e2e/allocationsAllocationWindowOpen.cy.ts +++ /dev/null @@ -1,206 +0,0 @@ -// eslint-disable-next-line import/no-extraneous-dependencies -import chaiColors from 'chai-colors'; - -import { - visitWithLoader, - mockCoinPricesServer, - navigateWithCheck, - connectWallet, - moveEpoch, -} from 'cypress/utils/e2e'; -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); - -let wasTimeMoved = false; -const budget = 10000000000; -// const budgetToBig = formatUnitsBigInt(BigInt(budget + 1)); - -[Object.values(viewports)[0]].forEach(() => { - describe('move time', { testIsolation: false }, () => { - 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(); - cy.clock(); - }); - beforeEach(() => { - cy.disconnectMetamaskWalletFromAllDapps(); - mockCoinPricesServer(); - localStorage.setItem(IS_ONBOARDING_ALWAYS_VISIBLE, 'false'); - localStorage.setItem(IS_ONBOARDING_DONE, 'true'); - localStorage.setItem(ALLOCATION_ITEMS_KEY, '[]'); - visitWithLoader(ROOT_ROUTES.playground.absolute); - }); - - it('allocation window is open, when it is not, move time', () => { - cy.window().then(async win => { - const isDecisionWindowOpen = win.clientReactQuery.getQueryData( - QUERY_KEYS.isDecisionWindowOpen, - ); - - if (isDecisionWindowOpen) { - expect(true).to.be.true; - return; - } - - // Move time only once, for the first device. - if (!wasTimeMoved) { - cy.wrap(null).then(() => { - return moveEpoch(win, 'decisionWindowOpen').then(() => { - const isDecisionWindowOpenAfter = win.clientReactQuery.getQueryData( - QUERY_KEYS.isDecisionWindowOpen, - ); - wasTimeMoved = true; - expect(isDecisionWindowOpenAfter).to.be.true; - }); - }); - } else { - expect(true).to.be.true; - } - }); - }); - - it('playground', () => { - cy.wait(30000); - }); - - it('AllocationItem shows all the elements', () => { - 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.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget } }); - - 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'); - 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('be.visible'); - cy.get('[data-test=AllocationItem]') - .eq(0) - .find('[data-test=AllocationItem__InputText]') - .should('be.enabled'); - }); - }); - // eslint-disable-next-line jest/no-disabled-tests - // describe( - // `allocation (allocation window open): ${device}`, - // { viewportHeight, viewportWidth }, - // () => { - // beforeEach(() => { - // 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.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget } }); - // - // 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('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]') - // .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 and shakes 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(budgetToBig); - // cy.get('[data-test=AllocationItem]') - // .eq(0) - // .find('[data-test=AllocationItem__InputText]') - // .should('have.css', 'background-color') - // .and('be.colored', '#ff6157'); - // cy.get('[data-test=AllocationItem]') - // .eq(0) - // .find('[data-test=AllocationItem__InputText]') - // .invoke('dat', 'iserror') - // .should('be.true'); - // }); - // }, - // ); -}); diff --git a/client/cypress/e2e/allocationsAllocationWindowOpen.ts b/client/cypress/e2e/allocationsAllocationWindowOpen.ts new file mode 100644 index 0000000000..dbb7f1b35d --- /dev/null +++ b/client/cypress/e2e/allocationsAllocationWindowOpen.ts @@ -0,0 +1,152 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import chaiColors from 'chai-colors'; + +import { + visitWithLoader, + mockCoinPricesServer, + navigateWithCheck, + connectWallet, + moveEpoch, +} from 'cypress/utils/e2e'; +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'; +import { formatUnitsBigInt } from 'src/utils/formatUnitsBigInt'; + +chai.use(chaiColors); + +let wasTimeMoved = false; +const budget = 10000000000; +const budgetToBig = formatUnitsBigInt(BigInt(budget + 1)); + +Object.values(viewports).forEach(({ device, viewportWidth, viewportHeight }) => { + describe( + `allocation (allocation window open): ${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'); + localStorage.setItem(ALLOCATION_ITEMS_KEY, '[]'); + visitWithLoader(ROOT_ROUTES.projects.absolute); + connectWallet(true, false); + cy.intercept('GET', '/rewards/budget/*/epoch/*', { body: { budget } }); + + 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('allocation window is open, when it is not, move time', () => { + cy.window().then(async win => { + const isDecisionWindowOpen = win.clientReactQuery.getQueryData( + QUERY_KEYS.isDecisionWindowOpen, + ); + + if (isDecisionWindowOpen) { + expect(true).to.be.true; + } + + // Move time only once, for the first device. + if (!wasTimeMoved) { + const currentEpochBefore = Number( + win.clientReactQuery.getQueryData(QUERY_KEYS.currentEpoch), + ); + + cy.wrap(null).then(() => { + return moveEpoch(win, 'decisionWindowOpen').then(() => { + const currentEpochAfter = Number( + win.clientReactQuery.getQueryData(QUERY_KEYS.currentEpoch), + ); + wasTimeMoved = true; + expect(currentEpochBefore + 1).to.eq(currentEpochAfter); + }); + }); + } else { + expect(true).to.be.true; + } + }); + }); + + 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('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]') + .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 and shakes 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(budgetToBig); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .should('have.css', 'background-color') + .and('be.colored', '#ff6157'); + cy.get('[data-test=AllocationItem]') + .eq(0) + .find('[data-test=AllocationItem__InputText]') + .invoke('dat', 'iserror') + .should('be.true'); + }); + }, + ); +}); diff --git a/client/cypress/support/e2e.d.ts b/client/cypress/support/e2e.d.ts index e29381d44e..b640c04f1a 100644 --- a/client/cypress/support/e2e.d.ts +++ b/client/cypress/support/e2e.d.ts @@ -4,8 +4,7 @@ declare namespace Cypress { interface ApplicationWindow { // Importing QueryClient breaks making these types not visible. clientReactQuery: any; - mutateAsyncMoveToDecisionWindowClosed: () => Promise; - mutateAsyncMoveToDecisionWindowOpen: () => Promise; - timeToIncrease?: number; + mutateAsyncMoveToDecisionWindowClosed: () => Promise; + mutateAsyncMoveToDecisionWindowOpen: () => Promise; } } diff --git a/client/cypress/utils/e2e.ts b/client/cypress/utils/e2e.ts index 81629b150d..107dde1e54 100644 --- a/client/cypress/utils/e2e.ts +++ b/client/cypress/utils/e2e.ts @@ -51,116 +51,95 @@ export const connectWallet = ( return cy.acceptMetamaskAccess(); }; -const test = (cypressWindow, moveTo): Chainable => { - if (moveTo === 'decisionWindowOpen') { - cy.wrap(null).then(() => { - return cypressWindow.mutateAsyncMoveToDecisionWindowOpen().then(timeToIncrease => { - // Waiting 5s is a way to prevent the effects of slowing down the e2e envirownment (data update). - cy.wait(5000); - cy.log(`timeToIncrease ${timeToIncrease}`); - cy.tick(timeToIncrease); - }); - }); - - return cy.wrap(null).then(() => { - return axios.post(`${env.serverEndpoint}snapshots/pending`).then(() => { - cy.log('3'); - // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). - cy.wait(2000); - // reload is needed to get updated data in the app - cy.reload(); - cy.get('[data-test*=AppLoader]').should('not.exist'); - cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); - // reload is needed to get updated data in the app - cy.reload(); - }); - }); - } else { - cy.wrap(null).then(() => { - return cypressWindow.mutateAsyncMoveToDecisionWindowOpen().then(timeToIncrease => { - // Waiting 5s is a way to prevent the effects of slowing down the e2e envirownment (data update). - cy.wait(5000); - cy.log(`timeToIncrease ${timeToIncrease}`); - cy.tick(timeToIncrease); - }); - }); - - cy.wrap(null).then(() => { - return axios.post(`${env.serverEndpoint}snapshots/pending`).then(() => { - cy.log('3'); +const test = (cypressWindow, moveTo): Promise => { + return new Promise(resolve => { + if (moveTo === 'decisionWindowOpen') { + cypressWindow.mutateAsyncMoveToDecisionWindowOpen().then(() => { // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). cy.wait(2000); - // reload is needed to get updated data in the app - cy.reload(); - cy.get('[data-test*=AppLoader]').should('not.exist'); - cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); - // reload is needed to get updated data in the app - cy.reload(); - }); - }); - - cy.wrap(null).then(() => { - return cypressWindow.mutateAsyncMoveToDecisionWindowClosed().then(timeToIncrease2 => { - cy.log('4'); - // Waiting 5s is a way to prevent the effects of slowing down the e2e envirownment (data update). - cy.wait(5000); - cy.log(`timeToIncrease ${timeToIncrease2}`); - cy.tick(timeToIncrease2); + axios.post(`${env.serverEndpoint}snapshots/pending`).then(() => { + cy.log('3'); + // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). + cy.wait(2000); + // reload is needed to get updated data in the app + cy.reload(); + cy.get('[data-test*=AppLoader]').should('not.exist'); + cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); + // reload is needed to get updated data in the app + cy.reload(); + resolve(true); + }); }); - }); - - return cy.wrap(null).then(() => { - return axios.post(`${env.serverEndpoint}snapshots/finalized`).then(() => { + } else { + cypressWindow.mutateAsyncMoveToDecisionWindowOpen().then(() => { // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). cy.wait(2000); - // reload is needed to get updated data in the app - cy.reload(); - cy.get('[data-test*=AppLoader]').should('not.exist'); - cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); - // reload is needed to get updated data in the app - cy.reload(); + axios.post(`${env.serverEndpoint}snapshots/pending`).then(() => { + cy.log('3'); + // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). + cy.wait(2000); + // reload is needed to get updated data in the app + cy.reload(); + cy.get('[data-test*=AppLoader]').should('not.exist'); + cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); + // reload is needed to get updated data in the app + cy.reload(); + cypressWindow.mutateAsyncMoveToDecisionWindowClosed().then(() => { + cy.log('4'); + // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). + cy.wait(2000); + axios.post(`${env.serverEndpoint}snapshots/finalized`).then(() => { + // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). + cy.wait(2000); + // reload is needed to get updated data in the app + cy.reload(); + cy.get('[data-test*=AppLoader]').should('not.exist'); + cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); + // reload is needed to get updated data in the app + cy.reload(); + resolve(true); + }); + }); + }); }); - }); - } + } + }); }; export const moveEpoch = ( cypressWindow: Cypress.AUTWindow, moveTo: 'decisionWindowClosed' | 'decisionWindowOpen', -): Chainable => { - const isDecisionWindowOpen = cypressWindow.clientReactQuery.getQueryData( - QUERY_KEYS.isDecisionWindowOpen, - ); - - if (isDecisionWindowOpen) { - cy.wrap(null).then(() => { - return cypressWindow.mutateAsyncMoveToDecisionWindowClosed().then(timeToIncrease => { +): Promise => { + return new Cypress.Promise(resolve => { + const isDecisionWindowOpen = cypressWindow.clientReactQuery.getQueryData( + QUERY_KEYS.isDecisionWindowOpen, + ); + + if (isDecisionWindowOpen) { + cypressWindow.mutateAsyncMoveToDecisionWindowClosed().then(() => { cy.log('1'); - // Waiting 5s is a way to prevent the effects of slowing down the e2e envirownment (data update). - cy.wait(5000); - cy.log(`timeToIncrease ${timeToIncrease}`); - cy.tick(timeToIncrease); - }); - }); - cy.wrap(null).then(() => { - return axios.post(`${env.serverEndpoint}snapshots/finalized`).then(() => { - cy.log('2'); // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). cy.wait(2000); - // reload is needed to get updated data in the app - cy.reload(); - cy.get('[data-test*=AppLoader]').should('not.exist'); - cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); - // reload is needed to get updated data in the app - cy.reload(); + axios.post(`${env.serverEndpoint}snapshots/finalized`).then(() => { + cy.log('2'); + // Waiting 2s is a way to prevent the effects of slowing down the e2e environment (data update). + cy.wait(2000); + // reload is needed to get updated data in the app + cy.reload(); + cy.get('[data-test*=AppLoader]').should('not.exist'); + cy.get('[data-test=SyncView]', { timeout: 60000 }).should('not.exist'); + // reload is needed to get updated data in the app + cy.reload(); + + test(cypressWindow, moveTo).then(() => { + resolve(true); + }); + }); }); - }); - return cy.wrap(null).then(() => { - return test(cypressWindow, moveTo); - }); - } else { - return cy.wrap(null).then(() => { - return test(cypressWindow, moveTo); - }); - } + } else { + test(cypressWindow, moveTo).then(() => { + resolve(true); + }); + } + }); }; diff --git a/client/global.d.ts b/client/global.d.ts index 9ebe3d8ee0..b6f360662f 100644 --- a/client/global.d.ts +++ b/client/global.d.ts @@ -11,6 +11,5 @@ export declare global { [WINDOW_PROJECTS_LOADED_ARCHIVED_EPOCHS_NUMBER]?: number; [WINDOW_PROJECTS_SCROLL_Y]?: number; clientReactQuery?: QueryClient; - timeToIncrease?: number; } } diff --git a/client/src/App.tsx b/client/src/App.tsx index e59d4aeb5c..0d38c6cbc7 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,5 +1,4 @@ -import React, {ReactElement, useState, Fragment} from 'react'; -// import {useConfig} from 'wagmi'; +import React, { ReactElement, useState, Fragment } from 'react'; import AppLoader from 'components/shared/AppLoader'; import ModalOnboarding from 'components/shared/ModalOnboarding/ModalOnboarding'; @@ -23,19 +22,10 @@ const App = (): ReactElement => { const { isSyncingInProgress } = useAppConnectManager(isFlushRequired, setIsFlushRequired); const isLoading = useAppIsLoading(isFlushRequired); const isProjectAdminMode = useIsProjectAdminMode(); - // const wagmiConfig = useConfig(); // useCypressHelpers needs to be called after all the initial sets done above. const { isFetching: isFetchingCypressHelpers } = useCypressHelpers(); - // useEffect(() => { - // const test = async () => { - // const block = await wagmiConfig.publicClient.getBlock(); - // console.log(block.timestamp); - // } - // test(); - // }, [wagmiConfig.publicClient]); - if (isLoading && !isSyncingInProgress) { return ; } diff --git a/client/src/api/clients/client-wagmi.ts b/client/src/api/clients/client-wagmi.ts index 58cc9bab1a..3da378c303 100644 --- a/client/src/api/clients/client-wagmi.ts +++ b/client/src/api/clients/client-wagmi.ts @@ -1,4 +1,4 @@ -// import { LedgerConnector } from '@wagmi/connectors/ledger'; +import { LedgerConnector } from '@wagmi/connectors/ledger'; import { w3mConnectors } from '@web3modal/ethereum'; import { configureChains, createConfig, ChainProviderFn } from 'wagmi'; import { localhost, mainnet, sepolia } from 'wagmi/chains'; @@ -33,7 +33,13 @@ export const wagmiConfig = createConfig({ autoConnect: true, connectors: [ ...w3mConnectors({ chains: CHAINS, projectId: PROJECT_ID }), - // TODO Readd LedgerConnector. + new LedgerConnector({ + chains: CHAINS, + options: { + projectId: PROJECT_ID, + }, + // unknown typing conflict. + }) as any, ], publicClient, }); diff --git a/client/src/components/Allocation/AllocationInputs/AllocationInputs.tsx b/client/src/components/Allocation/AllocationInputs/AllocationInputs.tsx index 53ca43ca9f..e7f460ecd6 100644 --- a/client/src/components/Allocation/AllocationInputs/AllocationInputs.tsx +++ b/client/src/components/Allocation/AllocationInputs/AllocationInputs.tsx @@ -83,7 +83,6 @@ const AllocationInputs: FC = ({
= ({ /> = ({ value={percentage} />
{titleSuffix} {isExpandable && ( diff --git a/client/src/components/ui/Modal/Modal.tsx b/client/src/components/ui/Modal/Modal.tsx index 31a1a97bb9..45d342d779 100644 --- a/client/src/components/ui/Modal/Modal.tsx +++ b/client/src/components/ui/Modal/Modal.tsx @@ -125,7 +125,6 @@ const Modal: FC = ({ = ({ value, onUnlock, hideThumb, - dataTest = 'Slider', ...rest }) => { const reactSliderRef = useRef(null); @@ -51,31 +50,19 @@ const Slider: FC = ({ reactSliderRef.current.handleResize(); }, [isDisabled, hideThumb]); - useLayoutEffect(() => { - if (!reactSliderRef?.current) { - return; - } - // @ts-expect-error method isn't typed - reactSliderRef.current.slider.setAttribute('data-test', dataTest); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return (
( // Hiding the thumb by returning null here prevent ReactSlider from setting value on initial render. -
+
)} - renderTrack={(props, { index }) => ( -
- )} thumbClassName={cx(styles.thumb, (isDisabled || hideThumb) && styles.isHidden)} trackClassName={cx( styles.track, diff --git a/client/src/components/ui/Slider/types.ts b/client/src/components/ui/Slider/types.ts index d516969dca..c13115feda 100644 --- a/client/src/components/ui/Slider/types.ts +++ b/client/src/components/ui/Slider/types.ts @@ -2,7 +2,6 @@ import { ReactSliderProps } from 'react-slider'; export default interface SliderProps extends ReactSliderProps { className?: string; - dataTest?: string; hideThumb?: boolean; isDisabled?: boolean; isError: boolean; diff --git a/client/src/hooks/mutations/useCypressMoveToDecisionWindowClosed.ts b/client/src/hooks/mutations/useCypressMoveToDecisionWindowClosed.ts index 620221a451..5b79e6ca93 100644 --- a/client/src/hooks/mutations/useCypressMoveToDecisionWindowClosed.ts +++ b/client/src/hooks/mutations/useCypressMoveToDecisionWindowClosed.ts @@ -4,7 +4,7 @@ import { useConfig } from 'wagmi'; import { QUERY_KEYS } from 'api/queryKeys'; import { readContractEpochs } from 'hooks/contracts/readContracts'; -export default function useCypressMoveEpoch(): UseMutationResult { +export default function useCypressMoveEpoch(): UseMutationResult { const queryClient = useQueryClient(); const wagmiConfig = useConfig(); @@ -77,8 +77,17 @@ export default function useCypressMoveEpoch(): UseMutationResult + readContractEpochs({ + functionName: 'isDecisionWindowOpen', + publicClient: wagmiConfig.publicClient, + }), + queryKey: QUERY_KEYS.isDecisionWindowOpen, + }); + // isEpochChanged - resolve(timeToIncrease); + resolve(isDecisionWindowOpenAfter === false); }); }, }); diff --git a/client/src/hooks/mutations/useCypressMoveToDecisionWindowOpen.ts b/client/src/hooks/mutations/useCypressMoveToDecisionWindowOpen.ts index dba175e89f..5099f17ff1 100644 --- a/client/src/hooks/mutations/useCypressMoveToDecisionWindowOpen.ts +++ b/client/src/hooks/mutations/useCypressMoveToDecisionWindowOpen.ts @@ -4,7 +4,7 @@ import { useConfig } from 'wagmi'; import { QUERY_KEYS } from 'api/queryKeys'; import { readContractEpochs } from 'hooks/contracts/readContracts'; -export default function useCypressMoveToDecisionWindowOpen(): UseMutationResult { +export default function useCypressMoveToDecisionWindowOpen(): UseMutationResult { const queryClient = useQueryClient(); const wagmiConfig = useConfig(); @@ -61,7 +61,17 @@ export default function useCypressMoveToDecisionWindowOpen(): UseMutationResult< }); await wagmiConfig.publicClient.request({ method: 'evm_mine' as any, params: [] as any }); - resolve(timeToIncrease); + const currentEpochAfter = await queryClient.fetchQuery({ + queryFn: () => + readContractEpochs({ + functionName: 'getCurrentEpoch', + publicClient: wagmiConfig.publicClient, + }), + queryKey: QUERY_KEYS.currentEpoch, + }); + + // isEpochChanged + resolve(Number(currentEpoch) + 1 === Number(currentEpochAfter)); }); }, }); diff --git a/client/src/routes/RootRoutes/RootRoutes.tsx b/client/src/routes/RootRoutes/RootRoutes.tsx index c1e35b1a6e..83689c19b9 100644 --- a/client/src/routes/RootRoutes/RootRoutes.tsx +++ b/client/src/routes/RootRoutes/RootRoutes.tsx @@ -87,7 +87,7 @@ const RootRoutes: FC = props => { } path={`${ROOT_ROUTES.earn.relative}/*`} /> - {(window.Cypress || env.network === 'Local' || true) && ( + {(window.Cypress || env.network === 'Local') && ( diff --git a/client/src/sentry.ts b/client/src/sentry.ts index c8cc190669..de6e41dcbc 100644 --- a/client/src/sentry.ts +++ b/client/src/sentry.ts @@ -1,19 +1,15 @@ // eslint-disable-next-line no-restricted-syntax import * as Sentry from '@sentry/react'; -// import env from 'env'; +import env from 'env'; Sentry.init({ dsn: 'https://207392fdfee5b8348be75b27b53c0e82@o4506778713194496.ingest.sentry.io/4506778715881472', - // TODO OCT-1525 enable browserTracingIntegration or remove this TODO. - // integrations: [Sentry.browserTracingIntegration()], + integrations: [Sentry.browserTracingIntegration()], // Capture 100% of the transactions // Set 'tracePropagationTargets' to control for which URLs distributed tracing should be enabled - // tracePropagationTargets: [ - // env.serverEndpoint, - // ...env.ipfsGateways.split(','), - // ], + tracePropagationTargets: [env.serverEndpoint, ...env.ipfsGateways.split(',')], // Performance Monitoring - // tracesSampleRate: 1.0, + tracesSampleRate: 1.0, // Session Replay }); diff --git a/client/src/views/PlaygroundView/PlaygroundView.tsx b/client/src/views/PlaygroundView/PlaygroundView.tsx index 226d3eb8d4..f3f94536ea 100644 --- a/client/src/views/PlaygroundView/PlaygroundView.tsx +++ b/client/src/views/PlaygroundView/PlaygroundView.tsx @@ -1,49 +1,5 @@ -import React, { ReactElement, useEffect, useState } from 'react'; -import { Trans } from 'react-i18next'; +import React, { ReactElement } from 'react'; -import Layout from 'components/shared/Layout'; -import useEpochAndAllocationTimestamps from 'hooks/helpers/useEpochAndAllocationTimestamps'; -import useIsDecisionWindowOpen from 'hooks/queries/useIsDecisionWindowOpen'; -import getTimeDistance from 'utils/getTimeDistance'; - -const PlaygroundView = (): ReactElement => { - const { timeCurrentAllocationEnd, timeCurrentEpochEnd } = useEpochAndAllocationTimestamps(); - const { data: isDecisionWindowOpen } = useIsDecisionWindowOpen(); - const getCurrentPeriod = () => { - if (isDecisionWindowOpen && timeCurrentAllocationEnd) { - return getTimeDistance(Date.now(), new Date(timeCurrentAllocationEnd).getTime()); - } - if (!isDecisionWindowOpen && timeCurrentEpochEnd) { - return getTimeDistance(Date.now(), new Date(timeCurrentEpochEnd).getTime()); - } - return ''; - }; - const [currentPeriod, setCurrentPeriod] = useState(() => getCurrentPeriod()); - - useEffect(() => { - const intervalId = setInterval(() => { - setCurrentPeriod(getCurrentPeriod()); - }, 1000); - - return () => clearInterval(intervalId); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isDecisionWindowOpen, timeCurrentAllocationEnd, timeCurrentEpochEnd]); - - return ( - -
- ]} - i18nKey={ - isDecisionWindowOpen - ? 'layouts.main.allocationEndsIn' - : 'layouts.main.allocationStartsIn' - } - values={{ currentPeriod }} - /> -
-
- ); -}; +const PlaygroundView = (): ReactElement =>
Playground
; export default PlaygroundView; diff --git a/client/src/views/SyncView/SyncView.tsx b/client/src/views/SyncView/SyncView.tsx index 0a53734af4..60b5eb3069 100644 --- a/client/src/views/SyncView/SyncView.tsx +++ b/client/src/views/SyncView/SyncView.tsx @@ -4,7 +4,6 @@ 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 useEpochs from 'hooks/subgraph/useEpochs'; import { octantSemiTransparent } from 'svg/logo'; @@ -13,12 +12,10 @@ import styles from './SyncView.module.scss'; const SyncView = (): ReactElement => { const { data: epochs } = useEpochs(); const { data: currentEpoch } = useCurrentEpoch(); - const { data: isDecisionWindowOpen } = useIsDecisionWindowOpen(); return (
{epochs}
{currentEpoch}
-
{isDecisionWindowOpen}