diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml index faf2cbb86..963405dfc 100644 --- a/.github/workflows/test_backend.yml +++ b/.github/workflows/test_backend.yml @@ -33,12 +33,26 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt + if [[ "${{ env.NETWORK }}" == "preprod" ]]; then + export FAUCET_API_KEY="${{ secrets.FAUCET_API_KEY_PREPROD }}" + elif [[ "${{ env.NETWORK }}" == "sanchonet" ]]; then + export FAUCET_API_KEY="${{ secrets.FAUCET_API_KEY_SANCHONET }}" + else + export FAUCET_API_KEY="${{ secrets.FAUCET_API_KEY_PREVIEW }}" + fi python ./setup.py python -m pytest --alluredir allure-results + if [[ "${{ env.NETWORK }}" == "preprod" ]]; then + echo "FAUCET_API_KEY=${{ secrets.FAUCET_API_KEY_PREPROD }}" >> $GITHUB_ENV + elif [[ "${{ env.NETWORK }}" == "sanchonet" ]]; then + echo "FAUCET_API_KEY=${{ secrets.FAUCET_API_KEY_SANCHONET }}" >> $GITHUB_ENV + else + echo "FAUCET_API_KEY=${{ secrets.FAUCET_API_KEY_PREVIEW }}" >> $GITHUB_ENV + fi env: BASE_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io/api' }} - FAUCET_API_KEY: ${{ secrets.FAUCET_API_KEY }} - KUBER_API_URL: https://kuber-govtool.cardanoapi.io + NETWORK: ${{ vars.NETWORK }} + KUBER_API_KEY: ${{ secrets.KUBER_API_KEY }} - name: Upload report if: always() diff --git a/.github/workflows/test_integration_playwright.yml b/.github/workflows/test_integration_playwright.yml index e6a3ff358..3f3c5fab1 100644 --- a/.github/workflows/test_integration_playwright.yml +++ b/.github/workflows/test_integration_playwright.yml @@ -54,7 +54,18 @@ jobs: mkdir -p ./lib/_mock chmod +w ./lib/_mock npm run generate-wallets + if [[ "${{ env.NETWORK }}" == "preprod" ]]; then + export FAUCET_API_KEY="${{ secrets.FAUCET_API_KEY_PREPROD }}" + export BLOCKFROST_API_KEY="${{ secrets.BLOCKFROST_API_KEY_PREPROD }}" + elif [[ "${{ env.NETWORK }}" == "sanchonet" ]]; then + export FAUCET_API_KEY="${{ secrets.FAUCET_API_KEY_SANCHONET }}" + export BLOCKFROST_API_KEY="${{ secrets.BLOCKFROST_API_KEY_SANCHONET }}" + else + export FAUCET_API_KEY="${{ secrets.FAUCET_API_KEY_PREVIEW }}" + export BLOCKFROST_API_KEY="${{ secrets.BLOCKFROST_API_KEY_PREVIEW }}" + fi npm test + - name: Upload report uses: actions/upload-artifact@v3 @@ -74,9 +85,6 @@ jobs: HOST_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }} API_URL: https://${{inputs.deployment || 'govtool.cardanoapi.io' }}/api DOCS_URL: ${{ vars.DOCS_URL }} - FAUCET_API_URL: ${{ vars.FAUCET_API_URL }} - FAUCET_API_KEY: ${{secrets.FAUCET_API_KEY}} - KUBER_API_URL: ${{vars.KUBER_API_URL}} KUBER_API_KEY: ${{secrets.KUBER_API_KEY}} TEST_WORKERS: ${{vars.TEST_WORKERS}} CI: ${{vars.CI}} diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b3ae685..f956c1c23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,35 @@ changes. ### Fixed +- + +### Changed + +- + +### Removed + +- + +## [v1.0.29](https://github.com/IntersectMBO/govtool/releases/tag/v1.0.29) 2024-12-05 + +### Added + +- Add support for displaying CIP-119 DRep images [Issue 1806](https://github.com/IntersectMBO/govtool/issues/1806) + +### Fixed + - Fix fetching voting power of newly registerd DRep [Issue 2407](https://github.com/IntersectMBO/govtool/issues/2407) - Fix inconsistent voting status [Issue 1713](https://github.com/IntersectMBO/govtool/issues/1713) +- Fix removing a child (link) when is not registed in DOM [Issue 2398](https://github.com/IntersectMBO/govtool/issues/2398) +- Fix blank screen on DRep delegation when UTXos are missing [Issue 2408](https://github.com/IntersectMBO/govtool/issues/2408) +- Fix broken guides links [Issue 2417](https://github.com/IntersectMBO/govtool/issues/2417) ### Changed -- +- Bump cardano-db-sync to 13.6.0.4 [Issue 2411](https://github.com/IntersectMBO/govtool/issues/2411) +- Bump @intersect.mbo/pdf-ui to v0.5.3 +- Bump @intersect.mbo/pdf-ui to v0.5.4 ### Removed diff --git a/README.md b/README.md index db07e081c..27388fa33 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The Cardano GovTool enables ada holders to experience the governance features de ### Documentation -Learn more; [docs.gov.tools](https://docs.gov.tools/). +Learn more; [docs.gov.tools](https://docs.gov.tools/cardano-govtool/using-govtool). ## 📍 Navigation diff --git a/docs/architecture/README.md b/docs/architecture/README.md index 66689da9c..8a74d4f8a 100644 --- a/docs/architecture/README.md +++ b/docs/architecture/README.md @@ -23,7 +23,7 @@ utility backend that serves validation of the governance actions metadata. # Description -Frontend is a React application using Vite as a built tool to enhance development speed and optimize production builds. Frontend interacts with the backend service via REST API and with the Cardano blockchain via cardano-serialization-lib and connected supported wallets (for the list of compatible wallets go [here](https://docs.gov.tools/how-to-use-the-govtool/getting-started/get-a-compatible-wallet)). +Frontend is a React application using Vite as a built tool to enhance development speed and optimize production builds. Frontend interacts with the backend service via REST API and with the Cardano blockchain via cardano-serialization-lib and connected supported wallets (for the list of compatible wallets go [here](https://docs.gov.tools/cardano-govtool/using-govtool/getting-started/compatible-wallets)). # Components diff --git a/govtool/backend/Dockerfile b/govtool/backend/Dockerfile index 9c1fabd7f..94b48a425 100644 --- a/govtool/backend/Dockerfile +++ b/govtool/backend/Dockerfile @@ -4,4 +4,4 @@ FROM $BASE_IMAGE_REPO:$BASE_IMAGE_TAG WORKDIR /src COPY . . RUN cabal build -RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-1.0.28/x/vva-be/build/vva-be/vva-be /usr/local/bin +RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-1.0.29/x/vva-be/build/vva-be/vva-be /usr/local/bin diff --git a/govtool/backend/Dockerfile.qovery b/govtool/backend/Dockerfile.qovery index f7e8fd8ba..53e825921 100644 --- a/govtool/backend/Dockerfile.qovery +++ b/govtool/backend/Dockerfile.qovery @@ -4,7 +4,7 @@ FROM $BASE_IMAGE_REPO:$BASE_IMAGE_TAG WORKDIR /src COPY . . RUN cabal build -RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-1.0.28/x/vva-be/build/vva-be/vva-be /usr/local/bin +RUN cp dist-newstyle/build/x86_64-linux/ghc-9.2.7/vva-be-1.0.29/x/vva-be/build/vva-be/vva-be /usr/local/bin # Expose the necessary port EXPOSE 9876 diff --git a/govtool/backend/app/Main.hs b/govtool/backend/app/Main.hs index 91a743025..e0dd5d3b8 100644 --- a/govtool/backend/app/Main.hs +++ b/govtool/backend/app/Main.hs @@ -53,7 +53,7 @@ import System.Clock (TimeSpec (TimeSpec)) import System.IO (stderr) import System.Log.Raven (initRaven, register, silentFallback) import System.Log.Raven.Transport.HttpConduit (sendRecord) -import System.Log.Raven.Types (SentryLevel (Error), SentryRecord (..)) +import System.Log.Raven.Types (SentryLevel (Error), SentryRecord (..), SentryService) import System.TimeManager (TimeoutThread (..)) import VVA.API @@ -71,12 +71,18 @@ main :: IO () main = do commandLineConfig <- execParser cmdParser vvaConfig <- loadVVAConfig (clcConfigPath commandLineConfig) + sentryService <- + initRaven + (sentryDSN vvaConfig) + id + sendRecord + silentFallback case clcCommand commandLineConfig of - StartApp -> startApp vvaConfig + StartApp -> startApp vvaConfig sentryService ShowConfig -> Text.putStrLn $ vvaConfigToText vvaConfig -startApp :: VVAConfig -> IO () -startApp vvaConfig = do +startApp :: VVAConfig -> SentryService -> IO () +startApp vvaConfig sentryService = do let vvaPort = serverPort vvaConfig vvaHost = fromString (Text.unpack (serverHost vvaConfig)) settings = @@ -92,7 +98,7 @@ startApp vvaConfig = do ++ show vvaPort ) ) - $ setOnException (exceptionHandler vvaConfig) defaultSettings + $ setOnException (exceptionHandler vvaConfig sentryService) defaultSettings cacheEnv <- do let newCache = Cache.newCache (Just $ TimeSpec (fromIntegral (cacheDurationSeconds vvaConfig)) 0) proposalListCache <- newCache @@ -122,8 +128,8 @@ startApp vvaConfig = do server' <- mkVVAServer appEnv runSettings settings server' -exceptionHandler :: VVAConfig -> Maybe Request -> SomeException -> IO () -exceptionHandler vvaConfig mRequest exception = do +exceptionHandler :: VVAConfig -> SentryService -> Maybe Request -> SomeException -> IO () +exceptionHandler vvaConfig sentryService mRequest exception = do print mRequest print exception let isNotTimeoutThread x = case fromException x of @@ -135,12 +141,6 @@ exceptionHandler vvaConfig mRequest exception = do guard . isNotTimeoutThread $ exception guard . isNotConnectionClosedByPeer $ exception let env = sentryEnv vvaConfig - sentryService <- - initRaven - (sentryDSN vvaConfig) - id - sendRecord - silentFallback register sentryService "vva.be" diff --git a/govtool/backend/vva-be.cabal b/govtool/backend/vva-be.cabal index 14c7fcb34..bfc746013 100644 --- a/govtool/backend/vva-be.cabal +++ b/govtool/backend/vva-be.cabal @@ -1,6 +1,6 @@ cabal-version: 3.6 name: vva-be -version: 1.0.28 +version: 1.0.29 -- A short (one-line) description of the package. -- synopsis: diff --git a/govtool/frontend/.env.example b/govtool/frontend/.env.example index 5fbb1a97f..2489d65a0 100644 --- a/govtool/frontend/.env.example +++ b/govtool/frontend/.env.example @@ -8,3 +8,5 @@ VITE_IS_DEV=true VITE_USERSNAP_SPACE_API_KEY="" VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED='true' VITE_PDF_API_URL="" +VITE_IPFS_GATEWAY="" +VITE_IPFS_PROJECT_ID="" \ No newline at end of file diff --git a/govtool/frontend/Dockerfile b/govtool/frontend/Dockerfile index 5186b390e..9a12dba06 100644 --- a/govtool/frontend/Dockerfile +++ b/govtool/frontend/Dockerfile @@ -10,6 +10,8 @@ ARG NPMRC_TOKEN ARG VITE_USERSNAP_SPACE_API_KEY ARG VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED='true' ARG VITE_PDF_API_URL +ARG VITE_IPFS_GATEWAY +ARG VITE_IPFS_PROJECT_ID # Ensure all required build arguments are set RUN \ @@ -21,7 +23,9 @@ RUN \ : "${VITE_SENTRY_DSN:?Build argument VITE_SENTRY_DSN is not set}" && \ : "${NPMRC_TOKEN:?Build argument NPMRC_TOKEN is not set}" && \ : "${VITE_USERSNAP_SPACE_API_KEY:?Build argument VITE_USERSNAP_SPACE_API_KEY is not set}" && \ - : "${VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED:?Build argument VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED is not set}" + : "${VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED:?Build argument VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED is not set}" && \ + : "${VITE_IPFS_GATEWAY:?Build argument VITE_IPFS_GATEWAY is not set}" && \ + : "${VITE_IPFS_PROJECT_ID:?Build argument VITE_IPFS_PROJECT_ID is not set}" ENV NODE_OPTIONS=--max_old_space_size=8192 diff --git a/govtool/frontend/Dockerfile.qovery b/govtool/frontend/Dockerfile.qovery index ddc6c1288..f9b562ade 100644 --- a/govtool/frontend/Dockerfile.qovery +++ b/govtool/frontend/Dockerfile.qovery @@ -10,6 +10,8 @@ ARG NPMRC_TOKEN ARG VITE_USERSNAP_SPACE_API_KEY ARG VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED='true' ARG VITE_PDF_API_URL +ARG VITE_IPFS_GATEWAY +ARG VITE_IPFS_PROJECT_ID ENV NODE_OPTIONS=--max_old_space_size=8192 diff --git a/govtool/frontend/package-lock.json b/govtool/frontend/package-lock.json index 200ada00e..a0b5f3816 100644 --- a/govtool/frontend/package-lock.json +++ b/govtool/frontend/package-lock.json @@ -14,7 +14,7 @@ "@emurgo/cardano-serialization-lib-asmjs": "^13.1.0", "@hookform/resolvers": "^3.3.1", "@intersect.mbo/intersectmbo.org-icons-set": "^1.0.8", - "@intersect.mbo/pdf-ui": "^0.5.2", + "@intersect.mbo/pdf-ui": "^0.5.4", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.4", "@rollup/plugin-babel": "^6.0.4", @@ -3513,9 +3513,9 @@ "license": "ISC" }, "node_modules/@intersect.mbo/pdf-ui": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@intersect.mbo/pdf-ui/-/pdf-ui-0.5.2.tgz", - "integrity": "sha512-zKPL3p+rve8GKDlDd9EozvVs/TDnCP6dDOLySeNndaiXaRw32QBdAOt5pSHaE0NGymKahlVV2QwO2vFNiawopA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@intersect.mbo/pdf-ui/-/pdf-ui-0.5.4.tgz", + "integrity": "sha512-QKaI+0C52/YAagkrRKzQQ/X2DwOHPovappEgeUXmVecvLzbVoJpKY9zGo5sNzmdu5qkYnE5/yIWbHZGVPpbvBA==", "dependencies": { "@emurgo/cardano-serialization-lib-asmjs": "^12.0.0-beta.2", "@fontsource/poppins": "^5.0.14", diff --git a/govtool/frontend/package.json b/govtool/frontend/package.json index 3144989d1..3df53b084 100644 --- a/govtool/frontend/package.json +++ b/govtool/frontend/package.json @@ -1,7 +1,7 @@ { "name": "@govtool/frontend", "private": true, - "version": "1.0.28", + "version": "1.0.29", "type": "module", "scripts": { "build": "vite build", @@ -28,7 +28,7 @@ "@emurgo/cardano-serialization-lib-asmjs": "^13.1.0", "@hookform/resolvers": "^3.3.1", "@intersect.mbo/intersectmbo.org-icons-set": "^1.0.8", - "@intersect.mbo/pdf-ui": "^0.5.2", + "@intersect.mbo/pdf-ui": "^0.5.4", "@mui/icons-material": "^5.14.3", "@mui/material": "^5.14.4", "@rollup/plugin-babel": "^6.0.4", @@ -108,5 +108,5 @@ "overrides": { "typescript": "^5.0.2" }, - "_id": "govtool@1.0.28" + "_id": "govtool@1.0.29" } diff --git a/govtool/frontend/public/icons/DefaultDRep.svg b/govtool/frontend/public/icons/DefaultDRep.svg new file mode 100644 index 000000000..00557579b --- /dev/null +++ b/govtool/frontend/public/icons/DefaultDRep.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/govtool/frontend/src/components/molecules/DataMissingHeader.tsx b/govtool/frontend/src/components/molecules/DataMissingHeader.tsx index 6267d8742..ef6e278b4 100644 --- a/govtool/frontend/src/components/molecules/DataMissingHeader.tsx +++ b/govtool/frontend/src/components/molecules/DataMissingHeader.tsx @@ -1,19 +1,24 @@ -import { Box, SxProps } from "@mui/material"; +import { Avatar, Box, SxProps } from "@mui/material"; import { Typography } from "@atoms"; import { MetadataValidationStatus } from "@models"; import { getMetadataDataMissingStatusTranslation } from "@/utils"; +import { ICONS } from "@/consts"; type DataMissingHeaderProps = { isDataMissing: MetadataValidationStatus | null; title?: string; titleStyle?: SxProps; + isDRep?: boolean; + image?: string | null; }; export const DataMissingHeader = ({ title, isDataMissing, titleStyle, + isDRep, + image, }: DataMissingHeaderProps) => ( + {isDRep && ( + + )} - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/dreps/drep-error-conditions", - ) - } + onClick={() => openInNewTab(LINKS.DREP_ERROR_CONDITIONS)} sx={{ fontFamily: "Poppins", fontSize: "16px", diff --git a/govtool/frontend/src/components/organisms/AutomatedVotingOptions.tsx b/govtool/frontend/src/components/organisms/AutomatedVotingOptions.tsx index 4b84380e8..acfcf7c33 100644 --- a/govtool/frontend/src/components/organisms/AutomatedVotingOptions.tsx +++ b/govtool/frontend/src/components/organisms/AutomatedVotingOptions.tsx @@ -16,6 +16,7 @@ import { AutomatedVotingOptionCurrentDelegation, AutomatedVotingOptionDelegationId, } from "@/types/automatedVotingOptions"; +import { LINKS } from "@/consts/links"; type AutomatedVotingOptionsProps = { currentDelegation?: string | null; @@ -118,11 +119,7 @@ export const AutomatedVotingOptions = ({ onClickDelegate={() => delegate(AutomatedVotingOptionDelegationId.abstain) } - onClickInfo={() => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/delegating/abstain-from-every-vote", - ) - } + onClickInfo={() => openInNewTab(LINKS.ABSTAIN_FROM_EVERY_VOTE)} title={ isDelegatedToAbstain ? t("dRepDirectory.delegatedToAbstainTitle", { @@ -162,9 +159,7 @@ export const AutomatedVotingOptions = ({ delegate(AutomatedVotingOptionDelegationId.no_confidence) } onClickInfo={() => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/delegating/signal-no-confidence-on-every-vote", - ) + openInNewTab(LINKS.SIGNAL_NO_CONFIDENCE_ON_EVERY_VOTE) } title={ isDelegatedToNoConfidence diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx index 0252327d0..7deecc45d 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StorageInformation.tsx @@ -17,6 +17,7 @@ import { isValidURLLength, openInNewTab, } from "@utils"; +import { LINKS } from "@/consts/links"; type StorageInformationProps = { setStep: Dispatch>; @@ -39,9 +40,7 @@ export const StorageInformation = ({ setStep }: StorageInformationProps) => { const fileName = getValues("governance_action_type") as string; const openGuideAboutStoringInformation = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); const isActionButtonDisabled = !watch("storingURL") || !!errors.storingURL; diff --git a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx index 9beb1f9e1..1dc5da480 100644 --- a/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx +++ b/govtool/frontend/src/components/organisms/CreateGovernanceActionSteps/StoreDataInfo.tsx @@ -10,6 +10,7 @@ import { import { CenteredBoxBottomButtons } from "@molecules"; import { ControlledField } from "@organisms"; import { openInNewTab } from "@utils"; +import { LINKS } from "@/consts/links"; type StoreDataInfoProps = { setStep: Dispatch>; @@ -20,10 +21,7 @@ export const StoreDataInfo = ({ setStep }: StoreDataInfoProps) => { const { control, errors, watch } = useCreateGovernanceActionForm(); const { isMobile } = useScreenDimension(); - const openLink = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + const openLink = () => openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); const isContinueDisabled = !watch("storeData"); diff --git a/govtool/frontend/src/components/organisms/DRepCard.tsx b/govtool/frontend/src/components/organisms/DRepCard.tsx index d63d0b859..c396f8f46 100644 --- a/govtool/frontend/src/components/organisms/DRepCard.tsx +++ b/govtool/frontend/src/components/organisms/DRepCard.tsx @@ -1,5 +1,5 @@ import { useNavigate } from "react-router-dom"; -import { Box, ButtonBase, Divider } from "@mui/material"; +import { Box, ButtonBase, Divider, Avatar } from "@mui/material"; import { Button, StatusPill, Typography } from "@atoms"; import { ICONS, PATHS } from "@consts"; @@ -24,7 +24,7 @@ type DRepCardProps = { }; export const DRepCard = ({ - dRep: { status, type, view, votingPower, givenName, metadataStatus }, + dRep: { status, type, view, votingPower, givenName, metadataStatus, image }, isConnected, isDelegationLoading, isInProgress, @@ -100,36 +100,47 @@ export const DRepCard = ({ containerType: "inline-size", }} > - - - {metadataStatus - ? getMetadataDataMissingStatusTranslation(metadataStatus) - : ellipsizeText(givenName ?? "", 25)} - - { - navigator.clipboard.writeText(view); - addSuccessAlert(t("alerts.copiedToClipboard")); - e.stopPropagation(); - }} - sx={{ - gap: 1, - width: "250px", - maxWidth: "100%", - "&:hover": { - opacity: 0.6, - transition: "opacity 0.3s", - }, - }} - > - - {view} + + + + + {metadataStatus + ? getMetadataDataMissingStatusTranslation(metadataStatus) + : ellipsizeText(givenName ?? "", 25)} - - + { + navigator.clipboard.writeText(view); + addSuccessAlert(t("alerts.copiedToClipboard")); + e.stopPropagation(); + }} + sx={{ + gap: 1, + width: "250px", + maxWidth: "100%", + "&:hover": { + opacity: 0.6, + transition: "opacity 0.3s", + }, + }} + > + + {view} + + + + diff --git a/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx b/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx index 77092b30f..ecbb61eba 100644 --- a/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx +++ b/govtool/frontend/src/components/organisms/DRepDetailsCardHeader.tsx @@ -32,7 +32,7 @@ export const DRepDetailsCardHeader = ({ const { votingPower: myVotingPower } = useGetAdaHolderVotingPowerQuery(stakeKey); - const { givenName, metadataStatus } = dRepData; + const { givenName, metadataStatus, image } = dRepData; const navigateToEditDRep = () => { navigate(PATHS.editDrepMetadata, { @@ -121,7 +121,9 @@ export const DRepDetailsCardHeader = ({ )} diff --git a/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx index b9c4338f7..bd9908734 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/DRepDashboardCard.tsx @@ -11,6 +11,7 @@ import { DashboardActionCardProps, } from "@molecules"; import { correctAdaFormat, openInNewTab } from "@utils"; +import { LINKS } from "@/consts/links"; type DRepDashboardCardProps = { dRepID: string; @@ -37,10 +38,7 @@ export const DRepDashboardCard = ({ const learnMoreButton = { children: t("learnMore"), dataTestId: "d-rep-learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/dreps/register-as-a-drep", - ), + onClick: () => openInNewTab(LINKS.REGISTER_AS_A_DREP), }; const navigateToDrepDirectory = () => { @@ -130,10 +128,7 @@ export const DRepDashboardCard = ({ ? { children: t("learnMore"), dataTestId: "register-learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/dreps/retire-as-a-drep", - ), + onClick: () => openInNewTab(LINKS.RETIRE_AS_A_DREP), } : learnMoreButton, ]; diff --git a/govtool/frontend/src/components/organisms/DashboardCards/DelegateDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/DelegateDashboardCard.tsx index 16459cd0b..b4a13d090 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/DelegateDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/DelegateDashboardCard.tsx @@ -20,6 +20,7 @@ import { AutomatedVotingOptionCurrentDelegation, AutomatedVotingOptionDelegationId, } from "@/types/automatedVotingOptions"; +import { LINKS } from "@/consts/links"; type DelegateDashboardCardProps = { currentDelegation: CurrentDelegation; @@ -45,10 +46,7 @@ export const DelegateDashboardCard = ({ const learnMoreButton = { children: t("learnMore"), dataTestId: "delegate-learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/how-to-use-the-govtool/using-govtool/delegating", - ), + onClick: () => openInNewTab(LINKS.DELEGATING), sx: { backgroundColor: "arcticWhite" }, }; diff --git a/govtool/frontend/src/components/organisms/DashboardCards/DirectVoterDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/DirectVoterDashboardCard.tsx index a32e9a2f9..386aca6d4 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/DirectVoterDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/DirectVoterDashboardCard.tsx @@ -8,6 +8,7 @@ import { useTranslation } from "@hooks"; import { VoterInfo } from "@models"; import { DashboardActionCard, DashboardActionCardProps } from "@molecules"; import { correctAdaFormat, openInNewTab } from "@utils"; +import { LINKS } from "@/consts/links"; type DirectVoterDashboardCardProps = { pendingTransaction: PendingTransaction; @@ -29,10 +30,7 @@ export const DirectVoterDashboardCard = ({ const learnMoreButton: LoadingButtonProps = { children: t("learnMore"), dataTestId: "direct-voter-learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/direct-voting", - ), + onClick: () => openInNewTab(LINKS.DIRECT_VOTING), }; const cardProps: Partial = (() => { @@ -96,10 +94,7 @@ export const DirectVoterDashboardCard = ({ { children: t("learnMore"), dataTestId: "learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/direct-voting", - ), + onClick: () => openInNewTab(LINKS.DIRECT_VOTING), }, ], description: ( diff --git a/govtool/frontend/src/components/organisms/DashboardCards/ListGovActionsDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/ListGovActionsDashboardCard.tsx index 00973c58d..eb59c26c5 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/ListGovActionsDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/ListGovActionsDashboardCard.tsx @@ -4,6 +4,7 @@ import { IMAGES, PATHS } from "@consts"; import { useTranslation } from "@hooks"; import { DashboardActionCard } from "@molecules"; import { openInNewTab } from "@utils"; +import { LINKS } from "@/consts/links"; export const ListGovActionsDashboardCards = () => { const navigate = useNavigate(); @@ -21,10 +22,7 @@ export const ListGovActionsDashboardCards = () => { { children: t("learnMore"), dataTestId: "list-gov-actions-learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/governance-actions/view-governance-actions", - ), + onClick: () => openInNewTab(LINKS.VIEW_GOVERNANCE_ACTIONS), variant: "outlined", }, ]} diff --git a/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx index 46191b137..165da2787 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx @@ -6,6 +6,7 @@ import { useTranslation } from "@hooks"; import { DashboardActionCard } from "@molecules"; import { openInNewTab } from "@utils"; import { useCallback } from "react"; +import { LINKS } from "@/consts/links"; type ProposeGovActionDashboardCardProps = { createGovActionTx: PendingTransaction["createGovAction"]; @@ -52,10 +53,7 @@ export const ProposeGovActionDashboardCard = ({ { children: t("learnMore"), dataTestId: "propose-gov-action-learn-more-button", - onClick: () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/governance-actions/propose-a-governance-action", - ), + onClick: () => openInNewTab(LINKS.PROPOSE_A_GOVERNANCE_ACTION), }, ]} description={t("dashboard.cards.proposeGovernanceAction.description")} diff --git a/govtool/frontend/src/components/organisms/DrawerMobile.tsx b/govtool/frontend/src/components/organisms/DrawerMobile.tsx index b2e107da9..dd48651a0 100644 --- a/govtool/frontend/src/components/organisms/DrawerMobile.tsx +++ b/govtool/frontend/src/components/organisms/DrawerMobile.tsx @@ -7,6 +7,7 @@ import { useFeatureFlag, useModal } from "@context"; import { openInNewTab } from "@utils"; import { DrawerMobileProps } from "./types"; +import { LINKS } from "@/consts/links"; const DRAWER_PADDING = 2; const CALCULATED_DRAWER_PADDING = DRAWER_PADDING * 8 * 2; @@ -21,10 +22,7 @@ export const DrawerMobile = ({ const { openModal } = useModal(); const { t } = useTranslation(); - const onClickHelp = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/support#for-support-you-can-reach-the-team-on-the-intersect-discord-server-at", - ); + const onClickHelp = () => openInNewTab(LINKS.SUPPORT); return ( >; @@ -35,9 +36,7 @@ export const EditDRepStorageInformation = ({ const fileName = getValues("givenName"); const openGuideAboutStoringInformation = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); const isActionButtonDisabled = !watch("storingURL") || !!errors.storingURL; diff --git a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx index e7fe54d7e..065e8e9e8 100644 --- a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx +++ b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepStoreDataInfo.tsx @@ -11,6 +11,7 @@ import { import { openInNewTab } from "@utils"; import { ControlledField } from ".."; +import { LINKS } from "@/consts/links"; export const EditDRepStoreDataInfo = ({ setStep, @@ -27,10 +28,7 @@ export const EditDRepStoreDataInfo = ({ const isContinueDisabled = !watch("storeData"); - const openLink = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + const openLink = () => openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); return ( <> diff --git a/govtool/frontend/src/components/organisms/Footer.tsx b/govtool/frontend/src/components/organisms/Footer.tsx index 79f037d8e..84b1e063d 100644 --- a/govtool/frontend/src/components/organisms/Footer.tsx +++ b/govtool/frontend/src/components/organisms/Footer.tsx @@ -5,6 +5,7 @@ import { ICONS } from "@consts"; import { useUsersnapApi } from "@context"; import { useScreenDimension, useTranslation } from "@hooks"; import { openInNewTab, testIdFromLabel } from "@utils"; +import { LINKS } from "@/consts/links"; type FooterLinkProps = { label: string; @@ -34,10 +35,7 @@ export const Footer = () => { const { t } = useTranslation(); const { openFeedbackWindow } = useUsersnapApi(); - const onClickHelp = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/support#for-support-you-can-reach-the-team-on-the-intersect-discord-server-at", - ); + const onClickHelp = () => openInNewTab(LINKS.SUPPORT); const onClickPrivacyPolicy = () => openInNewTab( diff --git a/govtool/frontend/src/components/organisms/HomeCards.tsx b/govtool/frontend/src/components/organisms/HomeCards.tsx index 59c97b252..8cc1e63f2 100644 --- a/govtool/frontend/src/components/organisms/HomeCards.tsx +++ b/govtool/frontend/src/components/organisms/HomeCards.tsx @@ -7,6 +7,7 @@ import { useFeatureFlag, useModal } from "@context"; import { ActionCard } from "@molecules"; import { useTranslation } from "@hooks"; import { openInNewTab } from "@utils"; +import { LINKS } from "@/consts/links"; export const HomeCards = () => { const { isProposalDiscussionForumEnabled } = useFeatureFlag(); @@ -26,25 +27,16 @@ export const HomeCards = () => { [openModal], ); - const onClickLearnMoreAboutDelegation = () => - openInNewTab( - "https://docs.gov.tools/how-to-use-the-govtool/using-govtool/delegating", - ); + const onClickLearnMoreAboutDelegation = () => openInNewTab(LINKS.DELEGATING); const onClickLearnMoreAboutDRepRegistration = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/dreps/register-as-a-drep", - ); + openInNewTab(LINKS.REGISTER_AS_A_DREP); const onClickLearnMoreAboutDirectVoterRegistration = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/direct-voting", - ); + openInNewTab(LINKS.DIRECT_VOTING); const onClickLearnMoreAboutProposingGovAction = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/governance-actions/governance-actions/propose-a-governance-action", - ); + openInNewTab(LINKS.PROPOSE_A_GOVERNANCE_ACTION); const navigateToGovActions = useCallback( () => navigate(PATHS.governanceActions), diff --git a/govtool/frontend/src/components/organisms/Modal/ChooseWalletModal.tsx b/govtool/frontend/src/components/organisms/Modal/ChooseWalletModal.tsx index 87a2ed712..16acd5cfa 100644 --- a/govtool/frontend/src/components/organisms/Modal/ChooseWalletModal.tsx +++ b/govtool/frontend/src/components/organisms/Modal/ChooseWalletModal.tsx @@ -8,6 +8,7 @@ import { WalletOptionButton } from "@molecules"; import { openInNewTab } from "@utils"; import { useTranslation } from "@hooks"; import { To } from "react-router-dom"; +import { LINKS } from "@/consts/links"; type ChooseWalletModalState = { pathToNavigate?: To; @@ -105,11 +106,7 @@ export const ChooseWalletModal = forwardRef((_, ref) => { - openInNewTab( - "https://docs.gov.tools/how-to-use-the-govtool/getting-started/get-a-compatible-wallet", - ) - } + onClick={() => openInNewTab(LINKS.COMPATIBLE_WALLETS)} sx={{ cursor: "pointer" }} > {t("here")} diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx index c36e73667..ab52f9882 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStorageInformation.tsx @@ -12,6 +12,7 @@ import { import { CenteredBoxBottomButtons, Step } from "@molecules"; import { ControlledField } from "@organisms"; import { openInNewTab, ellipsizeText } from "@utils"; +import { LINKS } from "@/consts/links"; type StorageInformationProps = { setStep: Dispatch>; @@ -36,9 +37,7 @@ export const DRepStorageInformation = ({ const fileName = getValues("givenName"); const openGuideAboutStoringInformation = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); const isActionButtonDisabled = !watch("storingURL") || !!errors.storingURL; diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx index a05696d7c..e8c31eff1 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/DRepStoreDataInfo.tsx @@ -11,6 +11,7 @@ import { import { openInNewTab } from "@utils"; import { ControlledField } from ".."; +import { LINKS } from "@/consts/links"; export const DRepStoreDataInfo = ({ setStep, @@ -27,10 +28,7 @@ export const DRepStoreDataInfo = ({ const onClickContinue = () => setStep(4); - const openLink = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + const openLink = () => openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); return ( <> diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx index a87883656..932bfe73f 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/RolesAndResponsibilities.tsx @@ -11,6 +11,7 @@ import { openInNewTab, PROTOCOL_PARAMS_KEY, } from "@utils"; +import { LINKS } from "@/consts/links"; export const RolesAndResponsibilities = ({ setStep, @@ -24,10 +25,7 @@ export const RolesAndResponsibilities = ({ const onClickContinue = () => setStep(2); - const openLearnMoreAboutDrep = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/dreps", - ); + const openLearnMoreAboutDrep = () => openInNewTab(LINKS.DREPS); return ( <> diff --git a/govtool/frontend/src/components/organisms/TopBanners.tsx b/govtool/frontend/src/components/organisms/TopBanners.tsx index 332a2c440..e27b8138d 100644 --- a/govtool/frontend/src/components/organisms/TopBanners.tsx +++ b/govtool/frontend/src/components/organisms/TopBanners.tsx @@ -1,6 +1,7 @@ import { Box, Link, Typography } from "@mui/material"; import { Trans, useTranslation } from "react-i18next"; import { useAppContext } from "@/context"; +import { LINKS } from "@/consts/links"; export const TopBanners = () => { const { isMainnet, networkName, isInBootstrapPhase, isAppInitializing } = @@ -60,7 +61,7 @@ export const TopBanners = () => { components={{ docs: ( >; @@ -35,9 +36,7 @@ export const VoteContextStoringInformation = ({ } = useVoteContextForm(setSavedHash, setStep, setErrorMessage); const openGuideAboutStoringInformation = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); const isContinueDisabled = !watch("storingURL"); diff --git a/govtool/frontend/src/components/organisms/VoteContext/VoteContextTerms.tsx b/govtool/frontend/src/components/organisms/VoteContext/VoteContextTerms.tsx index a98a948c8..d59d1af38 100644 --- a/govtool/frontend/src/components/organisms/VoteContext/VoteContextTerms.tsx +++ b/govtool/frontend/src/components/organisms/VoteContext/VoteContextTerms.tsx @@ -5,6 +5,7 @@ import { Spacer, Typography } from "@atoms"; import { useScreenDimension, useTranslation, useVoteContextForm } from "@hooks"; import { ControlledField, VoteContextWrapper } from "@organisms"; import { openInNewTab } from "@utils"; +import { LINKS } from "@/consts/links"; type StoreDataInfoProps = { setStep: Dispatch>; @@ -16,10 +17,7 @@ export const VoteContextTerms = ({ setStep, onCancel }: StoreDataInfoProps) => { const { control, errors, watch } = useVoteContextForm(); const { isMobile } = useScreenDimension(); - const openLink = () => - openInNewTab( - "https://docs.gov.tools/using-govtool/govtool-functions/storing-information-offline", - ); + const openLink = () => openInNewTab(LINKS.STORING_INFORMATION_OFFLINE); const isContinueDisabled = !watch("terms"); diff --git a/govtool/frontend/src/consts/icons.ts b/govtool/frontend/src/consts/icons.ts index bd70a2b06..4d8672516 100644 --- a/govtool/frontend/src/consts/icons.ts +++ b/govtool/frontend/src/consts/icons.ts @@ -13,6 +13,7 @@ export const ICONS = { copyWhiteIcon: "/icons/CopyWhite.svg", dashboardActiveIcon: "/icons/DashboardActive.svg", dashboardIcon: "/icons/Dashboard.svg", + defaultDRepIcon: "/icons/DefaultDRep.svg", download: "/icons/Download.svg", drawerIcon: "/icons/DrawerIcon.svg", dRepDirectoryActiveIcon: "/icons/DRepDirectoryActive.svg", diff --git a/govtool/frontend/src/consts/links.ts b/govtool/frontend/src/consts/links.ts new file mode 100644 index 000000000..c053c3001 --- /dev/null +++ b/govtool/frontend/src/consts/links.ts @@ -0,0 +1,29 @@ +export const LINKS = { + USING_GOVTOOL: "https://docs.gov.tools/cardano-govtool/using-govtool", + COMPATIBLE_WALLETS: + "https://docs.gov.tools/cardano-govtool/using-govtool/getting-started/compatible-wallets", + FAQS: "https://docs.gov.tools/cardano-govtool/faqs", + SUPPORT: "https://docs.gov.tools/cardano-govtool/support", + DREP_ERROR_CONDITIONS: + "https://docs.gov.tools/cardano-govtool/using-govtool/dreps/drep-error-conditions", + ABSTAIN_FROM_EVERY_VOTE: + "https://docs.gov.tools/cardano-govtool/using-govtool/delegating/abstain-from-every-vote", + SIGNAL_NO_CONFIDENCE_ON_EVERY_VOTE: + "https://docs.gov.tools/cardano-govtool/using-govtool/delegating/signal-no-confidence-on-every-vote", + REGISTER_AS_A_DREP: + "https://docs.gov.tools/cardano-govtool/using-govtool/dreps/register-as-a-drep", + PROPOSE_A_GOVERNANCE_ACTION: + "https://docs.gov.tools/cardano-govtool/using-govtool/governance-actions/propose-a-governance-action", + BOOTSTRAPPING_PHASE: + "https://docs.gov.tools/cardano-govtool/faqs/bootstrapping-phase", + STORING_INFORMATION_OFFLINE: + "https://docs.gov.tools/cardano-govtool/using-govtool/storing-information-offline", + DELEGATING: "https://docs.gov.tools/cardano-govtool/using-govtool/delegating", + DIRECT_VOTING: + "https://docs.gov.tools/cardano-govtool/using-govtool/direct-voting", + RETIRE_AS_A_DREP: + "https://docs.gov.tools/cardano-govtool/using-govtool/dreps/retire-as-a-drep", + VIEW_GOVERNANCE_ACTIONS: + "https://docs.gov.tools/cardano-govtool/using-govtool/governance-actions/view-governance-actions", + DREPS: "https://docs.gov.tools/cardano-govtool/using-govtool/dreps", +}; diff --git a/govtool/frontend/src/consts/navItems.tsx b/govtool/frontend/src/consts/navItems.tsx index c544fff52..74795d562 100644 --- a/govtool/frontend/src/consts/navItems.tsx +++ b/govtool/frontend/src/consts/navItems.tsx @@ -3,6 +3,7 @@ import i18n from "@/i18n"; import { theme } from "@/theme"; import { ICONS } from "./icons"; +import { LINKS } from "./links"; import { PATHS, PDF_PATHS } from "./paths"; export const NAV_ITEMS = [ @@ -33,13 +34,13 @@ export const NAV_ITEMS = [ dataTestId: "guides-link", navTo: "", label: i18n.t("menu.guides"), - newTabLink: "https://docs.gov.tools/", + newTabLink: LINKS.USING_GOVTOOL, }, { dataTestId: "faqs-link", navTo: "", label: i18n.t("menu.faqs"), - newTabLink: "https://docs.gov.tools/faqs", + newTabLink: LINKS.FAQS, }, ]; @@ -95,7 +96,7 @@ export const CONNECTED_NAV_ITEMS = [ navTo: "", activeIcon: ICONS.guidesActiveIcon, icon: ICONS.guidesIcon, - newTabLink: "https://docs.gov.tools/", + newTabLink: LINKS.USING_GOVTOOL, }, { dataTestId: "faqs-link", @@ -103,6 +104,6 @@ export const CONNECTED_NAV_ITEMS = [ navTo: "", activeIcon: ICONS.faqsActiveIcon, icon: ICONS.faqsIcon, - newTabLink: "https://docs.gov.tools/faqs", + newTabLink: LINKS.FAQS, }, ]; diff --git a/govtool/frontend/src/hooks/useDelegateToDrep.ts b/govtool/frontend/src/hooks/useDelegateToDrep.ts index 31509d14d..179e17457 100644 --- a/govtool/frontend/src/hooks/useDelegateToDrep.ts +++ b/govtool/frontend/src/hooks/useDelegateToDrep.ts @@ -52,8 +52,12 @@ export const useDelegateTodRep = () => { voter, }); } catch (error) { + console.error({ error }); openWalletErrorModal({ - error, + error: + typeof error === "string" + ? error + : (error as { message: string | null })?.message, dataTestId: "delegate-transaction-error-modal", }); Sentry.setTag("hook", "useDelegateTodRep"); diff --git a/govtool/frontend/src/models/api.ts b/govtool/frontend/src/models/api.ts index fff33a0c0..32ef7bcc5 100644 --- a/govtool/frontend/src/models/api.ts +++ b/govtool/frontend/src/models/api.ts @@ -144,6 +144,9 @@ export type DrepDataDTO = { url?: string; view: string; votingPower?: number; + imageUrl: string | null; + // either base64 for IPFS image or URL for regular image + image: string | null; }; export type DRepData = DrepDataDTO & { @@ -156,6 +159,9 @@ export type DRepData = DrepDataDTO & { doNotList: boolean; metadataStatus: MetadataValidationStatus | null; metadataValid: boolean; + imageUrl: string | null; + // either base64 for IPFS image or URL for regular image + image: string | null; }; export type Vote = "yes" | "no" | "abstain"; diff --git a/govtool/frontend/src/utils/jsonUtils.ts b/govtool/frontend/src/utils/jsonUtils.ts index 04f499ed9..c73856bb0 100644 --- a/govtool/frontend/src/utils/jsonUtils.ts +++ b/govtool/frontend/src/utils/jsonUtils.ts @@ -24,8 +24,6 @@ export const downloadJson = (json: NodeObject, fileName?: string) => { } else { link.click(); } - - document.body.removeChild(link); URL.revokeObjectURL(url); }; @@ -52,6 +50,5 @@ export const downloadTextFile = (text: string, fileName?: string) => { link.click(); } - document.body.removeChild(link); URL.revokeObjectURL(url); }; diff --git a/govtool/frontend/src/utils/mapDtoToDrep.ts b/govtool/frontend/src/utils/mapDtoToDrep.ts index 419973c14..4ab65d9fa 100644 --- a/govtool/frontend/src/utils/mapDtoToDrep.ts +++ b/govtool/frontend/src/utils/mapDtoToDrep.ts @@ -11,7 +11,7 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => { const emptyMetadata = { paymentAddress: null, givenName: "", - image: null, + imageUrl: null, objectives: null, motivations: null, qualifications: null, @@ -24,6 +24,33 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => { // DBSync contains wrong representation of DRep view for script based DReps const view = fixViewForScriptBasedDRep(dto.view, dto.isScriptBased); + // We need to prefetch the image, for the IPFS support + let base64Image = null; + const isIPFSImage = dto.imageUrl?.startsWith("ipfs://") || false; + if (dto.imageUrl) { + fetch( + isIPFSImage + ? `${process.env.VITE_IPFS_GATEWAY}/${dto.imageUrl?.slice(7)}` + : dto.imageUrl, + isIPFSImage + ? { + headers: { project_id: import.meta.env.VITE_IPFS_PROJECT_ID }, + } + : {}, + ) + .then(async (res) => { + const blob = await res.blob(); + const reader = new FileReader(); + reader.readAsDataURL(blob); + reader.onloadend = () => { + base64Image = reader.result; + }; + }) + .catch((e) => { + console.error("Fetching the DRep image failed, reason: ", e); + }); + } + if (dto.metadataHash && dto.url) { const validationResponse = await postValidate({ url: dto.url, @@ -36,6 +63,7 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => { ...validationResponse.metadata, metadataStatus: validationResponse.status || null, metadataValid: validationResponse.valid, + image: isIPFSImage ? base64Image : dto.imageUrl, view, }; } @@ -44,5 +72,6 @@ export const mapDtoToDrep = async (dto: DrepDataDTO): Promise => { ...dto, ...emptyMetadata, view, + image: isIPFSImage ? base64Image : dto.imageUrl, }; }; diff --git a/govtool/frontend/src/utils/tests/dRep.test.ts b/govtool/frontend/src/utils/tests/dRep.test.ts index 104bfe019..66f41fe63 100644 --- a/govtool/frontend/src/utils/tests/dRep.test.ts +++ b/govtool/frontend/src/utils/tests/dRep.test.ts @@ -24,6 +24,8 @@ const EXAMPLE_DREP: DRepData = { qualifications: null, doNotList: false, isScriptBased: false, + imageUrl: null, + image: null, }; describe("isSameDRep function", () => { diff --git a/govtool/frontend/yarn.lock b/govtool/frontend/yarn.lock index dd3772103..b78223cc9 100644 --- a/govtool/frontend/yarn.lock +++ b/govtool/frontend/yarn.lock @@ -1536,10 +1536,10 @@ resolved "https://registry.npmjs.org/@intersect.mbo/intersectmbo.org-icons-set/-/intersectmbo.org-icons-set-1.1.0.tgz" integrity sha512-sjKEtnK9eLYH/8kCD0YRQCms3byFA/tnSsei9NHTZbBYX9sBpeX6ErfR0sKYjOSxQOxl4FumX9D0X+vHIqxo8g== -"@intersect.mbo/pdf-ui@^0.5.2": - version "0.5.2" - resolved "https://registry.npmjs.org/@intersect.mbo/pdf-ui/-/pdf-ui-0.5.2.tgz" - integrity sha512-zKPL3p+rve8GKDlDd9EozvVs/TDnCP6dDOLySeNndaiXaRw32QBdAOt5pSHaE0NGymKahlVV2QwO2vFNiawopA== +"@intersect.mbo/pdf-ui@^0.5.4": + version "0.5.4" + resolved "https://registry.npmjs.org/@intersect.mbo/pdf-ui/-/pdf-ui-0.5.4.tgz" + integrity sha512-QKaI+0C52/YAagkrRKzQQ/X2DwOHPovappEgeUXmVecvLzbVoJpKY9zGo5sNzmdu5qkYnE5/yIWbHZGVPpbvBA== dependencies: "@emurgo/cardano-serialization-lib-asmjs" "^12.0.0-beta.2" "@fontsource/poppins" "^5.0.14" diff --git a/govtool/metadata-validation/package.json b/govtool/metadata-validation/package.json index 4e0da8c1e..5ada02dc8 100644 --- a/govtool/metadata-validation/package.json +++ b/govtool/metadata-validation/package.json @@ -1,6 +1,6 @@ { "name": "@govtool/metadata-validation", - "version": "1.0.28", + "version": "1.0.29", "description": "", "author": "", "private": true, diff --git a/govtool/metadata-validation/src/main.ts b/govtool/metadata-validation/src/main.ts index 12ac1fcb2..c975ebc86 100644 --- a/govtool/metadata-validation/src/main.ts +++ b/govtool/metadata-validation/src/main.ts @@ -13,7 +13,7 @@ async function bootstrap() { const config = new DocumentBuilder() .setTitle('Metadata Validation Tool') .setDescription('The Metadata Validation Tool API description') - .setVersion('1.0.28') + .setVersion('1.0.29') .build(); const document = SwaggerModule.createDocument(app, config); diff --git a/scripts/govtool/Makefile b/scripts/govtool/Makefile index 9a7e14f03..a4c9bb4cb 100644 --- a/scripts/govtool/Makefile +++ b/scripts/govtool/Makefile @@ -12,7 +12,7 @@ include config.mk # image tags cardano_node_image_tag := 10.1.2 -cardano_db_sync_image_tag := 13.6.0.1 +cardano_db_sync_image_tag := 13.6.0.4 .PHONY: all all: deploy-stack notify diff --git a/scripts/govtool/docker-compose.node+dbsync.yml b/scripts/govtool/docker-compose.node+dbsync.yml index e72f09cfb..c88b4e037 100644 --- a/scripts/govtool/docker-compose.node+dbsync.yml +++ b/scripts/govtool/docker-compose.node+dbsync.yml @@ -65,7 +65,7 @@ services: retries: 10 cardano-db-sync: - image: ghcr.io/intersectmbo/cardano-db-sync:13.6.0.1 + image: ghcr.io/intersectmbo/cardano-db-sync:13.6.0.4 environment: - NETWORK=sanchonet - POSTGRES_HOST=postgres diff --git a/scripts/govtool/frontend.mk b/scripts/govtool/frontend.mk index 64f5516e8..910cbd030 100644 --- a/scripts/govtool/frontend.mk +++ b/scripts/govtool/frontend.mk @@ -25,6 +25,8 @@ build-frontend: docker-login --build-arg VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED="$${IS_PROPOSAL_DISCUSSION_FORUM_ENABLED}" \ --build-arg NPMRC_TOKEN="$${NPMRC_TOKEN}" \ --build-arg VITE_PDF_API_URL="$${PDF_API_URL}" \ + --build-arg VITE_IPFS_GATEWAY="$${IPFS_GATEWAY}" \ + --build-arg VITE_IPFS_PROJECT_ID="$${IPFS_PROJECT_ID}" \ $(root_dir)/govtool/frontend .PHONY: push-frontend diff --git a/tests/govtool-backend/.env.example b/tests/govtool-backend/.env.example index 3bed52954..7e492cd11 100644 --- a/tests/govtool-backend/.env.example +++ b/tests/govtool-backend/.env.example @@ -5,4 +5,5 @@ METRICS_API_SECRET= `api_secret` # required for setup KUBER_API_URL = "https://kuber-govtool.cardanoapi.io" KUBER_API_KEY = "" # optional -FAUCET_API_KEY= """ \ No newline at end of file +FAUCET_API_KEY= """ +NETWORK= preview \ No newline at end of file diff --git a/tests/govtool-backend/config.py b/tests/govtool-backend/config.py index fb601c31b..e40af8b6d 100644 --- a/tests/govtool-backend/config.py +++ b/tests/govtool-backend/config.py @@ -11,5 +11,7 @@ RECORD_METRICS_API = os.getenv("RECORD_METRICS_API") METRICS_API_SECRET = os.getenv("METRICS_API_SECRET") -KUBER_API_URL = os.getenv("KUBER_API_URL") +KUBER_API_URL = f'https://{os.getenv("NETWORK","preview")}.kuber.cardanoapi.io' KUBER_API_KEY = os.getenv("KUBER_API_KEY") +FAUCET_API_URL = f'https://faucet.${os.getenv("NETWORK","preview")}.world.dev.cardano.org' +FACUET_API_KEY = os.getenv("FAUCET_API_KEY") diff --git a/tests/govtool-backend/lib/faucet_api.py b/tests/govtool-backend/lib/faucet_api.py index ed0150390..b6c63c9de 100644 --- a/tests/govtool-backend/lib/faucet_api.py +++ b/tests/govtool-backend/lib/faucet_api.py @@ -1,3 +1,4 @@ +from config import FAUCET_API_URL,FACUET_API_KEY import os from typing import TypedDict @@ -21,8 +22,8 @@ def __init__(self, api_key: str, base_url: str = "https://faucet.sanchonet.world @staticmethod def from_env(): - api_key = os.getenv("FAUCET_API_KEY") - base_url = os.getenv("FAUCET_API_URL", "https://faucet.sanchonet.world.dev.cardano.org") + api_key = FACUET_API_KEY + base_url = FAUCET_API_URL if not api_key: raise ValueError("FAUCET_API_KEY environment variable not set.") return CardanoFaucet(api_key, base_url) diff --git a/tests/govtool-backend/lib/kuber_api.py b/tests/govtool-backend/lib/kuber_api.py index d70fedca2..788701d12 100644 --- a/tests/govtool-backend/lib/kuber_api.py +++ b/tests/govtool-backend/lib/kuber_api.py @@ -1,4 +1,5 @@ import json +from config import KUBER_API_KEY,KUBER_API_URL import sys import time from typing import Any, Optional @@ -69,11 +70,12 @@ def wait_for_txout(self, txin: str, timeout=400, log: bool = False): @staticmethod def from_env() -> "KuberApi": - api_url = os.environ.get("KUBER_API_URL") + api_url = KUBER_API_URL + print(f"KUBER_API_URL: {api_url}") if api_url is not None: api_url = api_url[:-1] if api_url.endswith("/") else api_url print(f"KUBER_API_URL: {api_url}") - api_key = os.environ.get("KUBER_API_KEY") + api_key = KUBER_API_KEY return KuberApi(api_url, api_key) else: print("KUBER_API_URL environment variable is not set.", file=sys.stderr) diff --git a/tests/govtool-backend/models/TestData.py b/tests/govtool-backend/models/TestData.py index e4eb7e3ec..3f89f4149 100644 --- a/tests/govtool-backend/models/TestData.py +++ b/tests/govtool-backend/models/TestData.py @@ -18,13 +18,14 @@ class Proposal(TypedDict): txHash: str index: int type: str - details: Optional[dict] + details: Optional[dict] | Optional[list] expiryDate: str expiryEpochNo: int createdDate: str createdEpochNo: int url: str metadataHash: str + protocolParams: Optional[dict] title: Optional[str] abstract: Optional[str] motivation: Optional[str] @@ -38,6 +39,8 @@ class Proposal(TypedDict): poolYesVotes: int poolNoVotes: int poolAbstainVotes: int + prevGovActionIndex: Optional[int] + prevGovActionTxHash: Optional[str] class Drep(TypedDict): diff --git a/tests/govtool-frontend/playwright/.env.example b/tests/govtool-frontend/playwright/.env.example index 719692bbc..acd2110aa 100644 --- a/tests/govtool-frontend/playwright/.env.example +++ b/tests/govtool-frontend/playwright/.env.example @@ -10,11 +10,12 @@ PDF_URL=https://dev.api.pdf.gov.tools NETWORK_ID=0 # Faucet -FAUCET_API_URL=https://faucet.sanchonet.world.dev.cardano.org FAUCET_API_KEY= +#Blockfrost +BLOCKFROST_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXX + # Kuber -KUBER_API_URL=https://sanchonet.kuber.cardanoapi.io KUBER_API_KEY= # Transaction timeout diff --git a/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts b/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts index b71d3b382..88953c3fa 100644 --- a/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts +++ b/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts @@ -1,15 +1,15 @@ import environments from "./environments"; -export const DELEGATION_DOC_URL = `${environments.docsUrl}/about/what-is-cardano-govtool/govtool-functions/delegating`; -export const REGISTER_DREP_DOC_URL = `${environments.docsUrl}/using-govtool/govtool-functions/dreps/register-as-a-drep`; -export const DIRECT_VOTER_DOC_URL = `${environments.docsUrl}/using-govtool/govtool-functions/direct-voting`; -export const GOVERNANCE_ACTION_DOC_URL = `${environments.docsUrl}/using-govtool/govtool-functions/governance-actions/view-governance-actions`; -export const PROPOSE_GOVERNANCE_ACTION_DOC_URL = `${environments.docsUrl}/using-govtool/govtool-functions/governance-actions/propose-a-governance-action`; -export const ABSTAIN_VOTE_DOC_URL = `${environments.docsUrl}/using-govtool/govtool-functions/delegating/abstain-from-every-vote`; -export const SIGNAL_NO_CONFIDENCE_VOTE_DOC_URL = `${environments.docsUrl}/using-govtool/govtool-functions/delegating/signal-no-confidence-on-every-vote`; +export const DELEGATION_DOC_URL = `${environments.docsUrl}/using-govtool/delegating`; +export const REGISTER_DREP_DOC_URL = `${environments.docsUrl}/using-govtool/dreps/register-as-a-drep`; +export const DIRECT_VOTER_DOC_URL = `${environments.docsUrl}/using-govtool/direct-voting`; +export const GOVERNANCE_ACTION_DOC_URL = `${environments.docsUrl}/using-govtool/governance-actions/view-governance-actions`; +export const PROPOSE_GOVERNANCE_ACTION_DOC_URL = `${environments.docsUrl}/using-govtool/governance-actions/propose-a-governance-action`; +export const ABSTAIN_VOTE_DOC_URL = `${environments.docsUrl}/using-govtool/delegating/abstain-from-every-vote`; +export const SIGNAL_NO_CONFIDENCE_VOTE_DOC_URL = `${environments.docsUrl}/using-govtool/delegating/signal-no-confidence-on-every-vote`; export const FAQS_DOC_URL = `${environments.docsUrl}/faqs`; -export const GUIDES_DOC_URL = `${environments.docsUrl}`; +export const GUIDES_DOC_URL = `${environments.docsUrl}/using-govtool`; export const PRIVACY_POLICY = `https://docs.intersectmbo.org/legal/policies-and-conditions/privacy-policy`; export const TERMS_AND_CONDITIONS = `https://docs.intersectmbo.org/legal/policies-and-conditions/terms-of-use`; export const HELP_DOC_URL = `${environments.docsUrl}/support`; -export const BOOTSTRAP_DOC_URL = `${environments.docsUrl}/about/bootstrapping-phase`; +export const BOOTSTRAP_DOC_URL = `${environments.docsUrl}/faqs/bootstrapping-phase`; diff --git a/tests/govtool-frontend/playwright/lib/constants/environments.ts b/tests/govtool-frontend/playwright/lib/constants/environments.ts index 2982b74fa..13622c431 100644 --- a/tests/govtool-frontend/playwright/lib/constants/environments.ts +++ b/tests/govtool-frontend/playwright/lib/constants/environments.ts @@ -9,26 +9,21 @@ const NETWORK = process.env.NETWORK || "preview"; const environments = { frontendUrl: SERVER_HOST_URL, + blockfrostApiKey: process.env.BLOCKFROST_API_KEY, + blockfrostApiUrl: "https://cardano-" + NETWORK + ".blockfrost.io/api", apiUrl: `${SERVER_HOST_URL}/api`, - docsUrl: process.env.DOCS_URL || "https://docs.gov.tools", + docsUrl: process.env.DOCS_URL || "https://docs.gov.tools/cardano-govtool", pdfUrl: process.env.PDF_URL || "https://dev.api.pdf.gov.tools", networkId: parseInt(process.env.NETWORK_ID) || 0, faucet: { - apiUrl: - process.env.FAUCET_API_URL.replace("sanchonet", NETWORK) || - "https://faucet.sanchonet.world.dev.cardano.org".replace( - "sanchonet", - NETWORK - ), + apiUrl:`https://faucet.${NETWORK}.world.dev.cardano.org`, apiKey: process.env.FAUCET_API_KEY || "", address: process.env.FAUCET_ADDRESS || "addr_test1vz0ua2vyk7r4vufmpqh5v44awg8xff26hxlwyrt3uc67maqtql3kl", }, kuber: { - apiUrl: - process.env.KUBER_API_URL.replace("sanchonet", NETWORK) || - "https://sanchonet.kuber.cardanoapi.io".replace("sanchonet", NETWORK), + apiUrl: `https://${NETWORK}.kuber.cardanoapi.io`, apiKey: process.env.KUBER_API_KEY || "", }, txTimeOut: parseInt(process.env.TX_TIMEOUT) || 240000, diff --git a/tests/govtool-frontend/playwright/lib/fixtures/loadExtension.ts b/tests/govtool-frontend/playwright/lib/fixtures/loadExtension.ts index 41ed3e722..34c508d5b 100644 --- a/tests/govtool-frontend/playwright/lib/fixtures/loadExtension.ts +++ b/tests/govtool-frontend/playwright/lib/fixtures/loadExtension.ts @@ -20,6 +20,8 @@ export default async function loadDemosExtension( enableStakeSigning, kuberApiUrl: environments.kuber.apiUrl, kuberApiKey: environments.kuber.apiKey, + blockfrostApiKey: environments.blockfrostApiKey, + blockfrostApiUrl: environments.blockfrostApiUrl, }; await page.addInitScript( ({ walletConfig, supportedExtensions }) => { diff --git a/tests/govtool-frontend/playwright/lib/helpers/featureFlag.ts b/tests/govtool-frontend/playwright/lib/helpers/featureFlag.ts index 6169bfcf5..0a6f1576e 100644 --- a/tests/govtool-frontend/playwright/lib/helpers/featureFlag.ts +++ b/tests/govtool-frontend/playwright/lib/helpers/featureFlag.ts @@ -1,4 +1,4 @@ -import { GrovernanceActionType, IProposal } from "@types"; +import { GovernanceActionType, IProposal } from "@types"; import { isBootStrapingPhase } from "./cardano"; import { SECURITY_RELEVANT_PARAMS_MAP } from "@constants/index"; @@ -12,17 +12,17 @@ export const areDRepVoteTotalsDisplayed = async (proposal: IProposal) => { ); if (isInBootstrapPhase) { return !( - proposal.type === GrovernanceActionType.HardFork || - (proposal.type === GrovernanceActionType.ProtocolParameterChange && + proposal.type === GovernanceActionType.HardFork || + (proposal.type === GovernanceActionType.ProtocolParameterChange && !isSecurityGroup) ); } return ![ - GrovernanceActionType.NoConfidence, - GrovernanceActionType.NewCommittee, - GrovernanceActionType.UpdatetotheConstitution, - ].includes(proposal.type as GrovernanceActionType); + GovernanceActionType.NoConfidence, + GovernanceActionType.NewCommittee, + GovernanceActionType.UpdatetotheConstitution, + ].includes(proposal.type as GovernanceActionType); }; export const areSPOVoteTotalsDisplayed = async (proposal: IProposal) => { @@ -34,22 +34,22 @@ export const areSPOVoteTotalsDisplayed = async (proposal: IProposal) => { ] !== null ); if (isInBootstrapPhase) { - return proposal.type !== GrovernanceActionType.ProtocolParameterChange; + return proposal.type !== GovernanceActionType.ProtocolParameterChange; } return !( - proposal.type === GrovernanceActionType.UpdatetotheConstitution || - proposal.type === GrovernanceActionType.TreasuryWithdrawal || - (proposal.type === GrovernanceActionType.ProtocolParameterChange && + proposal.type === GovernanceActionType.UpdatetotheConstitution || + proposal.type === GovernanceActionType.TreasuryWithdrawal || + (proposal.type === GovernanceActionType.ProtocolParameterChange && !isSecurityGroup) ); }; -export const areCCVoteTotalsDisplayed = async ( - governanceActionType: GrovernanceActionType +export const areCCVoteTotalsDisplayed = ( + governanceActionType: GovernanceActionType ) => { return ![ - GrovernanceActionType.NoConfidence, - GrovernanceActionType.NewCommittee, + GovernanceActionType.NoConfidence, + GovernanceActionType.NewCommittee, ].includes(governanceActionType); }; diff --git a/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts b/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts index e12e92e07..1bbb69179 100644 --- a/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts @@ -1,6 +1,10 @@ import removeAllSpaces from "@helpers/removeAllSpaces"; import { Locator, Page, expect } from "@playwright/test"; -import { GrovernanceActionType, IProposal } from "@types"; +import { + FullGovernanceDRepVoteActionsType, + GovernanceActionType, + IProposal, +} from "@types"; import environments from "lib/constants/environments"; import GovernanceActionDetailsPage from "./governanceActionDetailsPage"; import { getEnumKeyByValue } from "@helpers/enum"; @@ -47,8 +51,22 @@ export default class GovernanceActionsPage { return new GovernanceActionDetailsPage(this.page); } + async viewFirstDRepVoteEnabledGovernanceAction(): Promise { + for (const governanceAction of Object.keys( + FullGovernanceDRepVoteActionsType + )) { + const result = await this.viewFirstProposalByGovernanceAction( + governanceAction as GovernanceActionType + ); + if (result) { + return result; + } + } + return null; + } + async viewFirstProposalByGovernanceAction( - governanceAction: GrovernanceActionType + governanceAction: GovernanceActionType ): Promise { const proposalCard = this.page .getByTestId(`govaction-${governanceAction}-card`) @@ -121,7 +139,7 @@ export default class GovernanceActionsPage { async sortAndValidate( sortOption: string, validationFn: (p1: IProposal, p2: IProposal) => boolean, - filterKeys = Object.keys(GrovernanceActionType) + filterKeys = Object.keys(GovernanceActionType) ) { const responsesPromise = Promise.all( filterKeys.map((filterKey) => @@ -129,7 +147,7 @@ export default class GovernanceActionsPage { response .url() .includes( - `&type[]=${GrovernanceActionType[filterKey]}&sort=${sortOption}` + `&type[]=${GovernanceActionType[filterKey]}&sort=${sortOption}` ) ) ) @@ -165,7 +183,7 @@ export default class GovernanceActionsPage { for (let dIdx = 0; dIdx <= proposalsByType.length - 1; dIdx++) { const proposals = proposalsByType[0] as IProposal[]; const filterOptionKey = getEnumKeyByValue( - GrovernanceActionType, + GovernanceActionType, proposals[0].type ); diff --git a/tests/govtool-frontend/playwright/lib/services/blockfrostService.ts b/tests/govtool-frontend/playwright/lib/services/blockfrostService.ts new file mode 100644 index 000000000..8e652d13f --- /dev/null +++ b/tests/govtool-frontend/playwright/lib/services/blockfrostService.ts @@ -0,0 +1,37 @@ +import environments from "@constants/environments"; + +export async function blockfrostSubmitTransaction(cborSignedTx: Buffer) { + const url = `${environments.blockfrostApiUrl}/v0/tx/submit`; + const res = await fetch(url, { + method: "POST", + headers: { + "Content-Type": "application/cbor", + project_id: environments.blockfrostApiKey, + }, + body: cborSignedTx, + }); + if (res.status === 200) { + return res.json(); + } else { + return res.text().then((txt) => { + let err; + let json: any; + try { + json = JSON.parse(txt); + if (json) { + err = Error( + `BlockFrost [Status ${res.status}] : ${ + json.message ? json.message : txt + }` + ); + } else { + err = Error(`BlockFrost [Status ${res.status}] : ${txt}`); + } + } catch (e) { + err = Error(`BlockFrost [Status ${res.status}] : ${txt}`); + } + err.status = res.status; + throw err; + }); + } +} diff --git a/tests/govtool-frontend/playwright/lib/services/kuberService.ts b/tests/govtool-frontend/playwright/lib/services/kuberService.ts index 154b885e1..8de48ce41 100644 --- a/tests/govtool-frontend/playwright/lib/services/kuberService.ts +++ b/tests/govtool-frontend/playwright/lib/services/kuberService.ts @@ -6,11 +6,12 @@ import { WalletAndAnchorType, } from "@types"; import * as blake from "blakejs"; -import environments from "lib/constants/environments"; +import environments from "@constants/environments"; import { LockInterceptor, LockInterceptorInfo } from "lib/lockInterceptor"; import fetch, { BodyInit, RequestInit } from "node-fetch"; -import { cborxDecoder, cborxEncoder } from "../helpers/encodeDecode"; +import { cborxEncoder } from "@helpers/encodeDecode"; import { Logger } from "@helpers/logger"; +import { blockfrostSubmitTransaction } from "@services/blockfrostService"; type CertificateType = "registerstake" | "registerdrep" | "deregisterdrep"; @@ -99,20 +100,17 @@ class Kuber { `Submitting tx: ${JSON.stringify({ lock_id: lockId, tx: signedTx })}` ); - const res = (await callKuber( - `/api/${this.version}/tx?submit=true`, + const response = (await callKuber( + `/api/${this.version}/tx?submit=false`, "POST", JSON.stringify(signedTx) )) as any; - let decodedTx = cborxDecoder.decode(Buffer.from(res.cborHex, "hex")); - const submittedTxBody = Uint8Array.from(cborxEncoder.encode(decodedTx[0])); - const submittedTxHash = Buffer.from( - blake.blake2b(submittedTxBody, undefined, 32) - ).toString("hex"); + const cborSignedTx = Buffer.from(response.cborHex, "hex"); + const submittedTxHash = await blockfrostSubmitTransaction(cborSignedTx); Logger.success(`Tx submitted: ${submittedTxHash}`); return { - cbor: res.cborHex, + cbor: response.cborHex, txId: submittedTxHash, }; } diff --git a/tests/govtool-frontend/playwright/lib/types.ts b/tests/govtool-frontend/playwright/lib/types.ts index 2bde6027d..b20caf09c 100644 --- a/tests/govtool-frontend/playwright/lib/types.ts +++ b/tests/govtool-frontend/playwright/lib/types.ts @@ -74,7 +74,7 @@ export enum ProposalType { treasury = "Treasury", } -export enum GrovernanceActionType { +export enum GovernanceActionType { ProtocolParameterChange = "ParameterChange", InfoAction = "InfoAction", TreasuryWithdrawal = "TreasuryWithdrawals", diff --git a/tests/govtool-frontend/playwright/package-lock.json b/tests/govtool-frontend/playwright/package-lock.json index 51252ad2e..74d2b854d 100644 --- a/tests/govtool-frontend/playwright/package-lock.json +++ b/tests/govtool-frontend/playwright/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@cardanoapi/cardano-test-wallet": "^2.1.1", + "@cardanoapi/cardano-test-wallet": "^3.0.0", "@faker-js/faker": "^8.4.1", "@noble/curves": "^1.3.0", "@noble/ed25519": "^2.0.0", @@ -43,9 +43,9 @@ } }, "node_modules/@cardanoapi/cardano-test-wallet": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@cardanoapi/cardano-test-wallet/-/cardano-test-wallet-2.1.1.tgz", - "integrity": "sha512-K5HISR0GmWiJpZOaWT3PqfP7ez+Ht9+jrW8dSGwEXKfKPnj9g6uqUSnkUbuw3FmPeVRgNcB9EwkwmZzPcg7gZw==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@cardanoapi/cardano-test-wallet/-/cardano-test-wallet-3.0.0.tgz", + "integrity": "sha512-5cQPQPrsco8eeDBIQI74jiAVl48hI0E3u2JSmya9PvRL/D18LUPqanmhpDScD8fpWRzS7MKSAhqoqsxTlgWYOw==", "license": "MIT" }, "node_modules/@cbor-extract/cbor-extract-darwin-arm64": { diff --git a/tests/govtool-frontend/playwright/package.json b/tests/govtool-frontend/playwright/package.json index 9edc9eac5..b52c3e5f8 100644 --- a/tests/govtool-frontend/playwright/package.json +++ b/tests/govtool-frontend/playwright/package.json @@ -28,7 +28,7 @@ "generate-wallets": "ts-node ./generate_wallets.ts 17" }, "dependencies": { - "@cardanoapi/cardano-test-wallet": "^2.1.1", + "@cardanoapi/cardano-test-wallet": "^3.0.0", "@faker-js/faker": "^8.4.1", "@noble/curves": "^1.3.0", "@noble/ed25519": "^2.0.0", diff --git a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts index d4842d6ba..9f593c004 100644 --- a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.dRep.spec.ts @@ -13,7 +13,7 @@ import GovernanceActionsPage from "@pages/governanceActionsPage"; import { expect } from "@playwright/test"; import walletManager from "lib/walletManager"; import DRepDirectoryPage from "@pages/dRepDirectoryPage"; -import { GrovernanceActionType } from "@types"; +import { GovernanceActionType } from "@types"; test.beforeEach(async () => { await setAllureEpic("3. DRep registration"); @@ -37,12 +37,12 @@ test.describe("Logged in DReps", () => { const governanceActionsPage = new GovernanceActionsPage(page); await governanceActionsPage.goto(); - + await expect(page.getByText(/info action/i).first()).toBeVisible(); const governanceActionDetailsPage = await governanceActionsPage.viewFirstProposalByGovernanceAction( - GrovernanceActionType.InfoAction + GovernanceActionType.InfoAction ); await expect(governanceActionDetailsPage.voteBtn).toBeVisible(); diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts index c4d2afbec..c066337ff 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.dRep.spec.ts @@ -16,7 +16,7 @@ import { invalid as mockInvalid, valid as mockValid } from "@mock/index"; import { BootstrapGovernanceActionType, FullGovernanceDRepVoteActionsType, - GrovernanceActionType, + GovernanceActionType, IProposal, } from "@types"; import walletManager from "lib/walletManager"; @@ -57,11 +57,16 @@ test.describe("Logged in DRep", () => { const govActionsPage = new GovernanceActionsPage(page); await govActionsPage.goto(); + // assert to wait until the loading button is hidden + await expect(page.getByTestId("to-vote-tab")).toBeVisible({ + timeout: 15_000, + }); + govActionDetailsPage = (await isBootStrapingPhase()) ? await govActionsPage.viewFirstProposalByGovernanceAction( - GrovernanceActionType.InfoAction + GovernanceActionType.InfoAction ) - : await govActionsPage.viewFirstProposal(); + : await govActionsPage.viewFirstDRepVoteEnabledGovernanceAction(); await govActionDetailsPage.contextBtn.click(); await govActionDetailsPage.contextInput.fill(faker.lorem.sentence(200)); @@ -207,9 +212,7 @@ test.describe("Check vote count", () => { // check ccCommittee votes if ( - areCCVoteTotalsDisplayed( - proposalToCheck.type as GrovernanceActionType - ) + areCCVoteTotalsDisplayed(proposalToCheck.type as GovernanceActionType) ) { await expect(govActionDetailsPage.ccCommitteeYesVotes).toHaveText( `${proposalToCheck.ccYesVotes}` diff --git a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts index 7918dc968..8b08c93c0 100644 --- a/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts +++ b/tests/govtool-frontend/playwright/tests/4-proposal-visibility/proposalVisibility.spec.ts @@ -9,7 +9,7 @@ import { import GovernanceActionDetailsPage from "@pages/governanceActionDetailsPage"; import GovernanceActionsPage from "@pages/governanceActionsPage"; import { expect, test } from "@playwright/test"; -import { GrovernanceActionType, IProposal } from "@types"; +import { GovernanceActionType, IProposal } from "@types"; test.beforeEach(async () => { await setAllureEpic("4. Proposal visibility"); @@ -39,9 +39,9 @@ test("4K. Should display correct vote counts on governance details page for disc page, browser, }) => { - const responsesPromise = Object.keys(GrovernanceActionType).map((filterKey) => + const responsesPromise = Object.keys(GovernanceActionType).map((filterKey) => page.waitForResponse((response) => - response.url().includes(`&type[]=${GrovernanceActionType[filterKey]}`) + response.url().includes(`&type[]=${GovernanceActionType[filterKey]}`) ) ); @@ -98,7 +98,7 @@ test("4K. Should display correct vote counts on governance details page for disc // check ccCommittee votes if ( - areCCVoteTotalsDisplayed(proposalToCheck.type as GrovernanceActionType) + areCCVoteTotalsDisplayed(proposalToCheck.type as GovernanceActionType) ) { await expect(govActionDetailsPage.ccCommitteeYesVotes).toHaveText( `${proposalToCheck.ccYesVotes}` diff --git a/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts index a801a334d..989f9e661 100644 --- a/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/5-proposal-functionality/proposalFunctionality.dRep.spec.ts @@ -12,7 +12,7 @@ import GovernanceActionDetailsPage from "@pages/governanceActionDetailsPage"; import GovernanceActionsPage from "@pages/governanceActionsPage"; import { Page, expect } from "@playwright/test"; import kuberService from "@services/kuberService"; -import { BootstrapGovernanceActionType, GrovernanceActionType } from "@types"; +import { BootstrapGovernanceActionType, GovernanceActionType } from "@types"; import walletManager from "lib/walletManager"; const invalidInfinityProposals = require("../../lib/_mock/invalidInfinityProposals.json"); @@ -32,12 +32,17 @@ test.describe("Proposal checks", () => { const govActionsPage = new GovernanceActionsPage(page); await govActionsPage.goto(); + // assert to wait until the loading button is hidden + await expect(page.getByTestId("to-vote-tab")).toBeVisible({ + timeout: 15_000, + }); + currentPage = page; govActionDetailsPage = (await isBootStrapingPhase()) ? await govActionsPage.viewFirstProposalByGovernanceAction( - GrovernanceActionType.InfoAction + GovernanceActionType.InfoAction ) - : await govActionsPage.viewFirstProposal(); + : await govActionsPage.viewFirstDRepVoteEnabledGovernanceAction(); }); test("5A. Should show relevant details about governance action as DRep", async () => { @@ -181,11 +186,16 @@ test.describe("Perform voting", () => { const govActionsPage = new GovernanceActionsPage(dRepPage); await govActionsPage.goto(); + // assert to wait until the loading button is hidden + await expect(dRepPage.getByTestId("to-vote-tab")).toBeVisible({ + timeout: 15_000, + }); + govActionDetailsPage = (await isBootStrapingPhase()) ? await govActionsPage.viewFirstProposalByGovernanceAction( - GrovernanceActionType.InfoAction + GovernanceActionType.InfoAction ) - : await govActionsPage.viewFirstProposal(); + : await govActionsPage.viewFirstDRepVoteEnabledGovernanceAction(); }); test("5E. Should re-vote with new data on a already voted governance action", async ({}, testInfo) => { @@ -211,7 +221,7 @@ test.describe("Perform voting", () => { await expect( govActionDetailsPage.currentPage.getByTestId("my-vote").getByText("No") - ).toBeVisible(); + ).toBeVisible({ timeout: 15_000 }); }); test("5F. Should show notification of casted vote after vote", async ({}, testInfo) => { @@ -271,77 +281,62 @@ test.describe("Check voting power", () => { }); test.describe("Bootstrap phase", () => { - test.use({ storageState: ".auth/dRep01.json", wallet: dRep01Wallet }); test("5L. Should restrict dRep votes to Info Governance actions During Bootstrapping Phase", async ({ - page, - context, + browser, }) => { - await page.route("**/epoch/params", async (route) => { - // Fetch the original response from the server - const response = await route.fetch(); - const json = await response.json(); - - // update protocol major version - json["protocol_major"] = 9; - await route.fulfill({ - status: 200, - contentType: "application/json", - body: JSON.stringify(json), - }); - }); - - const voteBlacklistOptions = Object.keys(GrovernanceActionType).filter( + const voteBlacklistOptions = Object.keys(GovernanceActionType).filter( (option) => option !== BootstrapGovernanceActionType.InfoAction ); - const govActionsPage = new GovernanceActionsPage(page); - await govActionsPage.goto(); - - // wait until the loading button is hidden - await expect( - page.getByRole("progressbar").getByRole("img") - ).not.toBeVisible({ timeout: 10_000 }); - - for (const voteBlacklistOption of voteBlacklistOptions) { - const governanceActionDetailsPage = - await govActionsPage.viewFirstProposalByGovernanceAction( - voteBlacklistOption as GrovernanceActionType - ); - - if (governanceActionDetailsPage !== null) { - // dRep vote - await expect(governanceActionDetailsPage.dRepYesVotes).toBeVisible(); - await expect( - governanceActionDetailsPage.dRepAbstainVotes - ).toBeVisible(); - await expect(governanceActionDetailsPage.dRepNoVotes).toBeVisible(); - - // sPos vote - await expect(governanceActionDetailsPage.sPosYesVotes).toBeVisible(); - await expect( - governanceActionDetailsPage.sPosAbstainVotes - ).toBeVisible(); - await expect(governanceActionDetailsPage.sPosNoVotes).toBeVisible(); - - // ccCommittee vote - await expect( - governanceActionDetailsPage.ccCommitteeYesVotes - ).toBeVisible(); - await expect( - governanceActionDetailsPage.ccCommitteeAbstainVotes - ).toBeVisible(); - await expect( - governanceActionDetailsPage.ccCommitteeNoVotes - ).toBeVisible(); - - await expect( - governanceActionDetailsPage.yesVoteRadio - ).not.toBeVisible(); - await expect(governanceActionDetailsPage.contextBtn).not.toBeVisible(); - await expect(governanceActionDetailsPage.voteBtn).not.toBeVisible(); - - await governanceActionDetailsPage.backBtn.click(); - } - } + await Promise.all( + voteBlacklistOptions.map(async (voteBlacklistOption) => { + const dRepPage = await createNewPageWithWallet(browser, { + storageState: ".auth/dRep01.json", + wallet: dRep01Wallet, + }); + + await dRepPage.route("**/epoch/params", async (route) => { + // Fetch the original response from the server + const response = await route.fetch(); + const json = await response.json(); + + // update protocol major version + json["protocol_major"] = 9; + await route.fulfill({ + status: 200, + contentType: "application/json", + body: JSON.stringify(json), + }); + }); + + const governanceActionsPage = new GovernanceActionsPage(dRepPage); + await governanceActionsPage.goto(); + + // assert to wait until proposal cards are visible + await expect(dRepPage.getByTestId("voting-power-chips")).toBeVisible(); + // wait until the loading button is hidden + await expect(dRepPage.getByTestId("to-vote-tab")).toBeVisible({ + timeout: 15_000, + }); + + const governanceActionDetailsPage = + await governanceActionsPage.viewFirstProposalByGovernanceAction( + voteBlacklistOption as GovernanceActionType + ); + + if (governanceActionDetailsPage) { + await expect( + dRepPage.getByTestId("governance-action-details-card-header") + ).toBeVisible(); + await expect( + governanceActionDetailsPage.yesVoteRadio + ).not.toBeVisible(); + await expect( + governanceActionDetailsPage.contextBtn + ).not.toBeVisible(); + await expect(governanceActionDetailsPage.voteBtn).not.toBeVisible(); + } + }) + ); }); }); diff --git a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts index b96e5e2dc..b61e6e3c4 100644 --- a/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/7-proposal-submission/proposalSubmission.loggedin.spec.ts @@ -9,7 +9,10 @@ import { import { faker } from "@faker-js/faker"; import { test } from "@fixtures/proposal"; import { setAllureEpic } from "@helpers/allure"; -import { skipIfTreasuryAndBootstrapping, skipIfNotHardFork } from "@helpers/cardano"; +import { + skipIfTreasuryAndBootstrapping, + skipIfNotHardFork, +} from "@helpers/cardano"; import { ShelleyWallet } from "@helpers/crypto"; import { createNewPageWithWallet } from "@helpers/page"; import { invalid, valid as mockValid } from "@mock/index"; @@ -261,7 +264,9 @@ test.describe("Proposal created logged state", () => { }) => { test.slow(); // Brute-force testing with 100 random data for (let i = 0; i < 50; i++) { - await proposalSubmissionPage.metadataUrlInput.fill(invalid.url(false)); + await proposalSubmissionPage.metadataUrlInput.fill( + invalid.url(false) + ); await expect(page.getByTestId("url-input-error-text")).toBeVisible(); }