diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..193d5a9f7 --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +REACT_APP_BASEDAO_DOCKERISED_URL=#LOCAL_BASEDAO_DOCKERIZED_URL +REACT_APP_CORS_PROXY_URL=#LOCAL_DORG_CORS_PROXY_URL +REACT_APP_DAO_DEPLOYER_API=#LOCAL_DAO_DEPLOYER_URL +REACT_APP_ENV=LOCAL +REACT_APP_HASURA_ADMIN_SECRET=#YOUR_HASURA_ADMIN_SECRET +REACT_APP_HASURA_ADMIN_SECRET_V2=#YOUR_HASURA_ADMIN_SECRET +REACT_APP_HASURA_URL=#YOUR_LOCAL_HOMBASE_INDEXER_HASURA_URL +REACT_APP_HASURA_URL_V2=#YOUR_LOCAL_HOMBASE_INDEXER_HASURA_URL +REACT_APP_LAUNCH_DARKLY_SDK_DEV=#YOUR_DARKLY_SDK_TOKEN_HERE +REACT_APP_LITE_API_URL=#YOUR_LOCAL_HOMBASE_LITE_BACKEND_URL +REACT_APP_MIXPANEL_DEBUG_ENABLED=false +REACT_APP_MIXPANEL_TOKEN=#YOUR_MIXPANEL_TOKEN_HERE +REACT_APP_NETWORK=ghostnet +REACT_APP_URL=http://localhost:3000 +REACT_APP_V2_URL=http://localhost:3000 +REACT_APP_IS_NOT_TESTING=false \ No newline at end of file diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 000000000..0c7ef174e --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,57 @@ +name: Playwright Tests + +on: + push: + branches: + # TODO: comment out testing branch constraints + - '**' + # TODO: uncomment below actual branch constraints + # - develop + # - staging + # - master + # TODO: uncomment below actual branch constraints + # pull_request: + # branches: + # types: [opened] + # - develop + # - staging + # - master + +jobs: +# TODO: Add something like https://github.com/JakePartusch/wait-for-netlify-action + test: + # TODO: Remove this when playwright tests are working again + # if: false + timeout-minutes: 60 + runs-on: ubuntu-latest + env: + WEBAPP_URL: https://deploy-preview-740--tezos-homebase.netlify.app + # TODO: uncomment below actual branch constraints + # ${{ + # github.event_name == 'pull_request' && 'https://deploy-preview-${{ github.event.number }}--tezos-homebase.netlify.app' || + # github.ref == 'refs/heads/develop' && 'https://develop--tezos-homebase.netlify.app' || + # github.ref == 'refs/heads/staging' && 'https://staging--tezos-homebase.netlify.app' || + # github.ref == 'refs/heads/master' && 'https://master--tezos-homebase.netlify.app' }} + steps: + - uses: actions/checkout@v4 + with: + repository: dOrgTech/homebase-app-tests + - uses: actions/setup-node@v4 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Install Playwright + run: npx playwright install --with-deps + - name: TC01 - Run Playwright tests + run: npm run test:tc01 + - name: TC02 - Run Playwright tests + run: npm run test:tc02 + - name: TC03 - Run Playwright tests + run: npm run test:tc03 + - name: TC04 - Run Playwright tests + run: npm run test:tc04 + - name: TC05 - Run Playwright tests + run: npm run test:tc05 + - name: TC06 - Run Playwright tests + run: npm run test:tc06 diff --git a/.gitignore b/.gitignore index 51ee811f5..9ee46ceb3 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,3 @@ yarn-error.log* .vscode .idea -.env.example diff --git a/src/modules/explorer/pages/DAOList/index.tsx b/src/modules/explorer/pages/DAOList/index.tsx index 4dc638247..ff1509745 100644 --- a/src/modules/explorer/pages/DAOList/index.tsx +++ b/src/modules/explorer/pages/DAOList/index.tsx @@ -238,7 +238,7 @@ export const DAOList: React.FC = () => { - + Create DAO diff --git a/src/modules/lite/creator/index.tsx b/src/modules/lite/creator/index.tsx index e43eebe56..cc5d82c82 100644 --- a/src/modules/lite/creator/index.tsx +++ b/src/modules/lite/creator/index.tsx @@ -33,6 +33,7 @@ import CodeOffIcon from "@mui/icons-material/CodeOff" import { ProposalCodeEditorInput } from "modules/explorer/components/ProposalFormInput" import Prism, { highlight } from "prismjs" import "prism-themes/themes/prism-night-owl.css" +import { getEnv, EnvKey } from "services/config" const CodeButton = styled(CodeIcon)(({ theme }) => ({ background: theme.palette.primary.dark, @@ -459,7 +460,7 @@ const CommunityForm = ({ submitForm, values, setFieldValue, errors, touched, set export const CommunityCreator: React.FC = () => { const navigate = useHistory() - const { network, account, wallet } = useTezos() + const { network, account, wallet, tezos } = useTezos() const openNotification = useNotification() const initialState: Community = { @@ -489,8 +490,15 @@ export const CommunityCreator: React.FC = () => { values.members.push(account) try { - const { signature, payloadBytes } = await getSignature(account, wallet, JSON.stringify(values)) - const publicKey = (await wallet?.client.getActiveAccount())?.publicKey + const { signature, payloadBytes } = await getSignature(account, wallet, network, JSON.stringify(values), tezos) + let publicKey + + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) !== "true") { + publicKey = await tezos.signer.publicKey() + } else { + publicKey = (await wallet?.client.getActiveAccount())?.publicKey + } + if (!signature) { openNotification({ message: `Issue with Signature`, diff --git a/src/modules/lite/explorer/pages/CreateProposal/index.tsx b/src/modules/lite/explorer/pages/CreateProposal/index.tsx index 96ab18476..6160b9ded 100644 --- a/src/modules/lite/explorer/pages/CreateProposal/index.tsx +++ b/src/modules/lite/explorer/pages/CreateProposal/index.tsx @@ -36,7 +36,7 @@ import CodeOffIcon from "@mui/icons-material/CodeOff" import { ProposalCodeEditorInput } from "modules/explorer/components/ProposalFormInput" import Prism, { highlight } from "prismjs" import "prism-themes/themes/prism-night-owl.css" - +import { EnvKey, getEnv } from "services/config" dayjs.extend(duration) const ProposalContainer = styled(Grid)(({ theme }) => ({ @@ -662,7 +662,7 @@ const calculateEndTime = (days: number, hours: number, minutes: number) => { export const ProposalCreator: React.FC<{ id?: string; onClose?: any }> = props => { const navigate = useHistory() - const { network, account, wallet } = useTezos() + const { network, account, wallet, tezos } = useTezos() const openNotification = useNotification() const [isLoading, setIsLoading] = useState(false) const daoId = useDAOID() @@ -699,8 +699,14 @@ export const ProposalCreator: React.FC<{ id?: string; onClose?: any }> = props = data.endTime = calculateEndTime(values.endTimeDays!, values.endTimeHours!, values.endTimeMinutes!) data.author = account - const { signature, payloadBytes } = await getSignature(account, wallet, JSON.stringify(data)) - const publicKey = (await wallet?.client.getActiveAccount())?.publicKey + const { signature, payloadBytes } = await getSignature(account, wallet, network, JSON.stringify(data), tezos) + let publicKey + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) !== "true") { + publicKey = await tezos.signer.publicKey() + } else { + publicKey = (await wallet?.client.getActiveAccount())?.publicKey + } + if (!signature) { openNotification({ message: `Issue with Signature`, diff --git a/src/modules/lite/explorer/pages/ProposalDetails/index.tsx b/src/modules/lite/explorer/pages/ProposalDetails/index.tsx index d26410df0..7502ba630 100644 --- a/src/modules/lite/explorer/pages/ProposalDetails/index.tsx +++ b/src/modules/lite/explorer/pages/ProposalDetails/index.tsx @@ -20,6 +20,7 @@ import { useDAO } from "services/services/dao/hooks/useDAO" import { useTokenVoteWeight } from "services/contracts/token/hooks/useTokenVoteWeight" import BigNumber from "bignumber.js" import { ArrowBackIosOutlined } from "@material-ui/icons" +import { EnvKey, getEnv } from "services/config" const PageContainer = styled("div")({ marginBottom: 50, @@ -57,7 +58,7 @@ export const ProposalDetails: React.FC<{ id: string }> = ({ id }) => { const { state } = useLocation<{ poll: Poll; choices: Choice[]; daoId: string }>() const navigate = useHistory() const { data: dao } = useDAO(state?.daoId) - const { account, wallet } = useTezos() + const { account, wallet, network, tezos } = useTezos() const openNotification = useNotification() const [refresh, setRefresh] = useState() const community = useCommunity(id) @@ -91,8 +92,14 @@ export const ProposalDetails: React.FC<{ id: string }> = ({ id }) => { } try { - const publicKey = (await wallet?.client.getActiveAccount())?.publicKey - const { signature, payloadBytes } = await getSignature(account, wallet, JSON.stringify(votesData)) + const { signature, payloadBytes } = await getSignature(account, wallet, network, JSON.stringify(votesData), tezos) + let publicKey + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) !== "true") { + publicKey = await tezos.signer.publicKey() + } else { + publicKey = (await wallet?.client.getActiveAccount())?.publicKey + } + if (!signature) { openNotification({ message: `Issue with Signature`, diff --git a/src/services/beacon/context.tsx b/src/services/beacon/context.tsx index d03d388d2..d7b868ca4 100644 --- a/src/services/beacon/context.tsx +++ b/src/services/beacon/context.tsx @@ -1,8 +1,11 @@ import React, { createContext, useEffect, useReducer } from "react" import mixpanel from "mixpanel-browser" -import { createTezos, createWallet, getTezosNetwork } from "./utils" +import { ALICE_PRIV_KEY, createTezos, createWallet, getTezosNetwork } from "./utils" import { INITIAL_STATE, reducer, TezosState } from "./reducer" import { TezosAction, TezosActionType } from "./actions" +import { InMemorySigner } from "@taquito/signer" +import { EnvKey, getEnv } from "services/config" +import { BeaconWallet } from "@taquito/beacon-wallet" interface TezosProvider { state: TezosState @@ -19,20 +22,29 @@ const getSavedState = async (): Promise => { try { const network = getTezosNetwork() const tezos = createTezos(network) - const wallet = createWallet(network) - const activeAccount = await wallet.client.getActiveAccount() - if (!activeAccount?.address) { - throw new Error("No wallet address found") + let wallet, account + + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) === "true") { + wallet = createWallet(network) + account = await tezos.wallet.pkh() + tezos.setProvider({ wallet }) + } else { + const signer = await InMemorySigner.fromSecretKey(ALICE_PRIV_KEY) + wallet = signer + account = await signer.publicKeyHash() + tezos.setProvider({ signer }) } - tezos.setProvider({ wallet }) + if (!account) { + throw new Error("No wallet address found") + } return { network, tezos, - wallet, - account: activeAccount.address + wallet: wallet as BeaconWallet, + account } } catch (error) { return INITIAL_STATE diff --git a/src/services/beacon/hooks/useTezos.ts b/src/services/beacon/hooks/useTezos.ts index 4c50e1309..00161881c 100644 --- a/src/services/beacon/hooks/useTezos.ts +++ b/src/services/beacon/hooks/useTezos.ts @@ -1,11 +1,13 @@ import { useQueryClient } from "react-query" import { useCallback, useContext } from "react" import { MichelCodecPacker, TezosToolkit } from "@taquito/taquito" -import { connectWithBeacon, createTezos, Network, rpcNodes, TezosActionType } from "services/beacon" +import { ALICE_PRIV_KEY, connectWithBeacon, Network, rpcNodes, TezosActionType, createTezos } from "services/beacon" import { TezosContext } from "services/beacon/context" import { Tzip16Module } from "@taquito/tzip16" import mixpanel from "mixpanel-browser" import { BeaconWallet } from "@taquito/beacon-wallet" +import { EnvKey, getEnv } from "services/config" +import { InMemorySigner } from "@taquito/signer" type WalletConnectReturn = { tezos: TezosToolkit @@ -17,6 +19,14 @@ type WalletConnectReturn = { wallet: BeaconWallet | undefined } +export const initTezosInstance = (network: Network) => { + const newTezos = new TezosToolkit(rpcNodes[network]) + newTezos.setPackerProvider(new MichelCodecPacker()) + newTezos.addExtension(new Tzip16Module()) + + return newTezos +} + export const useTezos = (): WalletConnectReturn => { const { state: { tezos, network, account, wallet }, @@ -27,12 +37,21 @@ export const useTezos = (): WalletConnectReturn => { const connect = useCallback( async (newNetwork?: Network) => { - const { wallet } = await connectWithBeacon(network) + const newTezos: TezosToolkit = initTezosInstance(network || newNetwork) - const newTezos: TezosToolkit = createTezos(network || newNetwork) - newTezos.setProvider({ wallet }) + let wallet, account - const account = await newTezos.wallet.pkh() + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) === "true") { + const { wallet: beaconWallet } = await connectWithBeacon(network) + wallet = beaconWallet + newTezos.setProvider({ wallet }) + account = await newTezos.wallet.pkh() + } else { + const signer = await InMemorySigner.fromSecretKey(ALICE_PRIV_KEY) + wallet = signer + account = await signer.publicKeyHash() + newTezos.setProvider({ signer }) + } dispatch({ type: TezosActionType.UPDATE_TEZOS, @@ -40,7 +59,7 @@ export const useTezos = (): WalletConnectReturn => { network: newNetwork || network, tezos: newTezos, account, - wallet + wallet: wallet as BeaconWallet } }) mixpanel.identify(account) @@ -79,17 +98,27 @@ export const useTezos = (): WalletConnectReturn => { } }) } else { - const { wallet } = await connectWithBeacon(newNetwork) - newTezos.setProvider({ wallet }) - const newAccount = await newTezos.wallet.pkh() + let wallet, account + + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) === "true") { + const { wallet: beaconWallet } = await connectWithBeacon(network) + wallet = beaconWallet + newTezos.setProvider({ wallet }) + account = await newTezos.wallet.pkh() + } else { + const signer = await InMemorySigner.fromSecretKey(ALICE_PRIV_KEY) + wallet = signer + account = await signer.publicKeyHash() + newTezos.setProvider({ signer }) + } dispatch({ type: TezosActionType.UPDATE_TEZOS, payload: { network: newNetwork, tezos: newTezos, - account: newAccount, - wallet + account, + wallet: wallet as BeaconWallet } }) } diff --git a/src/services/beacon/utils.ts b/src/services/beacon/utils.ts index 179ae0ff9..fcdabe387 100644 --- a/src/services/beacon/utils.ts +++ b/src/services/beacon/utils.ts @@ -6,7 +6,8 @@ import { EnvKey, getEnv } from "services/config" export type Network = "mainnet" | "ghostnet" -export const ALICE_PRIV_KEY = "edsk3QoqBuvdamxouPhin7swCvkQNgq4jP5KZPbwWNnwdZpSpJiEbq" +export const ALICE_PRIV_KEY = + "edskS1q2utmfdhVsqFKvKbQHShFg7eme6xbVKoc5iRPeW1fX8a4M5iXdhzbyTca3r6CMfCfVQZAZ6yqiE5wqi9UyMr1VZ6S6dA" export const rpcNodes: Record = { mainnet: "https://mainnet.api.tez.ie", diff --git a/src/services/config/constants.ts b/src/services/config/constants.ts index 562c6290d..1d60547f0 100644 --- a/src/services/config/constants.ts +++ b/src/services/config/constants.ts @@ -16,6 +16,7 @@ export enum EnvKey { REACT_APP_LITE_API_URL = "REACT_APP_LITE_API_URL", REACT_APP_API_URL = "REACT_APP_API_URL", REACT_APP_BASE_URL = "REACT_APP_BASE_URL", + REACT_APP_IS_NOT_TESTING = "REACT_APP_IS_NOT_TESTING", REACT_APP_DAO_DEPLOYER_API = "REACT_APP_DAO_DEPLOYER_API" } diff --git a/src/services/contracts/baseDAO/hooks/useOriginate.ts b/src/services/contracts/baseDAO/hooks/useOriginate.ts index 0e3f92b7b..eaab61341 100644 --- a/src/services/contracts/baseDAO/hooks/useOriginate.ts +++ b/src/services/contracts/baseDAO/hooks/useOriginate.ts @@ -258,8 +258,14 @@ export const useOriginate = (template: DAOTemplate) => { daoContract: contract.address, tokenID: params.orgSettings.governanceToken.tokenId } - const { signature, payloadBytes } = await getSignature(account, wallet, JSON.stringify(values)) - const publicKey = (await wallet?.client.getActiveAccount())?.publicKey + const { signature, payloadBytes } = await getSignature(account, wallet, network, JSON.stringify(values), tezos) + let publicKey + + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) !== "true") { + publicKey = await tezos.signer.publicKey() + } else { + publicKey = (await wallet?.client.getActiveAccount())?.publicKey + } const resp = await saveLiteCommunity(signature, publicKey, payloadBytes) const data = await resp.json() diff --git a/src/services/lite/utils.ts b/src/services/lite/utils.ts index 3fce0e8b8..a7b4c1d3b 100644 --- a/src/services/lite/utils.ts +++ b/src/services/lite/utils.ts @@ -8,6 +8,8 @@ import { BeaconWallet } from "@taquito/beacon-wallet" import { RequestSignPayloadInput, SigningType } from "@airgap/beacon-sdk" import BigNumber from "bignumber.js" import { Network } from "services/beacon" +import { TezosToolkit } from "@taquito/taquito" +import { EnvKey, getEnv } from "services/config" export const getCurrentBlock = async (network: Network) => { const url = `https://api.${networkNameMap[network]}.tzkt.io/v1/head` @@ -150,7 +152,13 @@ export const formatByDecimals = (value: string, decimals: string) => { return nFormatter(new BigNumber(value).div(new BigNumber(10).pow(decimals)), 1) } -export const getSignature = async (userAddress: string, wallet: BeaconWallet, data?: string) => { +export const getSignature = async ( + userAddress: string, + wallet: BeaconWallet, + network: Network, + data?: string, + tezos?: TezosToolkit +) => { const formattedInput: string = [ "Tezos Signed Message:", process.env.REACT_APP_BASE_URL, @@ -167,8 +175,16 @@ export const getSignature = async (userAddress: string, wallet: BeaconWallet, da sourceAddress: userAddress } - const signedPayload = await wallet?.client.requestSignPayload(payload) - const { signature } = signedPayload + let signature + + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) !== "true" && tezos) { + const { sig: walletSign } = await tezos?.signer.sign(payloadBytes) + signature = walletSign + } else { + const signedPayload = await wallet?.client.requestSignPayload(payload) + const { signature: walletSign } = signedPayload + signature = walletSign + } return { signature, payloadBytes } } diff --git a/src/services/utils/utils.ts b/src/services/utils/utils.ts index 1cd44ec50..0eda9b651 100644 --- a/src/services/utils/utils.ts +++ b/src/services/utils/utils.ts @@ -8,6 +8,8 @@ import { RequestSignPayloadInput, SigningType } from "@airgap/beacon-sdk" import BigNumber from "bignumber.js" import { Network } from "services/beacon" import { networkNameMap } from "services/bakingBad" +import { TezosToolkit } from "@taquito/taquito" +import { EnvKey, getEnv } from "services/config" export const getCurrentBlock = async (network: Network) => { const url = `https://api.${networkNameMap[network]}.tzkt.io/v1/head` @@ -170,7 +172,13 @@ export const formatByDecimals = (value: string, decimals: string) => { return nFormatter(new BigNumber(value).div(new BigNumber(10).pow(decimals)), 1) } -export const getSignature = async (userAddress: string, wallet: BeaconWallet, data?: string) => { +export const getSignature = async ( + userAddress: string, + wallet: BeaconWallet, + network: Network, + data?: string, + tezos?: TezosToolkit +) => { const formattedInput: string = [ "Tezos Signed Message:", process.env.REACT_APP_BASE_URL, @@ -187,8 +195,16 @@ export const getSignature = async (userAddress: string, wallet: BeaconWallet, da sourceAddress: userAddress } - const signedPayload = await wallet?.client.requestSignPayload(payload) - const { signature } = signedPayload + let signature + + if (getEnv(EnvKey.REACT_APP_IS_NOT_TESTING) !== "true" && tezos) { + const { sig: walletSign } = await tezos?.signer.sign(bytes) + signature = walletSign + } else { + const signedPayload = await wallet?.client.requestSignPayload(payload) + const { signature: walletSign } = signedPayload + signature = walletSign + } return { signature, payloadBytes } }