diff --git a/.github/workflows/test_storybook.yml b/.github/workflows/test_storybook.yml index 45c4ca641..88b58a879 100644 --- a/.github/workflows/test_storybook.yml +++ b/.github/workflows/test_storybook.yml @@ -27,7 +27,7 @@ jobs: - name: Install Playwright run: npx playwright install --with-deps - name: Build Storybook - run: NODE_OPTIONS="--max-old-space-size=8046" npm run build-storybook --quiet + run: NODE_OPTIONS="--max-old-space-size=8046" npm run build:storybook --quiet - name: Serve Storybook and run tests run: | npx concurrently -k -s first -n "SB,TEST" -c "magenta,blue" \ diff --git a/CHANGELOG.md b/CHANGELOG.md index e18791a97..4d8447c59 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ changes. ### Fixed +- drep/get-voting-power no longer throws 500 for non-existing dreps. Instead it returns 0 [Issue 1093](https://github.com/IntersectMBO/govtool/issues/1093) - proposal/list no longer throws 500 error when proposal's url is incorrect [Issue 1073](https://github.com/IntersectMBO/govtool/issues/1073) - drep/list sql fix (now the drep type is correct) [Issue 957](https://github.com/IntersectMBO/govtool/issues/957) - drep/list sql fix (now the latest tx date is correct) [Issue 826](https://github.com/IntersectMBO/govtool/issues/826) @@ -99,6 +100,7 @@ changes. - Fix validation of the GAs with missing references [Issue 1282](https://github.com/IntersectMBO/govtool/issues/1282) - Fix displaying the GA Markdowns [Issue 1244](https://github.com/IntersectMBO/govtool/issues/1244) - Fix app crash on voting on the GA without the connected wallet before [Issue 1313](https://github.com/IntersectMBO/govtool/issues/1313) +- Fix the navigation to Home from Proposal pillar on disconnected wallet [Issue 1355](https://github.com/IntersectMBO/govtool/issues/1355) ### Changed @@ -131,6 +133,7 @@ changes. - Changed documents to prepare for open source [Issue 737](https://github.com/IntersectMBO/govtool/issues/737) - Changed copy on maintenance page [Issue 753](https://github.com/IntersectMBO/govtool/issues/753) - Update link to docs [Issue 1246](https://github.com/IntersectMBO/govtool/issues/1246) +- Change label of Proposal Discussion nav item [Issue 1349](https://github.com/IntersectMBO/govtool/issues/1349) ### Removed diff --git a/docs/GOVERNANCE_ACTION_SUBMISSION.md b/docs/GOVERNANCE_ACTION_SUBMISSION.md index 1f41a31fd..2262d5ddd 100644 --- a/docs/GOVERNANCE_ACTION_SUBMISSION.md +++ b/docs/GOVERNANCE_ACTION_SUBMISSION.md @@ -144,3 +144,46 @@ await buildSignSubmitConwayCertTx({ ### Step 6: Verify the Governance Action `buildSignSubmitConwayCertTx` logs the transaction CBOR making it able to be tracked on the transactions tools such as cexplorer. + +## Additional steps for using the GovTool metadata validation on the imported Pillar component + +```tsx +enum MetadataValidationStatus { + URL_NOT_FOUND = "URL_NOT_FOUND", + INVALID_JSONLD = "INVALID_JSONLD", + INVALID_HASH = "INVALID_HASH", + INCORRECT_FORMAT = "INCORRECT_FORMAT", +} +// Using the props passed to the component +type Props = { + validateMetadata: ({ + url, + hash, + standard, + }: { + url: string; + hash: string; + standard: "CIP108"; + }) => Promise<{ + metadata?: any; + status?: MetadataValidationStatus; + valid: boolean; + }>; +}; + +import React, { Suspense } from "react"; + +const SomeImportedPillar: React.FC = React.lazy( + () => import("path/to/SomeImportedPillar") +); + +const SomeWrapperComponent = () => { + const { validateMetadata } = useValidateMutation(); + + return ( + I am lazy loading...}> + + + ); +}; +``` diff --git a/govtool/backend/src/VVA/DRep.hs b/govtool/backend/src/VVA/DRep.hs index 347527337..ce0b031ba 100644 --- a/govtool/backend/src/VVA/DRep.hs +++ b/govtool/backend/src/VVA/DRep.hs @@ -46,10 +46,12 @@ getVotingPower :: Text -> m Integer getVotingPower drepId = withPool $ \conn -> do - [SQL.Only votingPower] <- + result <- liftIO (SQL.query @_ @(SQL.Only Scientific) conn getVotingPowerSql $ SQL.Only drepId) - return $ floor votingPower + case result of + [SQL.Only votingPower] -> return $ floor votingPower + [] -> return 0 listDRepsSql :: SQL.Query listDRepsSql = sqlFrom $(embedFile "sql/list-dreps.sql") diff --git a/govtool/frontend/package-lock.json b/govtool/frontend/package-lock.json index 3d53d63ab..dcea00ac9 100644 --- a/govtool/frontend/package-lock.json +++ b/govtool/frontend/package-lock.json @@ -1,12 +1,12 @@ { - "name": "voltaire-voting-app", - "version": "0.0.0", + "name": "govtool", + "version": "1.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "voltaire-voting-app", - "version": "0.0.0", + "name": "govtool", + "version": "1.0.6", "hasInstallScript": true, "dependencies": { "@emotion/react": "^11.11.1", @@ -20,9 +20,6 @@ "@rollup/plugin-babel": "^6.0.4", "@rollup/pluginutils": "^5.1.0", "@sentry/react": "^7.77.0", - "@types/jsonld": "^1.5.13", - "@types/react": "^18.2.12", - "@types/react-gtm-module": "^2.0.2", "@usersnap/browser": "^0.0.5", "axios": "^1.4.0", "bech32": "^2.0.0", @@ -64,7 +61,9 @@ "@testing-library/user-event": "^14.5.2", "@types/jsonld": "^1.5.13", "@types/node": "^20.4.8", + "@types/react": "^18.2.12", "@types/react-dom": "^18.0.11", + "@types/react-gtm-module": "^2.0.2", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", "@vitejs/plugin-react": "^4.3.0", @@ -9696,7 +9695,8 @@ "node_modules/@types/react-gtm-module": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/react-gtm-module/-/react-gtm-module-2.0.3.tgz", - "integrity": "sha512-fL2zKdDFN5LckSsVBXEhhm9M4tFTM9oHJfGcfZJzktQkzpOTGtDM8oXIP9d9UBDxO4xLNZhS22dlgRVv6wgK9w==" + "integrity": "sha512-fL2zKdDFN5LckSsVBXEhhm9M4tFTM9oHJfGcfZJzktQkzpOTGtDM8oXIP9d9UBDxO4xLNZhS22dlgRVv6wgK9w==", + "dev": true }, "node_modules/@types/react-transition-group": { "version": "4.4.10", diff --git a/govtool/frontend/package.json b/govtool/frontend/package.json index 0c62659b1..12d82d250 100644 --- a/govtool/frontend/package.json +++ b/govtool/frontend/package.json @@ -1,11 +1,11 @@ { - "name": "voltaire-voting-app", + "name": "govtool", "private": true, - "version": "0.0.0", + "version": "1.0.6", "type": "module", "scripts": { "build": "vite build", - "build-storybook": "storybook build", + "build:storybook": "storybook build", "chromatic": "chromatic", "dev": "vite", "format": "prettier --write src", @@ -34,9 +34,6 @@ "@rollup/plugin-babel": "^6.0.4", "@rollup/pluginutils": "^5.1.0", "@sentry/react": "^7.77.0", - "@types/jsonld": "^1.5.13", - "@types/react": "^18.2.12", - "@types/react-gtm-module": "^2.0.2", "@usersnap/browser": "^0.0.5", "axios": "^1.4.0", "bech32": "^2.0.0", @@ -78,7 +75,9 @@ "@testing-library/user-event": "^14.5.2", "@types/jsonld": "^1.5.13", "@types/node": "^20.4.8", + "@types/react": "^18.2.12", "@types/react-dom": "^18.0.11", + "@types/react-gtm-module": "^2.0.2", "@typescript-eslint/eslint-plugin": "^7.3.1", "@typescript-eslint/parser": "^7.3.1", "@vitejs/plugin-react": "^4.3.0", @@ -107,5 +106,5 @@ "typescript": "^5.0.2" }, "readme": "ERROR: No README data found!", - "_id": "voltaire-voting-app@0.0.0" + "_id": "govtool@1.0.6" } diff --git a/govtool/frontend/src/App.tsx b/govtool/frontend/src/App.tsx index c1756cdae..c61c798cc 100644 --- a/govtool/frontend/src/App.tsx +++ b/govtool/frontend/src/App.tsx @@ -40,7 +40,7 @@ import { PDFWrapper } from "./components/organisms/PDFWrapper"; export default () => { const { isProposalDiscussionForumEnabled } = useFeatureFlag(); - const { enable, isEnabled } = useCardano(); + const { enable } = useCardano(); const navigate = useNavigate(); const { modal, openModal, modals } = useModal(); @@ -87,10 +87,7 @@ export default () => { !window.location.pathname.includes(PATHS.proposalPillar.replace("/*", "")) ) { navigate( - `${(isEnabled - ? PATHS.connectedProposalPillar - : PATHS.proposalPillar - ).replace("/*", "")}${window.location.pathname}`, + `${PATHS.proposalPillar.replace("/*", "")}${window.location.pathname}`, ); } }, [window.location.pathname]); @@ -101,9 +98,6 @@ export default () => { } /> } /> - {isProposalDiscussionForumEnabled && !isEnabled && ( - } /> - )} } @@ -115,10 +109,7 @@ export default () => { }> } /> {isProposalDiscussionForumEnabled && ( - } - /> + } /> )} { ); const copyButton = screen.getByTestId("copy-button"); - await userEvent.click(copyButton); - expect(navigator.clipboard.writeText).toHaveBeenCalledWith("Example Text"); - expect(screen.getByText("alerts.copiedToClipboard")).toBeInTheDocument(); + await act(async () => { + await userEvent.click(copyButton); + }); + + expect(navigator.clipboard.writeText).toHaveBeenCalledWith("Example Text"); }); }); diff --git a/govtool/frontend/src/components/atoms/DrawerLink.tsx b/govtool/frontend/src/components/atoms/DrawerLink.tsx index e35bcdb0d..94848be2c 100644 --- a/govtool/frontend/src/components/atoms/DrawerLink.tsx +++ b/govtool/frontend/src/components/atoms/DrawerLink.tsx @@ -17,7 +17,7 @@ type LinkProps = { const isRouteActive = (isActive: boolean, route: string) => isActive || (route === - `${PATHS.connectedProposalPillar.replace("/*", "")}${ + `${PATHS.proposalPillar.replace("/*", "")}${ PDF_PATHS.proposalDiscussion }` && Object.values(PDF_PATHS).some((pdfPath) => diff --git a/govtool/frontend/src/components/atoms/modal/Modal.tsx b/govtool/frontend/src/components/atoms/modal/Modal.tsx index 4c88d99f8..9eb5ca08f 100644 --- a/govtool/frontend/src/components/atoms/modal/Modal.tsx +++ b/govtool/frontend/src/components/atoms/modal/Modal.tsx @@ -1,3 +1,4 @@ +import { DialogContent } from "@mui/material"; import MuiModal from "@mui/material/Modal"; import type { ComponentProps } from "react"; @@ -11,6 +12,6 @@ interface Props { export const Modal = ({ open, children, handleClose }: Props) => ( - {children} + {children} ); diff --git a/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx b/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx index 598b21e4b..8576b4824 100644 --- a/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx +++ b/govtool/frontend/src/components/atoms/modal/ModalWrapper.tsx @@ -3,6 +3,7 @@ import { SxProps, styled } from "@mui/material/styles"; import { ICONS } from "@consts"; import { useModal } from "@context"; import { callAll } from "@utils"; +import { forwardRef } from "react"; interface Props { variant?: "modal" | "popup"; @@ -49,27 +50,32 @@ export const CloseButton = styled("img")` right: 24px; `; -export const ModalWrapper = ({ - children, - onClose, - variant = "modal", - hideCloseButton = false, - dataTestId = "modal", - sx, -}: Props) => { - const { closeModal } = useModal(); +export const ModalWrapper = forwardRef( + ( + { + children, + onClose, + variant = "modal", + hideCloseButton = false, + dataTestId = "modal", + sx, + }, + ref, + ) => { + const { closeModal } = useModal(); - return ( - - {variant !== "popup" && !hideCloseButton && ( - - )} - {children} - - ); -}; + return ( + + {variant !== "popup" && !hideCloseButton && ( + + )} + {children} + + ); + }, +); diff --git a/govtool/frontend/src/components/atoms/types.ts b/govtool/frontend/src/components/atoms/types.ts index 94ab87d4f..f67bf37f2 100644 --- a/govtool/frontend/src/components/atoms/types.ts +++ b/govtool/frontend/src/components/atoms/types.ts @@ -21,7 +21,7 @@ export type LoadingButtonProps = ButtonProps & { export type TypographyProps = Pick< MUITypographyProps, - "color" | "lineHeight" | "sx" + "color" | "lineHeight" | "sx" | "component" > & { children?: React.ReactNode; fontSize?: number; diff --git a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx index 243287f36..6c7f8ebf1 100644 --- a/govtool/frontend/src/components/molecules/DashboardActionCard.tsx +++ b/govtool/frontend/src/components/molecules/DashboardActionCard.tsx @@ -85,6 +85,7 @@ export const DashboardActionCard: FC = ({ variant="title2" fontWeight={600} sx={{ display: "inline" }} + component="span" > {` ${t("inProgress")}`} diff --git a/govtool/frontend/src/components/molecules/WalletInfoCard.tsx b/govtool/frontend/src/components/molecules/WalletInfoCard.tsx index 054067c3d..475828f64 100644 --- a/govtool/frontend/src/components/molecules/WalletInfoCard.tsx +++ b/govtool/frontend/src/components/molecules/WalletInfoCard.tsx @@ -16,7 +16,7 @@ export const WalletInfoCard = () => { await disconnectWallet(); navigate( pathname.includes("/connected") - ? `${pathname.replace("/connected", "")}${hash}` + ? `${pathname.replace("/connected", "")}${hash ?? ""}` : PATHS.home, ); window.location.reload(); diff --git a/govtool/frontend/src/components/molecules/WalletOption.tsx b/govtool/frontend/src/components/molecules/WalletOption.tsx index f33b599aa..381fa2ee5 100644 --- a/govtool/frontend/src/components/molecules/WalletOption.tsx +++ b/govtool/frontend/src/components/molecules/WalletOption.tsx @@ -35,9 +35,12 @@ export const WalletOptionButton: FC = ({ const result = await enable(name); if (result?.stakeKey) { navigate( - pathToNavigate ?? pathname === "/" + // eslint-disable-next-line no-unneeded-ternary + pathToNavigate + ? pathToNavigate + : pathname === "/" ? "/dashboard" - : `connected${pathname}${hash}`, + : `connected${pathname}${hash ?? ""}`, { state }, ); return; diff --git a/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx b/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx index a68aa2005..00a0adfc3 100644 --- a/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx +++ b/govtool/frontend/src/components/organisms/DashboardCards/ProposeGovActionDashboardCard.tsx @@ -37,7 +37,7 @@ export const ProposeGovActionDashboardCard = ({ navigate( isProposalDiscussionForumEnabled - ? `${PATHS.connectedProposalPillar.replace("/*", "")}${ + ? `${PATHS.proposalPillar.replace("/*", "")}${ PDF_PATHS.proposalDiscussion }` : PATHS.createGovernanceAction, diff --git a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx index 205321e00..00f121378 100644 --- a/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx +++ b/govtool/frontend/src/components/organisms/EditDRepInfoSteps/EditDRepForm.tsx @@ -21,15 +21,19 @@ const MAX_NUMBER_OF_LINKS = 7; export const EditDRepForm = ({ onClickCancel, setStep, + loadUserData, + setLoadUserData, }: { onClickCancel: () => void; setStep: Dispatch>; + loadUserData: boolean; + setLoadUserData: Dispatch>; }) => { const { state } = useLocation(); const { t } = useTranslation(); const { isMobile } = useScreenDimension(); const { dRepID } = useCardano(); - const { control, errors, isError, register, watch, reset, getValues } = + const { control, errors, isError, register, watch, reset } = useEditDRepInfoForm(); const { append, @@ -47,7 +51,10 @@ export const EditDRepForm = ({ { enabled: !state }, ); - const onClickContinue = () => setStep(2); + const onClickContinue = () => { + setStep(2); + setLoadUserData(false); + }; const addLink = useCallback(() => append({ uri: "" }), [append]); @@ -56,25 +63,28 @@ export const EditDRepForm = ({ const isContinueButtonDisabled = !watch("dRepName") || isError; useEffect(() => { - if (!getValues().dRepName) + if (loadUserData) { reset( state ? { ...state, - references: state.references.map((uri: string) => ({ - uri, - })), + references: state.references.length + ? state.references.map((uri: string) => ({ + uri, + })) + : [{ uri: "" }], } : { ...yourselfDRepList?.[0], - references: yourselfDRepList?.[0].references.map( - (uri: string) => ({ - uri, - }), - ), + references: yourselfDRepList?.[0].references.length + ? yourselfDRepList?.[0].references.map((uri: string) => ({ + uri, + })) + : [{ uri: "" }], }, ); - }, [yourselfDRepList]); + } + }, [yourselfDRepList?.[0], loadUserData]); const renderLinks = useCallback( () => diff --git a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx index 2350bb0a5..d1be2a797 100644 --- a/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx +++ b/govtool/frontend/src/components/organisms/GovernanceActionDetailsCardData.tsx @@ -64,7 +64,9 @@ export const GovernanceActionDetailsCardData = ({ const govActionLinkToShare = `${window.location.protocol}//${ window.location.hostname - }${window.location.port ? `:${window.location.port}` : ""}${pathname}${hash}`; + }${window.location.port ? `:${window.location.port}` : ""}${pathname}${ + hash ?? "" + }`; return ( { +export const ChooseWalletModal = forwardRef((_, ref) => { const { t } = useTranslation(); const { state } = useModal(); @@ -47,7 +47,7 @@ export const ChooseWalletModal = () => { }, [window]); return ( - + {t("wallet.connectYourWallet")} { ); -}; +}); diff --git a/govtool/frontend/src/components/organisms/Modal/ExternalLinkModal.tsx b/govtool/frontend/src/components/organisms/Modal/ExternalLinkModal.tsx index e49f5b625..8437f3ae0 100644 --- a/govtool/frontend/src/components/organisms/Modal/ExternalLinkModal.tsx +++ b/govtool/frontend/src/components/organisms/Modal/ExternalLinkModal.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from "react"; import { Box, Button, Typography } from "@mui/material"; import { ModalContents, ModalHeader, ModalWrapper } from "@atoms"; @@ -11,7 +12,7 @@ export interface ExternalLinkModalState { externalLink: string; } -export const ExternalLinkModal = () => { +export const ExternalLinkModal = forwardRef((_, ref) => { const { state, closeModal } = useModal(); const { isMobile } = useScreenDimension(); const { t } = useTranslation(); @@ -20,7 +21,7 @@ export const ExternalLinkModal = () => { } = theme; return ( - + Status icon { ); -}; +}); diff --git a/govtool/frontend/src/components/organisms/Modal/LoadingModal.tsx b/govtool/frontend/src/components/organisms/Modal/LoadingModal.tsx index 2c95b9574..8a87b91bd 100644 --- a/govtool/frontend/src/components/organisms/Modal/LoadingModal.tsx +++ b/govtool/frontend/src/components/organisms/Modal/LoadingModal.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from "react"; import { Typography } from "@mui/material"; import { Loader, ModalContents, ModalHeader, ModalWrapper } from "@atoms"; @@ -10,7 +11,7 @@ export interface LoadingModalState { dataTestId: string; } -export const LoadingModal = () => { +export const LoadingModal = forwardRef((_, ref) => { const { state } = useModal(); const { isMobile } = useScreenDimension(); @@ -18,6 +19,7 @@ export const LoadingModal = () => { @@ -33,4 +35,4 @@ export const LoadingModal = () => { ); -}; +}); diff --git a/govtool/frontend/src/components/organisms/Modal/StatusModal.tsx b/govtool/frontend/src/components/organisms/Modal/StatusModal.tsx index ca5adb5d1..2b1e24ea6 100644 --- a/govtool/frontend/src/components/organisms/Modal/StatusModal.tsx +++ b/govtool/frontend/src/components/organisms/Modal/StatusModal.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from "react"; import { Button, Link, Typography } from "@mui/material"; import { ModalContents, ModalHeader, ModalWrapper } from "@atoms"; @@ -22,7 +23,7 @@ export interface StatusModalState { dataTestId: string; } -export const StatusModal = () => { +export const StatusModal = forwardRef((_, ref) => { const { state, closeModal } = useModal(); const { isMobile } = useScreenDimension(); const { t } = useTranslation(); @@ -34,7 +35,10 @@ export const StatusModal = () => { }; return ( - + Status icon { WebkitBoxOrient: "vertical", WebkitLineClamp: 8, wordBreak: "break-word", + whiteSpace: "pre-line", }} > {state?.message}{" "} @@ -125,4 +130,4 @@ export const StatusModal = () => { )} ); -}; +}); diff --git a/govtool/frontend/src/components/organisms/Modal/VotingPowerModal.tsx b/govtool/frontend/src/components/organisms/Modal/VotingPowerModal.tsx index 95c03b718..524f9bda4 100644 --- a/govtool/frontend/src/components/organisms/Modal/VotingPowerModal.tsx +++ b/govtool/frontend/src/components/organisms/Modal/VotingPowerModal.tsx @@ -1,3 +1,4 @@ +import { forwardRef } from "react"; import { Box } from "@mui/material"; import { ModalContents, ModalWrapper, Typography, VotePill } from "@atoms"; @@ -13,7 +14,7 @@ export interface VotingPowerModalState { vote?: string; } -export const VotingPowerModal = () => { +export const VotingPowerModal = forwardRef((_, ref) => { const { state } = useModal(); const { isMobile } = useScreenDimension(); const { t } = useTranslation(); @@ -28,6 +29,7 @@ export const VotingPowerModal = () => { @@ -94,4 +96,4 @@ export const VotingPowerModal = () => { ); -}; +}); diff --git a/govtool/frontend/src/components/organisms/PDFWrapper.tsx b/govtool/frontend/src/components/organisms/PDFWrapper.tsx index 5571c632d..310bc889d 100644 --- a/govtool/frontend/src/components/organisms/PDFWrapper.tsx +++ b/govtool/frontend/src/components/organisms/PDFWrapper.tsx @@ -1,11 +1,15 @@ -import React, { Suspense } from "react"; +import React, { ComponentProps, Suspense } from "react"; import { Box, CircularProgress } from "@mui/material"; import "@intersect.mbo/pdf-ui/style"; import { useCardano, useGovernanceActions } from "@/context"; +import { useValidateMutation } from "@/hooks/mutations"; -const PDF = React.lazy(() => import("@intersect.mbo/pdf-ui/cjs")); +const ProposalDiscussion = React.lazy( + () => import("@intersect.mbo/pdf-ui/cjs"), +); export const PDFWrapper = () => { + const { validateMetadata } = useValidateMutation(); const { walletApi, ...context } = useCardano(); const { createGovernanceActionJsonLD, createHash } = useGovernanceActions(); @@ -33,7 +37,7 @@ export const PDFWrapper = () => { } > - { createHash, }} pathname={window.location.pathname} + validateMetadata={ + validateMetadata as ComponentProps< + typeof ProposalDiscussion + >["validateMetadata"] + } /> diff --git a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx index 36e6459ac..f04378102 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDRepSteps/WhatRetirementMeans.tsx @@ -1,4 +1,5 @@ -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; +import * as Sentry from "@sentry/react"; import { Typography } from "@atoms"; import { useCardano, useModal } from "@context"; @@ -34,6 +35,10 @@ export const WhatRetirementMeans = ({ closeModal(); }; + useEffect(() => { + Sentry.setTag("component_name", "WhatRetirementMeans"); + }, []); + const retireAsDrep = useCallback(async () => { try { setIsRetirementLoading(true); @@ -65,6 +70,7 @@ export const WhatRetirementMeans = ({ } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { + Sentry.captureException(error); openWalletErrorModal({ error, onSumbit: onClickCancel, diff --git a/govtool/frontend/src/components/organisms/RegisterAsDirectVoterBox.tsx b/govtool/frontend/src/components/organisms/RegisterAsDirectVoterBox.tsx index dc5e3a78d..05922d965 100644 --- a/govtool/frontend/src/components/organisms/RegisterAsDirectVoterBox.tsx +++ b/govtool/frontend/src/components/organisms/RegisterAsDirectVoterBox.tsx @@ -1,6 +1,7 @@ -import { useCallback, useState } from "react"; +import { useCallback, useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; +import * as Sentry from "@sentry/react"; import { PATHS } from "@consts"; import { RegisterAsDirectVoterBoxContent } from "@organisms"; @@ -19,6 +20,10 @@ export const RegisterAsDirectVoterBox = () => { const { voter } = useGetVoterInfo(); const openWalletErrorModal = useWalletErrorModal(); + useEffect(() => { + Sentry.setTag("component_name", "RegisterAsDirectVoterBox"); + }, []); + const onRegister = useCallback(async () => { setIsLoading(true); @@ -49,6 +54,7 @@ export const RegisterAsDirectVoterBox = () => { } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { + Sentry.captureException(error); openWalletErrorModal({ error, buttonText: t("modals.common.goToDashboard"), diff --git a/govtool/frontend/src/consts/navItems.tsx b/govtool/frontend/src/consts/navItems.tsx index 5b6ec10f0..acd4fb2b2 100644 --- a/govtool/frontend/src/consts/navItems.tsx +++ b/govtool/frontend/src/consts/navItems.tsx @@ -64,7 +64,7 @@ export const CONNECTED_NAV_ITEMS = [ { dataTestId: "proposal-discussion-link", label: i18n.t("proposalDiscussion.title"), - navTo: `${PATHS.connectedProposalPillar.replace("/*", "")}${ + navTo: `${PATHS.proposalPillar.replace("/*", "")}${ PDF_PATHS.proposalDiscussion }`, activeIcon: ( diff --git a/govtool/frontend/src/consts/paths.ts b/govtool/frontend/src/consts/paths.ts index d7e1973d1..3c16566ca 100644 --- a/govtool/frontend/src/consts/paths.ts +++ b/govtool/frontend/src/consts/paths.ts @@ -25,7 +25,6 @@ export const PATHS = { retireAsDirectVoter: "/retire_direct_voter", stakeKeys: "/stake_keys", proposalPillar: "/proposal_pillar/*", - connectedProposalPillar: "/connected/proposal_pillar/*", }; export const PDF_PATHS = { diff --git a/govtool/frontend/src/context/getUtxos.ts b/govtool/frontend/src/context/getUtxos.ts index 6888846a8..615877691 100644 --- a/govtool/frontend/src/context/getUtxos.ts +++ b/govtool/frontend/src/context/getUtxos.ts @@ -13,7 +13,7 @@ type Utxos = { }[]; export const getUtxos = async ( - enabledApi: CardanoApiWallet + enabledApi: CardanoApiWallet, ): Promise => { const utxos = []; @@ -24,7 +24,7 @@ export const getUtxos = async ( // eslint-disable-next-line no-restricted-syntax for (const rawUtxo of rawUtxos) { const utxo = TransactionUnspentOutput.from_bytes( - Buffer.from(rawUtxo, "hex") + Buffer.from(rawUtxo, "hex"), ); const input = utxo.input(); const txid = input.transaction_id().to_hex(); @@ -54,7 +54,7 @@ export const getUtxos = async ( // @ts-ignore const assetNameHex = Buffer.from( assetName.name(), - "utf8" + "utf8", ).toString("hex"); const multiassetAmt = multiasset.get_asset(policyId, assetName); multiAssetStr += `+ ${multiassetAmt.to_str()} + ${policyIdHex}.${assetNameHex} (${assetNameString})`; @@ -76,6 +76,7 @@ export const getUtxos = async ( return utxos; } catch (err) { + Sentry.setTag("util", "getUtxos"); Sentry.captureException(err); console.error(err); } diff --git a/govtool/frontend/src/context/governanceAction.tsx b/govtool/frontend/src/context/governanceAction.tsx index c03cd9460..a1f1066e2 100644 --- a/govtool/frontend/src/context/governanceAction.tsx +++ b/govtool/frontend/src/context/governanceAction.tsx @@ -4,9 +4,11 @@ import { createContext, useContext, useCallback, + useEffect, } from "react"; import { NodeObject } from "jsonld"; import { blake2bHex } from "blakejs"; +import * as Sentry from "@sentry/react"; import { CIP_108, GOVERNANCE_ACTION_CONTEXT } from "@/consts"; import { canonizeJSON, generateJsonld, generateMetadataBody } from "@/utils"; @@ -40,6 +42,11 @@ const GovernanceActionProvider = ({ children }: PropsWithChildren) => { * @param {GovActionMetadata} govActionMetadata - The metadata of the governance action. * @returns The JSON-LD representation of the governance action. */ + + useEffect(() => { + Sentry.setTag("component_name", "GovernanceActionProvider"); + }, []); + const createGovernanceActionJsonLD = useCallback( async (govActionMetadata: GovActionMetadata) => { try { @@ -55,6 +62,7 @@ const GovernanceActionProvider = ({ children }: PropsWithChildren) => { ); return jsonLD; } catch (error) { + Sentry.captureException(error); console.error(error); } }, @@ -72,6 +80,7 @@ const GovernanceActionProvider = ({ children }: PropsWithChildren) => { const canonizedJsonHash = blake2bHex(canonizedJson, undefined, 32); return canonizedJsonHash; } catch (error) { + Sentry.captureException(error); console.error(error); } }, []); diff --git a/govtool/frontend/src/context/pendingTransaction/utils.tsx b/govtool/frontend/src/context/pendingTransaction/utils.tsx index ffa97b22a..1d3e765dd 100644 --- a/govtool/frontend/src/context/pendingTransaction/utils.tsx +++ b/govtool/frontend/src/context/pendingTransaction/utils.tsx @@ -70,13 +70,13 @@ export const refetchData = async ( resourceId === AutomatedVotingOptionDelegationId.no_confidence || resourceId === AutomatedVotingOptionDelegationId.abstain ) { - return data.dRepView; + return data?.dRepView; } - return data.dRepHash; + return data?.dRepHash; } if (type === "registerAsDrep" || type === "retireAsDrep") - return data.isRegisteredAsDRep; + return data?.isRegisteredAsDRep; if (type === "registerAsDirectVoter" || type === "retireAsDirectVoter") - return data.isRegisteredAsSoleVoter; + return data?.isRegisteredAsSoleVoter; return undefined; }; diff --git a/govtool/frontend/src/context/wallet.tsx b/govtool/frontend/src/context/wallet.tsx index 1ea70f80c..3251c0ae9 100644 --- a/govtool/frontend/src/context/wallet.tsx +++ b/govtool/frontend/src/context/wallet.tsx @@ -208,6 +208,7 @@ const CardanoProvider = (props: Props) => { // return changeAddress for the usage of the pillars; return changeAddress; } catch (err) { + Sentry.setTag("wallet-action", "getChangeAddress"); Sentry.captureException(err); console.error(err); } @@ -217,11 +218,13 @@ const CardanoProvider = (props: Props) => { try { const raw = await enabledApi.getUsedAddresses(); const rawFirst = raw[0]; + if (!rawFirst) return []; const usedAddress = Address.from_bytes( Buffer.from(rawFirst, "hex"), ).to_bech32(); setWalletState((prev) => ({ ...prev, usedAddress })); } catch (err) { + Sentry.setTag("wallet-action", "getUsedAddresses"); Sentry.captureException(err); console.error(err); } @@ -250,8 +253,22 @@ const CardanoProvider = (props: Props) => { .enable({ extensions: [{ cip: 95 }], }) + .then((enabledWalletApi) => { + Sentry.addBreadcrumb({ + category: "wallet", + message: "Wallet connected", + level: "info", + data: window.cardano[walletName], + }); + return enabledWalletApi; + }) .catch((e) => { - Sentry.captureException(e); + Sentry.addBreadcrumb({ + category: "wallet", + message: "Wallet connection failed", + level: "warning", + }); + Sentry.captureException(e, { data: window.cardano[walletName] }); throw e.info; }); await getChangeAddress(enabledApi); @@ -351,6 +368,7 @@ const CardanoProvider = (props: Props) => { return { status: t("ok"), stakeKey: stakeKeySet }; } catch (e) { + Sentry.setTag("wallet-action", "enable"); Sentry.captureException(e); console.error(e); setError(`${e}`); @@ -381,6 +399,12 @@ const CardanoProvider = (props: Props) => { setAddress(undefined); setStakeKey(undefined); setIsEnabled(false); + + Sentry.addBreadcrumb({ + category: "wallet", + message: "Wallet disconnected", + level: "info", + }); }, []); // Create transaction builder @@ -561,6 +585,7 @@ const CardanoProvider = (props: Props) => { disconnectWallet(); } + Sentry.setTag("wallet-action", "buildSignSubmitConwayCertTx"); Sentry.captureException(error); console.error(error, "error"); throw error?.info ?? error; @@ -607,6 +632,7 @@ const CardanoProvider = (props: Props) => { return certBuilder; } catch (e) { + Sentry.setTag("wallet-action", "buildVoteDelegationCert"); Sentry.captureException(e); console.error(e); throw e; @@ -645,6 +671,7 @@ const CardanoProvider = (props: Props) => { } return Certificate.new_drep_registration(dRepRegCert); } catch (e) { + Sentry.setTag("wallet-action", "buildDRepRegCert"); Sentry.captureException(e); console.error(e); throw e; @@ -674,6 +701,7 @@ const CardanoProvider = (props: Props) => { } return Certificate.new_drep_update(dRepUpdateCert); } catch (e) { + Sentry.setTag("wallet-action", "buildDRepUpdateCert"); Sentry.captureException(e); console.error(e); throw e; @@ -696,6 +724,7 @@ const CardanoProvider = (props: Props) => { return Certificate.new_drep_deregistration(dRepRetirementCert); } catch (e) { + Sentry.setTag("wallet-action", "buildDRepRetirementCert"); Sentry.captureException(e); console.error(e); throw e; @@ -749,6 +778,7 @@ const CardanoProvider = (props: Props) => { return votingBuilder; } catch (e) { + Sentry.setTag("wallet-action", "buildVote"); Sentry.captureException(e); console.error(e); throw e; @@ -932,26 +962,17 @@ function useCardano() { status: "info", dataTestId: "info-about-sancho-net-modal", message: ( -

- {t("system.sanchoNetIsBeta")} - openInNewTab("https://sancho.network/")} - sx={{ cursor: "pointer" }} - > - {t("system.sanchoNet")} - - . -
-
- , - ]} - /> -

+ openInNewTab("https://sancho.network/")} + sx={{ cursor: "pointer" }} + />, + ]} + /> ), - title: t("system.toolConnectedToSanchonet"), + title: t("system.testnetTitle"), buttonText: t("ok"), }, }); @@ -962,6 +983,7 @@ function useCardano() { // TODO: type error // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (e: any) { + Sentry.setTag("wallet-action", "enable"); Sentry.captureException(e); await context.disconnectWallet(); navigate(PATHS.home); diff --git a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts index 7406665b0..9fdaae96f 100644 --- a/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts +++ b/govtool/frontend/src/hooks/forms/useCreateGovernanceActionForm.ts @@ -2,7 +2,7 @@ import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useFormContext } from "react-hook-form"; import { blake2bHex } from "blakejs"; -import { captureException } from "@sentry/react"; +import * as Sentry from "@sentry/react"; import { useTranslation } from "react-i18next"; import { NodeObject } from "jsonld"; @@ -156,7 +156,8 @@ export const useCreateGovernanceActionForm = ( throw new Error(t("errors.invalidGovernanceActionType")); } } catch (error) { - captureException(error); + Sentry.setTag("hook", "useCreateGovernanceActionForm"); + Sentry.captureException(error); } }, [hash], @@ -248,7 +249,8 @@ export const useCreateGovernanceActionForm = ( : undefined, dataTestId: "create-governance-action-error-modal", }); - captureException(error); + Sentry.setTag("hook", "useCreateGovernanceActionForm"); + Sentry.captureException(error); } } finally { setIsLoading(false); diff --git a/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx b/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx index 7de68755c..720aac3b7 100644 --- a/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useDelegateTodRepForm.tsx @@ -1,6 +1,7 @@ import { useCallback, useState } from "react"; import { useNavigate } from "react-router-dom"; import { useForm, useWatch } from "react-hook-form"; +import * as Sentry from "@sentry/react"; import { PATHS } from "@consts"; import { useCardano, useModal } from "@context"; @@ -72,6 +73,8 @@ export const useDelegateTodRepForm = () => { }); } } catch (error) { + Sentry.setTag("hook", "useDelegateTodRepForm"); + Sentry.captureException(error); openModal({ type: "statusModal", state: { diff --git a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts index a01bfa121..3ea4e1872 100644 --- a/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts +++ b/govtool/frontend/src/hooks/forms/useEditDRepInfoForm.ts @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { useFormContext } from "react-hook-form"; import { blake2bHex } from "blakejs"; -import { captureException } from "@sentry/react"; +import * as Sentry from "@sentry/react"; import { NodeObject } from "jsonld"; import { @@ -77,6 +77,7 @@ export const useEditDRepInfoForm = ( // Navigation const backToForm = useCallback(() => { + window.scrollTo(0, 0); setStep?.(1); closeModal(); }, [setStep]); @@ -177,7 +178,8 @@ export const useEditDRepInfoForm = ( }, }); } else { - captureException(error); + Sentry.setTag("hook", "useEditDRepInfoForm"); + Sentry.captureException(error); openWalletErrorModal({ error, diff --git a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx index e7724c407..39c590d95 100644 --- a/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx +++ b/govtool/frontend/src/hooks/forms/useRegisterAsdRepForm.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { useFormContext } from "react-hook-form"; import { blake2bHex } from "blakejs"; -import { captureException } from "@sentry/react"; +import * as Sentry from "@sentry/react"; import { NodeObject } from "jsonld"; import { @@ -136,7 +136,8 @@ export const useRegisterAsdRepForm = ( return certBuilder; } catch (error) { - captureException(error); + Sentry.setTag("hook", "useRegisterAsdRepForm"); + Sentry.captureException(error); throw error; } }, @@ -212,7 +213,8 @@ export const useRegisterAsdRepForm = ( }, }); } else { - captureException(error); + Sentry.setTag("hook", "useRegisterAsdRepForm"); + Sentry.captureException(error); openWalletErrorModal({ error, diff --git a/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx b/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx index b554413a4..b23a369f4 100644 --- a/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx +++ b/govtool/frontend/src/hooks/forms/useVoteActionForm.tsx @@ -2,7 +2,8 @@ import { useCallback, useMemo, useState } from "react"; import { useForm, useWatch } from "react-hook-form"; import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; -import { useLocation, useNavigate } from "react-router-dom"; +import { useLocation, useNavigate, useParams } from "react-router-dom"; +import * as Sentry from "@sentry/react"; import { PATHS } from "@consts"; import { useCardano, useSnackbar } from "@context"; @@ -37,7 +38,8 @@ export const useVoteActionForm = ( useCardano(); const { addSuccessAlert } = useSnackbar(); const navigate = useNavigate(); - const { state } = useLocation(); + const { state, hash } = useLocation(); + const params = useParams(); const openWalletErrorModal = useWalletErrorModal(); const { @@ -62,21 +64,23 @@ export const useVoteActionForm = ( const urlSubmitValue = voteContextUrl ?? ""; const hashSubmitValue = voteContextHash ?? ""; + const txHash = state?.txHash ?? params.proposalId; + const index = state?.index ?? hash.replace("#", ""); try { const isPendingTx = isPendingTransaction(); if (isPendingTx) return; const votingBuilder = await buildVote( vote, - state.txHash, - state.index, + txHash, + index, urlSubmitValue, hashSubmitValue, ); const result = await buildSignSubmitConwayCertTx({ votingBuilder, type: "vote", - resourceId: state.txHash + state.index, + resourceId: txHash + index, }); if (result) { addSuccessAlert("Vote submitted"); @@ -87,6 +91,8 @@ export const useVoteActionForm = ( }); } } catch (error) { + Sentry.setTag("hook", "useVoteActionForm"); + Sentry.captureException(error); openWalletErrorModal({ error, dataTestId: "vote-transaction-error-modal", @@ -95,7 +101,7 @@ export const useVoteActionForm = ( setIsLoading(false); } }, - [state, buildVote, buildSignSubmitConwayCertTx], + [state, buildVote, buildSignSubmitConwayCertTx, params, hash], ); return { diff --git a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx index d0afc7ea6..e94e80a79 100644 --- a/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx +++ b/govtool/frontend/src/hooks/forms/useVoteContextForm.tsx @@ -1,7 +1,7 @@ import { Dispatch, SetStateAction, useCallback, useState } from "react"; import { useFormContext } from "react-hook-form"; import { blake2bHex } from "blakejs"; -import { captureException } from "@sentry/react"; +import * as Sentry from "@sentry/react"; import { downloadTextFile } from "@utils"; import { MetadataValidationStatus } from "@models"; @@ -85,7 +85,8 @@ export const useVoteContextForm = ( await validateHash(data.storingURL, hash); // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { - captureException(error); + Sentry.setTag("hook", "useVoteContextForm"); + Sentry.captureException(error); } finally { if (setSavedHash) setSavedHash(hash); if (setStep) setStep(4); diff --git a/govtool/frontend/src/hooks/useDelegateToDrep.ts b/govtool/frontend/src/hooks/useDelegateToDrep.ts index d86655a6a..963c782ec 100644 --- a/govtool/frontend/src/hooks/useDelegateToDrep.ts +++ b/govtool/frontend/src/hooks/useDelegateToDrep.ts @@ -44,6 +44,7 @@ export const useDelegateTodRep = () => { error, dataTestId: "delegate-transaction-error-modal", }); + Sentry.setTag("hook", "useDelegateTodRep"); Sentry.captureException(error); } finally { setIsDelegating(null); diff --git a/govtool/frontend/src/i18n/locales/en.ts b/govtool/frontend/src/i18n/locales/en.ts index 9e2b2ddf6..809815140 100644 --- a/govtool/frontend/src/i18n/locales/en.ts +++ b/govtool/frontend/src/i18n/locales/en.ts @@ -385,7 +385,7 @@ export const en = { }, }, proposalDiscussion: { - title: "Proposal Discussion", + title: "Proposals", proposeAGovernanceAction: "Propose a Governance Action", }, govActions: { @@ -712,12 +712,9 @@ export const en = { retireDirectVoter: "Retire as a Direct Voter", }, system: { - sanchoNet: "SanchoNet", - sanchoNetIsBeta: - "The SanchoNet GovTool is currently in beta and it connects to ", - testAdaNote: - "Please note, this tool uses ‘Test ada’ <0>NOT real ada. All governance actions and related terms pertain to SanchoNet.", - toolConnectedToSanchonet: "This tool is connected to SanchoNet", + testnetDescription: + "The SanchoNet GovTool is currently in beta and it connects to <0>SanchoNet.\n\n Please note, this tool uses ‘Test ada’ NOT real ada. All governance actions and related terms pertain to SanchoNet.", + testnetTitle: "This tool is connected to SanchoNet", }, tooltips: { delegateTodRep: { diff --git a/govtool/frontend/src/main.tsx b/govtool/frontend/src/main.tsx index 641d5f102..6a39a1d3a 100644 --- a/govtool/frontend/src/main.tsx +++ b/govtool/frontend/src/main.tsx @@ -18,21 +18,11 @@ import { ContextProviders, UsersnapProvider } from "@context"; import App from "./App.tsx"; import { theme } from "./theme.ts"; import "./i18n"; +import pkg from "../package.json"; -const queryClient = new QueryClient(); - -interface SentryEventDataLayer { - event: string; - sentryEventId: string; - sentryErrorMessage?: JSONValue; -} +const { version } = pkg; -// TODO: Move to types -declare global { - interface Window { - dataLayer: SentryEventDataLayer[]; - } -} +const queryClient = new QueryClient(); const tagManagerArgs = { gtmId: import.meta.env.VITE_GTM_ID, @@ -43,6 +33,7 @@ TagManager.initialize(tagManagerArgs); Sentry.init({ dsn: import.meta.env.VITE_SENTRY_DSN, environment: import.meta.env.VITE_APP_ENV, + release: version, integrations: [ new Sentry.BrowserTracing({ routingInstrumentation: Sentry.reactRouterV6Instrumentation( diff --git a/govtool/frontend/src/pages/EditDRepMetadata.tsx b/govtool/frontend/src/pages/EditDRepMetadata.tsx index de3736d6a..15639360c 100644 --- a/govtool/frontend/src/pages/EditDRepMetadata.tsx +++ b/govtool/frontend/src/pages/EditDRepMetadata.tsx @@ -24,6 +24,7 @@ import { checkIsWalletConnected } from "@utils"; export const EditDRepMetadata = () => { const [step, setStep] = useState(1); + const [loadUserData, setLoadUserData] = useState(true); const { isMobile } = useScreenDimension(); const navigate = useNavigate(); const { t } = useTranslation(); @@ -95,6 +96,8 @@ export const EditDRepMetadata = () => { )} {step === 2 && } diff --git a/govtool/frontend/src/types/@intersect.mbo.d.ts b/govtool/frontend/src/types/@intersect.mbo.d.ts index 3c9ca7e21..df61e1eae 100644 --- a/govtool/frontend/src/types/@intersect.mbo.d.ts +++ b/govtool/frontend/src/types/@intersect.mbo.d.ts @@ -1,10 +1,32 @@ -type PDFProps = { +enum MetadataValidationStatus { + URL_NOT_FOUND = "URL_NOT_FOUND", + INVALID_JSONLD = "INVALID_JSONLD", + INVALID_HASH = "INVALID_HASH", + INCORRECT_FORMAT = "INCORRECT_FORMAT", +} + +type ProposalDiscussionProps = { // eslint-disable-next-line @typescript-eslint/no-explicit-any walletAPI: any; pathname: string; locale?: string; + validateMetadata: ({ + url, + hash, + standard, + }: { + url: string; + hash: string; + standard: "CIP108"; + }) => Promise< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + | { status?: MetadataValidationStatus; metadata?: any; valid: boolean } + | undefined + >; }; declare module "@intersect.mbo/pdf-ui/cjs" { - export default function PDF(props: PDFProps): JSX.Element; + export default function ProposalDiscussion( + props: ProposalDiscussionProps, + ): JSX.Element; } diff --git a/govtool/frontend/src/types/global.d.ts b/govtool/frontend/src/types/global.d.ts index 0e91e6dcd..f4c1c3b93 100644 --- a/govtool/frontend/src/types/global.d.ts +++ b/govtool/frontend/src/types/global.d.ts @@ -1,6 +1,15 @@ export {}; +interface SentryEventDataLayer { + event: string; + sentryEventId: string; + sentryErrorMessage?: JSONValue; +} declare global { + interface Window { + dataLayer: SentryEventDataLayer[]; + } + type VoteType = "yes" | "no" | "abstain"; type ActionTypeFromAPI = { diff --git a/govtool/frontend/src/utils/tests/getDRepID.test.ts b/govtool/frontend/src/utils/tests/getDRepID.test.ts index 61bc5470c..d533d4a40 100644 --- a/govtool/frontend/src/utils/tests/getDRepID.test.ts +++ b/govtool/frontend/src/utils/tests/getDRepID.test.ts @@ -43,15 +43,18 @@ describe("getPubDRepID function", () => { }); it("returns undefined values for dRepKey, dRepID, and dRepIDBech32 when walletApi throws an error", async () => { - mockGetPubDRepKey.mockRejectedValueOnce( - new Error("Failed to get PubDRepKey"), - ); + const failedToGetPubDRepKeyError = new Error("Failed to get PubDRepKey"); + mockGetPubDRepKey.mockRejectedValueOnce(failedToGetPubDRepKeyError); + + const consoleErrorSpy = vi.spyOn(console, "error"); const result = await getPubDRepID(mockWalletApi); + expect(result).toEqual({ dRepKey: undefined, dRepID: undefined, dRepIDBech32: undefined, }); - expect(mockGetPubDRepKey).toHaveBeenCalled(); + + expect(consoleErrorSpy).toHaveBeenCalledWith(failedToGetPubDRepKeyError); }); }); diff --git a/govtool/frontend/storybook.Dockerfile b/govtool/frontend/storybook.Dockerfile index 599f88e16..183ed82ec 100644 --- a/govtool/frontend/storybook.Dockerfile +++ b/govtool/frontend/storybook.Dockerfile @@ -18,7 +18,7 @@ WORKDIR /src COPY --from=deps /src/node_modules ./node_modules COPY . . -RUN npm run build-storybook --quiet +RUN npm run build:storybook --quiet FROM nginx:stable-alpine EXPOSE 80 diff --git a/govtool/frontend/vite.config.ts b/govtool/frontend/vite.config.ts index 5ad7053a3..877fbe5a3 100644 --- a/govtool/frontend/vite.config.ts +++ b/govtool/frontend/vite.config.ts @@ -81,6 +81,13 @@ const vitestConfig = defineVitestConfig({ test: { setupFiles: "./src/setupTests.ts", globals: true, + pool: "forks", + poolOptions: { + threads: { + minThreads: 2, + }, + }, + maxConcurrency: 4, environment: "jsdom", reporters: ["default", "junit"], outputFile: { diff --git a/scripts/govtool/config/templates/docker-compose.yml.tpl b/scripts/govtool/config/templates/docker-compose.yml.tpl index 5882aef20..3c56a1064 100644 --- a/scripts/govtool/config/templates/docker-compose.yml.tpl +++ b/scripts/govtool/config/templates/docker-compose.yml.tpl @@ -273,7 +273,7 @@ services: logging: *logging labels: - "traefik.enable=true" - - "traefik.http.middlewares.frontend-csp.headers.contentSecurityPolicy=default-src 'self'; img-src *.usersnap.com https://www.googletagmanager.com 'self' data:; script-src *.usersnap.com 'self' https://www.googletagmanager.com https://browser.sentry-cdn.com; style-src *.usersnap.com *.googleapis.com 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src *.usersnap.com https://s3.eu-central-1.amazonaws.com/upload.usersnap.com 'self' o4506155985141760.ingest.sentry.io *.google-analytics.com *.api.pdf.gov.tools; font-src *.usersnap.com *.gstatic.com 'self' https://fonts.gstatic.com data:; worker-src blob:" + - "traefik.http.middlewares.frontend-csp.headers.contentSecurityPolicy=default-src 'self'; img-src *.usersnap.com https://www.googletagmanager.com 'self' data:; script-src *.usersnap.com 'self' https://www.googletagmanager.com https://browser.sentry-cdn.com; style-src *.usersnap.com *.googleapis.com 'self' 'unsafe-inline' https://fonts.googleapis.com; connect-src *.usersnap.com https://s3.eu-central-1.amazonaws.com/upload.usersnap.com 'self' *.ingest.sentry.io *.google-analytics.com *.api.pdf.gov.tools; font-src *.usersnap.com *.gstatic.com 'self' https://fonts.gstatic.com data:; worker-src blob:" - "traefik.http.routers.to-frontend.rule=Host(``)" - "traefik.http.routers.to-frontend.entrypoints=websecure" - "traefik.http.routers.to-frontend.tls.certresolver=myresolver" diff --git a/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts b/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts new file mode 100644 index 000000000..3f6c27f0d --- /dev/null +++ b/tests/govtool-frontend/playwright/lib/constants/docsUrl.ts @@ -0,0 +1,11 @@ +import environments from "./environments"; + +export const DELEGATION_DOC_URL = `${environments.docsUrl}/faqs/ways-to-use-your-voting-power`; +export const REGISTER_DREP_DOC_URL = `${environments.docsUrl}/faqs/what-does-it-mean-to-register-as-a-drep`; +export const DIRECT_VOTER_DOC_URL = `${environments.docsUrl}/how-to-use-the-govtool/using-govtool/direct-voting`; +export const GOVERNANCE_ACTION_DOC_URL = `${environments.docsUrl}/how-to-use-the-govtool/using-govtool/governance-actions/governance-actions-how-to-vote`; +export const PROPOSE_GOVERNANCE_ACTION_DOC_URL = `${environments.docsUrl}/how-to-use-the-govtool/using-govtool/governance-actions`; +export const ABSTAIN_VOTE_DOC_URL = `${environments.docsUrl}/how-to-use-the-govtool/using-govtool/delegating/abstain-from-every-vote`; +export const SIGNAL_NO_CONFIDENCE_VOTE_DOC_URL = `${environments.docsUrl}/how-to-use-the-govtool/using-govtool/delegating/signal-no-confidence-on-every-vote`; +export const FAQS_DOC_URL = `${environments.docsUrl}/faqs`; +export const GUIDES_DOC_URL = `${environments.docsUrl}/about/what-is-sanchonet-govtool`; diff --git a/tests/govtool-frontend/playwright/lib/helpers/enum.ts b/tests/govtool-frontend/playwright/lib/helpers/enum.ts new file mode 100644 index 000000000..7cfed0143 --- /dev/null +++ b/tests/govtool-frontend/playwright/lib/helpers/enum.ts @@ -0,0 +1,6 @@ +export function getEnumKeyByValue( + enumObj: any, + value: string +): string | undefined { + return Object.keys(enumObj).find((key) => enumObj[key] === value); +} diff --git a/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts b/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts index 9b07aa783..bff4c432e 100644 --- a/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/dRepDirectoryPage.ts @@ -19,19 +19,17 @@ export default class DRepDirectoryPage { "signal-no-confidence-on-every-vote-info-button" ); - readonly automaticDelegationOptionsDropdown = this.page.getByRole("button", { - name: "Automated Voting Options arrow", - }); // BUG: testId -> delegation-options-dropdown + readonly automaticDelegationOptionsDropdown = this.page.getByTestId( + "automated-voting-options-accordion" + ); readonly delegateToDRepCard = this.page.getByTestId("delegate-to-drep-card"); - readonly signalNoConfidenceCard = this.page - .getByRole("region") - .locator("div") - .filter({ hasText: "Signal No Confidence on Every" }) - .nth(2); // BUG: testId -> signal-no-confidence-card - readonly abstainDelegationCard = this.page.getByText( - "Abstain from Every VoteSelect this to vote ABSTAIN to every vote.Voting Power₳" - ); // BUG: testId -> abstain-delegation-card + readonly signalNoConfidenceCard = this.page.getByTestId( + "no-confidence-delegation-card" + ); + readonly abstainDelegationCard = this.page.getByTestId( + "abstain-delegation-card" + ); readonly delegationErrorModal = this.page.getByTestId( "delegate-transaction-error-modal" diff --git a/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts b/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts index 128a80e1a..90de67800 100644 --- a/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/dRepRegistrationPage.ts @@ -5,9 +5,11 @@ import DRepForm from "../forms/dRepForm"; export default class DRepRegistrationPage extends DRepForm { readonly registerBtn = this.page.getByTestId("register-button"); readonly confirmBtn = this.page.getByTestId("confirm-modal-button"); + readonly registrationSuccessModal = this.page.getByTestId( "governance-action-submitted-modal" ); + readonly metadataErrorModal = this.page.getByTestId("modal"); constructor(private readonly page: Page) { super(page); diff --git a/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts b/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts index 6cebc20fd..265b00d82 100644 --- a/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/governanceActionDetailsPage.ts @@ -19,9 +19,7 @@ export default class GovernanceActionDetailsPage { readonly externalModalBtn = this.page.getByTestId("external-modal-button"); readonly governanceActionId = this.page.getByText("Governance Action ID:"); - readonly contextBtn = this.page.getByRole("button", { - name: "Provide context about your", - }); // BUG testId + readonly contextBtn = this.page.getByTestId("provide-context-button"); readonly viewOtherDetailsLink = this.page.getByTestId( "view-other-details-button" ); @@ -59,14 +57,14 @@ export default class GovernanceActionDetailsPage { this.page .getByRole("button", { name: "download Vote_Context.jsonld" }) - .click(); + .click(); // BUG missing test id const voteMetadata = await this.downloadVoteMetadata(); const url = await metadataBucketService.uploadMetadata( voteMetadata.name, voteMetadata.data ); - await this.page.getByPlaceholder("URL").fill(url); + await this.page.getByPlaceholder("URL").fill(url); // Bug showing data-testid="undefinedinput" on url await this.confirmModalBtn.click(); await this.page.getByTestId("go-to-vote-modal-button").click(); } diff --git a/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts b/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts index 12d5bbbd3..28694a3f2 100644 --- a/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts +++ b/tests/govtool-frontend/playwright/lib/pages/governanceActionsPage.ts @@ -3,6 +3,7 @@ import { Locator, Page, expect } from "@playwright/test"; import { FilterOption, IProposal } from "@types"; import environments from "lib/constants/environments"; import GovernanceActionDetailsPage from "./governanceActionDetailsPage"; +import { getEnumKeyByValue } from "@helpers/enum"; const MAX_SLIDES_DISPLAY_PER_TYPE = 6; @@ -135,8 +136,13 @@ export default class GovernanceActionsPage { // Frontend validation for (let dIdx = 0; dIdx <= proposalsByType.length - 1; dIdx++) { const proposals = proposalsByType[0] as IProposal[]; + const filterOptionKey = getEnumKeyByValue( + FilterOption, + proposals[0].type + ); + const slides = await this.page - .locator(`[data-testid="govaction-${proposals[0].type}-card"]`) + .locator(`[data-testid="govaction-${filterOptionKey}-card"]`) .all(); const actualSlidesInDisplay = diff --git a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts index 572d0f788..13434f4aa 100644 --- a/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/2-delegation/delegation.drep.spec.ts @@ -66,11 +66,7 @@ test("2N. Should show DRep information on details page", async ({ await dRepRegistrationPage.confirmBtn.click(); - const dRepDirectory = new DRepDirectoryPage(dRepPage); - await dRepDirectory.goto(); - - await dRepDirectory.searchInput.fill(wallet.dRepId); - await dRepPage.getByTestId(`${wallet.dRepId}-view-details-button`).click(); + await dRepPage.getByTestId("view-drep-details-button").click(); // Verification await expect(dRepPage.getByTestId("copy-drep-id-button")).toHaveText( 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 10f0601cb..0c194a69b 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 @@ -27,9 +27,9 @@ test.describe("Logged in DReps", () => { await expect(page.getByTestId("voting-power-chips")).toBeVisible(); - await expect(page.getByTestId("dRep-id-display")).toContainText( - dRep01Wallet.dRepId - ); // BUG: testId -> dRep-id-display-dashboard (It is taking sidebar dRep-id) + await expect( + page.getByTestId("dRep-id-display-card-dashboard") + ).toContainText(dRep01Wallet.dRepId); await page.goto(`${environments.frontendUrl}/governance_actions`); await page @@ -48,13 +48,17 @@ test.describe("Logged in DReps", () => { await page.getByTestId("view-drep-details-button").click(); await page.getByTestId("edit-drep-data-button").click(); - const newDRepName = faker.internet.userName(); - await page.getByPlaceholder("ex. JohnDRep").fill(newDRepName); + const newDRepName = faker.person.firstName(); + await page.getByTestId("name-input").fill(newDRepName); + await page.getByTestId("email-input").fill(faker.internet.email()); + await page.getByTestId("bio-input").fill(faker.lorem.paragraph(2)); + await page.getByTestId("continue-button").click(); await page.getByRole("checkbox").click(); await page.getByTestId("continue-button").click(); - page.getByRole("button", { name: `${newDRepName}.jsonld` }).click(); + page.getByRole("button", { name: `${newDRepName}.jsonld` }).click(); // BUG missing test ids + const download: Download = await page.waitForEvent("download"); const dRepMetadata = await downloadMetadata(download); @@ -63,7 +67,7 @@ test.describe("Logged in DReps", () => { dRepMetadata.data ); - await page.getByPlaceholder("URL").fill(url); + await page.getByTestId("metadata-url-input").fill(url); await page.getByTestId("continue-button").click(); // BUG -> incorrect test id await page.getByTestId("confirm-modal-button").click(); diff --git a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts index ddfcf7e44..73de7344d 100644 --- a/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/3-drep-registration/dRepRegistration.loggedin.spec.ts @@ -121,7 +121,9 @@ test.describe("Validation of dRep Registration Form", () => { metadataAnchorGreaterThan128Bytes ); - await expect(page.getByTestId("invalid-url-error")).toBeVisible(); + await expect( + page.getByTestId("url-must-be-less-than-128-bytes-error") + ).toBeVisible(); }); }); @@ -154,7 +156,7 @@ test("3O. Should reject invalid dRep registration metadata", async ({ await dRepRegistrationPage.metadataUrlInput.fill(invalidMetadataAnchor); await dRepRegistrationPage.registerBtn.click(); - await expect( - page.getByTestId("registration-transaction-error-modal") - ).not.toHaveText(/utxo balance insufficient/i); + await expect(dRepRegistrationPage.metadataErrorModal).toHaveText( + /your external data does not/i + ); }); diff --git a/tests/govtool-frontend/playwright/tests/3-drep-registration/editDRep.dRep.spec.ts b/tests/govtool-frontend/playwright/tests/3-drep-registration/editDRep.dRep.spec.ts index b5ff1317e..9482fedfc 100644 --- a/tests/govtool-frontend/playwright/tests/3-drep-registration/editDRep.dRep.spec.ts +++ b/tests/govtool-frontend/playwright/tests/3-drep-registration/editDRep.dRep.spec.ts @@ -19,6 +19,8 @@ test.describe("Validation of edit dRep Form", () => { const editDRepPage = new EditDRepPage(page); await editDRepPage.goto(); + await editDRepPage.addLinkBtn.click(); + for (let i = 0; i < 100; i++) { await editDRepPage.validateForm( faker.internet.displayName(), @@ -44,6 +46,8 @@ test.describe("Validation of edit dRep Form", () => { const editDRepPage = new EditDRepPage(page); await editDRepPage.goto(); + await editDRepPage.addLinkBtn.click(); + for (let i = 0; i < 100; i++) { await editDRepPage.inValidateForm( mockInvalid.name(), @@ -99,7 +103,9 @@ test.describe("Validation of edit dRep Form", () => { await editDRepPage.metadataUrlInput.fill(metadataAnchorGreaterThan128Bytes); - await expect(page.getByTestId("invalid-url-error")).toBeVisible(); + await expect( + page.getByTestId("url-must-be-less-than-128-bytes-error") + ).toBeVisible(); }); }); @@ -118,7 +124,7 @@ test("3P. Should reject invalid edit dRep metadata", async ({ page }) => { await editDRepPage.metadataUrlInput.fill(invalidMetadataAnchor); await editDRepPage.continueBtn.click(); - await expect( - page.getByTestId("registration-transaction-error-modal") - ).toBeVisible(); + await expect(page.getByTestId("modal")).toHaveText( + /your external data does not/i + ); }); diff --git a/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts b/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts index 2d3d1c46c..b96e62322 100644 --- a/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts +++ b/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.loggedin.spec.ts @@ -1,4 +1,12 @@ -import environments from "@constants/environments"; +import { + ABSTAIN_VOTE_DOC_URL, + DELEGATION_DOC_URL, + DIRECT_VOTER_DOC_URL, + GOVERNANCE_ACTION_DOC_URL, + PROPOSE_GOVERNANCE_ACTION_DOC_URL, + REGISTER_DREP_DOC_URL, + SIGNAL_NO_CONFIDENCE_VOTE_DOC_URL, +} from "@constants/docsUrl"; import { user01Wallet } from "@constants/staticWallets"; import { createTempUserAuth } from "@datafactory/createAuth"; import { faker } from "@faker-js/faker"; @@ -31,35 +39,25 @@ test.describe("Logged in user", () => { context.waitForEvent("page"), page.getByTestId("delegate-learn-more-button").click(), ]); - - await expect(delegationLearnMorepage).toHaveURL( - `${environments.docsUrl}/faqs/ways-to-use-your-voting-power` - ); + await expect(delegationLearnMorepage).toHaveURL(DELEGATION_DOC_URL); const [registerLearnMorepage] = await Promise.all([ context.waitForEvent("page"), page.getByTestId("register-learn-more-button").click(), ]); - - await expect(registerLearnMorepage).toHaveURL( - `${environments.docsUrl}/faqs/what-does-it-mean-to-register-as-a-drep` - ); + await expect(registerLearnMorepage).toHaveURL(REGISTER_DREP_DOC_URL); const [directVoterLearnMorepage] = await Promise.all([ context.waitForEvent("page"), page.getByTestId("learn-more-button").first().click(), // BUG should be unique test id ]); - - await expect(directVoterLearnMorepage).toHaveURL( - `${environments.docsUrl}/faqs/what-does-it-mean-to-register-as-a-drep` - ); + await expect(directVoterLearnMorepage).toHaveURL(DIRECT_VOTER_DOC_URL); const [GA_LearnMorepage] = await Promise.all([ context.waitForEvent("page"), page.getByTestId("learn-more-governance-actions-button").click(), ]); - - await expect(GA_LearnMorepage).toHaveURL("https://sancho.network/actions/"); + await expect(GA_LearnMorepage).toHaveURL(GOVERNANCE_ACTION_DOC_URL); const [proposed_GA_VoterLearnMorepage] = await Promise.all([ context.waitForEvent("page"), @@ -69,9 +67,8 @@ test.describe("Logged in user", () => { .getByTestId("learn-more-button") .click(), ]); // BUG should be unique test id - await expect(proposed_GA_VoterLearnMorepage).toHaveURL( - `${environments.docsUrl}/faqs/what-is-a-governance-action` + PROPOSE_GOVERNANCE_ACTION_DOC_URL ); }); @@ -88,16 +85,14 @@ test.describe("Logged in user", () => { context.waitForEvent("page"), dRepDirectoryPage.abstainInfoButton.click(), ]); - - await expect(abstain_Info_Page).toHaveURL(`${environments.docsUrl}`); + await expect(abstain_Info_Page).toHaveURL(ABSTAIN_VOTE_DOC_URL); const [signal_No_Confidence_Info_Page] = await Promise.all([ context.waitForEvent("page"), dRepDirectoryPage.signalNoConfidenceInfoButton.click(), ]); - await expect(signal_No_Confidence_Info_Page).toHaveURL( - `${environments.docsUrl}` + SIGNAL_NO_CONFIDENCE_VOTE_DOC_URL ); }); diff --git a/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.spec.ts b/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.spec.ts index 67ee1af2d..2a65c17b4 100644 --- a/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.spec.ts +++ b/tests/govtool-frontend/playwright/tests/6-miscellaneous/miscellaneous.spec.ts @@ -1,3 +1,12 @@ +import { + DELEGATION_DOC_URL, + DIRECT_VOTER_DOC_URL, + REGISTER_DREP_DOC_URL, + GOVERNANCE_ACTION_DOC_URL, + PROPOSE_GOVERNANCE_ACTION_DOC_URL, + FAQS_DOC_URL, + GUIDES_DOC_URL, +} from "@constants/docsUrl"; import { test } from "@fixtures/walletExtension"; import { setAllureEpic } from "@helpers/allure"; import { isMobile, openDrawer } from "@helpers/mobile"; @@ -25,9 +34,7 @@ test("6C. Navigation within the dApp", async ({ page, context }) => { page.getByTestId("guides-link").click(), ]); - await expect(guidesPage).toHaveURL( - `${environments.docsUrl}/about/what-is-sanchonet-govtool` - ); + await expect(guidesPage).toHaveURL(GUIDES_DOC_URL); if (isMobile(page)) { await openDrawer(page); @@ -37,7 +44,7 @@ test("6C. Navigation within the dApp", async ({ page, context }) => { page.getByTestId("faqs-link").click(), ]); - await expect(faqsPage).toHaveURL(`${environments.docsUrl}/faqs`); + await expect(faqsPage).toHaveURL(FAQS_DOC_URL); if (isMobile(page)) { await openDrawer(page); @@ -56,33 +63,25 @@ test("6D. Should open Sanchonet docs in a new tab when clicking `Learn More` on context.waitForEvent("page"), page.getByTestId("delegate-learn-more-button").click(), ]); - - await expect(delegationLearnMorepage).toHaveURL( - `${environments.docsUrl}/faqs/ways-to-use-your-voting-power` - ); + await expect(delegationLearnMorepage).toHaveURL(DELEGATION_DOC_URL); const [registerLearnMorepage] = await Promise.all([ context.waitForEvent("page"), page.getByTestId("register-learn-more-button").click(), ]); - - await expect(registerLearnMorepage).toHaveURL( - `${environments.docsUrl}/faqs/what-does-it-mean-to-register-as-a-drep` - ); + await expect(registerLearnMorepage).toHaveURL(REGISTER_DREP_DOC_URL); const [directVoterLearnMorepage] = await Promise.all([ context.waitForEvent("page"), page.getByTestId("lear-more-about-sole-voter-button").click(), ]); - - await expect(directVoterLearnMorepage).toHaveURL(`${environments.docsUrl}`); + await expect(directVoterLearnMorepage).toHaveURL(DIRECT_VOTER_DOC_URL); const [proposed_GA_VoterLearnMorepage] = await Promise.all([ context.waitForEvent("page"), page.getByRole("button", { name: "Learn more" }).nth(3).click(), // BUG missing test id ]); - await expect(proposed_GA_VoterLearnMorepage).toHaveURL( - `${environments.docsUrl}` + PROPOSE_GOVERNANCE_ACTION_DOC_URL ); }); diff --git a/tests/test-infrastructure/docker-compose-govtool.yml b/tests/test-infrastructure/docker-compose-govtool.yml index c129fd247..77f5718f6 100644 --- a/tests/test-infrastructure/docker-compose-govtool.yml +++ b/tests/test-infrastructure/docker-compose-govtool.yml @@ -43,6 +43,7 @@ services: VITE_SENTRY_DSN: ${SENTRY_DSN_FRONTEND} NPMRC_TOKEN: ${NPMRC_TOKEN} VITE_APP_ENV: ${APP_ENV:-test} + VITE_USERSNAP_SPACE_API_KEY: ${USERSNAP_SPACE_API_KEY} VITE_IS_PROPOSAL_DISCUSSION_FORUM_ENABLED: "true" GTM_ID: ${GTM_ID} environment: @@ -79,6 +80,8 @@ services: NPMRC_TOKEN: ${NPMRC_TOKEN} environment: VIRTUAL_HOST: https://storybook-${BASE_DOMAIN} + networks: + - frontend deploy: restart_policy: delay: "30s" diff --git a/tests/test-infrastructure/playbook.yml b/tests/test-infrastructure/playbook.yml index 7a6337f3c..f38eace75 100644 --- a/tests/test-infrastructure/playbook.yml +++ b/tests/test-infrastructure/playbook.yml @@ -22,6 +22,7 @@ GTM_ID: "{{ lookup ('env', 'GTM_ID') }}" GOVTOOL_TAG: "{{ lookup('env', 'GOVTOOL_TAG') }}" NPMRC_TOKEN: "{{ lookup('env','NPMRC_TOKEN') }}" + USERSNAP_SPACE_API_KEY: "{{ lookup('env','USERSNAP_SPACE_API_KEY') }}" SENTRY_DSN_FRONTEND: "{{ lookup ('env', 'SENTRY_DSN_FRONTEND') }}" SENTRY_DSN_BACKEND: "{{ lookup ('env', 'SENTRY_DSN_BACKEND') }}" become: yes \ No newline at end of file