Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
429a4ca454 | ||
|
|
4d3cd74847 | ||
|
|
58ef07adaf | ||
|
|
ceeb239c3a | ||
|
|
a5c35b6640 | ||
|
|
00dbb7dd60 | ||
|
|
89e2033556 | ||
|
|
2d821700bd | ||
|
|
28b4593a1d |
@@ -1,36 +0,0 @@
|
||||
name: Cypress Console tests -- live environment
|
||||
|
||||
# This workflow runs using provided url
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
url:
|
||||
description: 'Url'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
cypress-run:
|
||||
name: Run Cypress Trading tests -- live environment
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Use Node.js 20
|
||||
id: Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: Run Cypress tests
|
||||
uses: cypress-io/github-action@v4
|
||||
with:
|
||||
browser: chrome
|
||||
record: true
|
||||
project: ./apps/trading-e2e
|
||||
config: baseUrl=${{ github.event.inputs.url }}
|
||||
env: grepTags=@live
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -12,7 +12,6 @@ on:
|
||||
options:
|
||||
- explorer-e2e
|
||||
- governance-e2e
|
||||
- trading-e2e
|
||||
tags:
|
||||
description: 'Test tags to run'
|
||||
required: true
|
||||
|
||||
@@ -10,5 +10,5 @@ jobs:
|
||||
uses: ./.github/workflows/cypress-run.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
projects: '["explorer-e2e","governance-e2e","trading-e2e"]'
|
||||
projects: '["explorer-e2e","governance-e2e"]'
|
||||
tags: '@smoke @regression @slow'
|
||||
|
||||
@@ -8,12 +8,14 @@ import EpochMissingOverview from './epoch-missing';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import type { IconProps } from '@vegaprotocol/ui-toolkit';
|
||||
import isPast from 'date-fns/isPast';
|
||||
import { EpochSymbol } from '../links/block-link/block-link';
|
||||
|
||||
const borderClass =
|
||||
'border-solid border-2 border-vega-dark-200 border-collapse';
|
||||
|
||||
export type EpochOverviewProps = {
|
||||
id?: string;
|
||||
icon?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -24,7 +26,7 @@ export type EpochOverviewProps = {
|
||||
*
|
||||
* The details are hidden in a tooltip, behind the epoch number
|
||||
*/
|
||||
const EpochOverview = ({ id }: EpochOverviewProps) => {
|
||||
const EpochOverview = ({ id, icon = true }: EpochOverviewProps) => {
|
||||
const { data, error, loading } = useExplorerEpochQuery({
|
||||
variables: { id: id || '' },
|
||||
});
|
||||
@@ -38,7 +40,12 @@ const EpochOverview = ({ id }: EpochOverviewProps) => {
|
||||
}
|
||||
|
||||
if (!ti || loading || error) {
|
||||
return <span>{id}</span>;
|
||||
return (
|
||||
<span>
|
||||
<EpochSymbol />
|
||||
{id}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
const description = (
|
||||
@@ -90,7 +97,11 @@ const EpochOverview = ({ id }: EpochOverviewProps) => {
|
||||
return (
|
||||
<Tooltip description={description}>
|
||||
<p>
|
||||
<IconForEpoch start={ti.start} end={ti.end} />
|
||||
{icon ? (
|
||||
<IconForEpoch start={ti.start} end={ti.end} />
|
||||
) : (
|
||||
<EpochSymbol />
|
||||
)}
|
||||
{id}
|
||||
</p>
|
||||
</Tooltip>
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
query ExplorerEpochForBlock($block: String!) {
|
||||
epoch(block: $block) {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
lastBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type ExplorerEpochForBlockQueryVariables = Types.Exact<{
|
||||
block: Types.Scalars['String'];
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerEpochForBlockQuery = { __typename?: 'Query', epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, lastBlock?: string | null } } };
|
||||
|
||||
|
||||
export const ExplorerEpochForBlockDocument = gql`
|
||||
query ExplorerEpochForBlock($block: String!) {
|
||||
epoch(block: $block) {
|
||||
id
|
||||
timestamps {
|
||||
start
|
||||
end
|
||||
lastBlock
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useExplorerEpochForBlockQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useExplorerEpochForBlockQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useExplorerEpochForBlockQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useExplorerEpochForBlockQuery({
|
||||
* variables: {
|
||||
* block: // value for 'block'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useExplorerEpochForBlockQuery(baseOptions: Apollo.QueryHookOptions<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>(ExplorerEpochForBlockDocument, options);
|
||||
}
|
||||
export function useExplorerEpochForBlockLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>(ExplorerEpochForBlockDocument, options);
|
||||
}
|
||||
export type ExplorerEpochForBlockQueryHookResult = ReturnType<typeof useExplorerEpochForBlockQuery>;
|
||||
export type ExplorerEpochForBlockLazyQueryHookResult = ReturnType<typeof useExplorerEpochForBlockLazyQuery>;
|
||||
export type ExplorerEpochForBlockQueryResult = Apollo.QueryResult<ExplorerEpochForBlockQuery, ExplorerEpochForBlockQueryVariables>;
|
||||
@@ -4,17 +4,56 @@ import { Link } from 'react-router-dom';
|
||||
|
||||
import type { ComponentProps } from 'react';
|
||||
import Hash from '../hash';
|
||||
import { useExplorerEpochForBlockQuery } from './__generated__/EpochByBlock';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export type BlockLinkProps = Partial<ComponentProps<typeof Link>> & {
|
||||
height: string;
|
||||
showEpoch?: boolean;
|
||||
};
|
||||
|
||||
const BlockLink = ({ height, ...props }: BlockLinkProps) => {
|
||||
const BlockLink = ({ height, showEpoch = false, ...props }: BlockLinkProps) => {
|
||||
return (
|
||||
<Link className="underline" {...props} to={`/${Routes.BLOCKS}/${height}`}>
|
||||
<Hash text={height} />
|
||||
</Link>
|
||||
<>
|
||||
<Link className="underline" {...props} to={`/${Routes.BLOCKS}/${height}`}>
|
||||
<Hash text={height} />
|
||||
</Link>
|
||||
{showEpoch && <EpochForBlock block={height} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export function EpochForBlock(props: { block: string }) {
|
||||
const { error, data, loading } = useExplorerEpochForBlockQuery({
|
||||
errorPolicy: 'ignore',
|
||||
variables: { block: props.block },
|
||||
});
|
||||
|
||||
// NOTE: 0.73.x & <0.74.2 can error showing epoch, so for now we hide loading
|
||||
// or error states and only display if we get usable data
|
||||
if (error || loading || !data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="ml-2" title={t('Epoch')}>
|
||||
<EpochSymbol />
|
||||
{data.epoch.id}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export const EPOCH_SYMBOL = 'ⓔ';
|
||||
|
||||
export function EpochSymbol() {
|
||||
return (
|
||||
<em
|
||||
title={t('Epoch')}
|
||||
className="mr-1 cursor-default text-xl leading-none align-text-bottom not-italic"
|
||||
>
|
||||
{EPOCH_SYMBOL}
|
||||
</em>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlockLink;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import GovernanceLink from './governance-link';
|
||||
|
||||
describe('GovernanceLink', () => {
|
||||
it('renders the link with the correct text', () => {
|
||||
render(<GovernanceLink text="Governance internet website" />);
|
||||
const linkElement = screen.getByText('Governance internet website');
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the link with the correct href and sensible default text', () => {
|
||||
render(<GovernanceLink />);
|
||||
const linkElement = screen.getByText('Governance');
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,18 @@
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { ENV } from '../../../config/env';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export type GovernanceLinkProps = {
|
||||
text?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Just a link to the governance page, with optional text
|
||||
*/
|
||||
const GovernanceLink = ({ text = t('Governance') }: GovernanceLinkProps) => {
|
||||
const base = ENV.dataSources.governanceUrl;
|
||||
|
||||
return <ExternalLink href={base}>{text}</ExternalLink>;
|
||||
};
|
||||
|
||||
export default GovernanceLink;
|
||||
@@ -10,6 +10,8 @@ import { ChainResponseCode } from '../chain-response-code/chain-reponse.code';
|
||||
import { TxDataView } from '../../tx-data-view';
|
||||
import Hash from '../../../links/hash';
|
||||
import { Signature } from '../../../signature/signature';
|
||||
import { useExplorerEpochForBlockQuery } from '../../../links/block-link/__generated__/EpochByBlock';
|
||||
import EpochOverview from '../../../epoch-overview/epoch';
|
||||
|
||||
interface TxDetailsSharedProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -44,6 +46,11 @@ export const TxDetailsShared = ({
|
||||
blockData,
|
||||
hideTypeRow = false,
|
||||
}: TxDetailsSharedProps) => {
|
||||
const { data } = useExplorerEpochForBlockQuery({
|
||||
errorPolicy: 'ignore',
|
||||
variables: { block: txData?.block.toString() || '' },
|
||||
});
|
||||
|
||||
if (!txData) {
|
||||
return <>{t('Awaiting Block Explorer transaction details')}</>;
|
||||
}
|
||||
@@ -74,7 +81,7 @@ export const TxDetailsShared = ({
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Block')}</TableCell>
|
||||
<TableCell>
|
||||
<BlockLink height={height} />
|
||||
<BlockLink height={height} showEpoch={false} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
@@ -83,6 +90,7 @@ export const TxDetailsShared = ({
|
||||
<Signature signature={txData.signature} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
|
||||
<TableCell>
|
||||
@@ -100,6 +108,14 @@ export const TxDetailsShared = ({
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{data && data.epoch && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell scope="row">{t('Epoch')}</TableCell>
|
||||
<TableCell modifier="bordered">
|
||||
<EpochOverview id={data.epoch.id} icon={false} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Response code')}</TableCell>
|
||||
<TableCell>
|
||||
|
||||
@@ -113,13 +113,16 @@ export const TxDetailsTransfer = ({
|
||||
|
||||
/**
|
||||
* Gets a string description of this transfer
|
||||
* @param txData A full transfer
|
||||
* @param tx A full transfer
|
||||
* @returns string Transfer label
|
||||
*/
|
||||
export function getTypeLabelForTransfer(tx: Transfer) {
|
||||
if (tx.to === SPECIAL_CASE_NETWORK || tx.to === SPECIAL_CASE_NETWORK_ID) {
|
||||
if (tx.toAccountType === 'ACCOUNT_TYPE_NETWORK_TREASURY') {
|
||||
return 'Treasury transfer';
|
||||
}
|
||||
if (tx.recurring && tx.recurring.dispatchStrategy) {
|
||||
return 'Reward top up transfer';
|
||||
return 'Reward transfer';
|
||||
}
|
||||
// Else: we don't know that it's a reward transfer, so let's not guess
|
||||
} else if (tx.recurring) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import type { components } from '../../../types/explorer';
|
||||
import { VoteIcon } from '../vote-icon/vote-icon';
|
||||
import { ExternalChainIcon } from '../links/external-explorer-link/external-chain-icon';
|
||||
import { getTypeLabelForTransfer } from './details/tx-transfer';
|
||||
|
||||
interface TxOrderTypeProps {
|
||||
orderType: string;
|
||||
@@ -95,7 +96,7 @@ export function getLabelForOrderType(
|
||||
|
||||
/**
|
||||
* Given a proposal, will return a specific label
|
||||
* @param chainEvent
|
||||
* @param proposal
|
||||
* @returns
|
||||
*/
|
||||
export function getLabelForProposal(
|
||||
@@ -142,6 +143,36 @@ export function getLabelForProposal(
|
||||
}
|
||||
}
|
||||
|
||||
type label = {
|
||||
type: string;
|
||||
colours: string;
|
||||
};
|
||||
|
||||
export function getLabelForTransfer(
|
||||
transfer: components['schemas']['commandsv1Transfer']
|
||||
): label {
|
||||
const type = getTypeLabelForTransfer(transfer);
|
||||
|
||||
if (transfer.toAccountType === 'ACCOUNT_TYPE_NETWORK_TREASURY') {
|
||||
return {
|
||||
type,
|
||||
colours:
|
||||
'text-vega-green dark:text-green bg-vega-dark-150 dark:bg-vega-dark-250',
|
||||
};
|
||||
} else if (transfer.recurring) {
|
||||
return {
|
||||
type,
|
||||
colours:
|
||||
'text-vega-yellow dark:text-yellow bg-vega-dark-150 dark:bg-vega-dark-250',
|
||||
};
|
||||
}
|
||||
return {
|
||||
type,
|
||||
colours:
|
||||
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a chain event, will try to provide a more useful label
|
||||
* @param chainEvent
|
||||
@@ -225,9 +256,10 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||
type = getLabelForChainEvent(command.chainEvent);
|
||||
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
||||
} else if (type === 'Validator Heartbeat') {
|
||||
colours =
|
||||
'text-white dark-text-white bg-vega-light-200 dark:bg-vega-dark-100';
|
||||
} else if (type === 'Transfer Funds' && command?.transfer) {
|
||||
const res = getLabelForTransfer(command.transfer);
|
||||
type = res.type;
|
||||
colours = res.colours;
|
||||
} else if (type === 'Proposal' || type === 'Governance Proposal') {
|
||||
if (command && !!command.proposalSubmission) {
|
||||
type = getLabelForProposal(command.proposalSubmission);
|
||||
|
||||
@@ -16,6 +16,8 @@ import { useBlockInfo } from '@vegaprotocol/tendermint';
|
||||
import { NodeLink } from '../../../components/links';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
import EmptyList from '../../../components/empty-list/empty-list';
|
||||
import { useExplorerEpochForBlockQuery } from '../../../components/links/block-link/__generated__/EpochByBlock';
|
||||
import EpochOverview from '../../../components/epoch-overview/epoch';
|
||||
|
||||
type Params = { block: string };
|
||||
|
||||
@@ -26,6 +28,11 @@ const Block = () => {
|
||||
state: { data: blockData, loading, error },
|
||||
} = useBlockInfo(Number(block));
|
||||
|
||||
const { data } = useExplorerEpochForBlockQuery({
|
||||
errorPolicy: 'ignore',
|
||||
variables: { block: block?.toString() || '' },
|
||||
});
|
||||
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
|
||||
@@ -75,6 +82,7 @@ const Block = () => {
|
||||
<code>{blockData.result.block.header.consensus_hash}</code>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">Mined by</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
@@ -97,6 +105,14 @@ const Block = () => {
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{data && data.epoch && (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell scope="row">{t('Epoch')}</TableCell>
|
||||
<TableCell modifier="bordered">
|
||||
<EpochOverview id={data.epoch.id} icon={false} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
<TableRow modifier="bordered">
|
||||
<TableHeader scope="row">Transactions</TableHeader>
|
||||
<TableCell modifier="bordered">
|
||||
|
||||
@@ -33,7 +33,10 @@ export const NetworkAccountsTable = () => {
|
||||
return (
|
||||
<section className="md:flex md:flex-row flex-wrap">
|
||||
{c.map((a) => (
|
||||
<div className="basis-1/2 md:basis-1/4">
|
||||
<div
|
||||
className="basis-1/2 md:basis-1/4"
|
||||
key={`${a.assetId}-${a.balance}`}
|
||||
>
|
||||
<div className="bg-white rounded overflow-hidden shadow-lg dark:bg-black dark:border-slate-500 dark:border">
|
||||
<div className="text-center p-6 bg-gray-100 dark:bg-slate-900 border-b dark:border-slate-500">
|
||||
<p className="flex justify-center">
|
||||
|
||||
@@ -16,19 +16,21 @@ import type { DeepPartial } from '@apollo/client/utilities';
|
||||
|
||||
describe('typeLabel', () => {
|
||||
it('should return "Transfer" for "OneOffTransfer" kind', () => {
|
||||
expect(typeLabel('OneOffTransfer')).toBe('Transfer');
|
||||
expect(typeLabel('OneOffTransfer')).toBe('Transfer - one time');
|
||||
});
|
||||
|
||||
it('should return "Transfer" for "RecurringTransfer" kind', () => {
|
||||
expect(typeLabel('RecurringTransfer')).toBe('Transfer');
|
||||
expect(typeLabel('RecurringTransfer')).toBe('Transfer - repeating');
|
||||
});
|
||||
|
||||
it('should return "Governance" for "OneOffGovernanceTransfer" kind', () => {
|
||||
expect(typeLabel('OneOffGovernanceTransfer')).toBe('Governance');
|
||||
expect(typeLabel('OneOffGovernanceTransfer')).toBe('Governance - one time');
|
||||
});
|
||||
|
||||
it('should return "Governance" for "RecurringGovernanceTransfer" kind', () => {
|
||||
expect(typeLabel('RecurringGovernanceTransfer')).toBe('Governance');
|
||||
expect(typeLabel('RecurringGovernanceTransfer')).toBe(
|
||||
'Governance - repeating'
|
||||
);
|
||||
});
|
||||
|
||||
it('should return "Unknown" for unknown kind', () => {
|
||||
@@ -256,7 +258,7 @@ describe('NetworkTransfersTable', () => {
|
||||
expect(screen.getByTestId('from-account').textContent).toEqual('Treasury');
|
||||
expect(screen.getByTestId('to-account').textContent).toEqual('7100…97a0');
|
||||
expect(screen.getByTestId('transfer-kind').textContent).toEqual(
|
||||
'Governance'
|
||||
'Governance - one time'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { useMemo } from 'react';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import ProposalLink from '../../../components/links/proposal-link/proposal-link';
|
||||
|
||||
export const colours = {
|
||||
INCOMING: '!fill-vega-green-600 text-vega-green-600 mr-2',
|
||||
@@ -50,14 +51,24 @@ export function getToAccountTypeLabel(type?: AccountType): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function isGovernanceTransfer(kind?: string): boolean {
|
||||
if (kind && kind.includes('Governance')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function typeLabel(kind?: string): string {
|
||||
switch (kind) {
|
||||
case 'OneOffTransfer':
|
||||
return t('Transfer - one time');
|
||||
case 'RecurringTransfer':
|
||||
return t('Transfer');
|
||||
return t('Transfer - repeating');
|
||||
case 'OneOffGovernanceTransfer':
|
||||
return t('Governance - one time');
|
||||
case 'RecurringGovernanceTransfer':
|
||||
return t('Governance');
|
||||
return t('Governance - repeating');
|
||||
default:
|
||||
return t('Unknown');
|
||||
}
|
||||
@@ -239,6 +250,11 @@ export const NetworkTransfersTable = () => {
|
||||
>
|
||||
{a && typeLabel(a.kind.__typename)}
|
||||
</span>
|
||||
{isGovernanceTransfer(a?.kind.__typename) && a?.id && (
|
||||
<span className="ml-4">
|
||||
<ProposalLink id={a?.id} text="View" />
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { NetworkAccountsTable } from './components/network-accounts-table';
|
||||
import { NetworkTransfersTable } from './components/network-transfers-table';
|
||||
import GovernanceLink from '../../components/links/governance-link/governance-link';
|
||||
|
||||
export type NonZeroAccount = {
|
||||
assetId: string;
|
||||
@@ -16,7 +17,33 @@ export const NetworkTreasury = () => {
|
||||
return (
|
||||
<section>
|
||||
<RouteTitle data-testid="block-header">{t(`Treasury`)}</RouteTitle>
|
||||
<div>
|
||||
<details className="w-full md:w-3/5 cursor-pointer shadow-lg p-5 dark:border-l-2 dark:border-vega-green">
|
||||
<summary>{t('About the Network Treasury')}</summary>
|
||||
<section className="mt-4 b-1 border-grey">
|
||||
<p className="mb-2">
|
||||
The network treasury can hold funds from any active settlement asset
|
||||
on the network. It is funded periodically by transfers from Gobalsky
|
||||
as part of the Community Adoption Fund (CAF), but in future may
|
||||
receive funds from any sources.
|
||||
</p>
|
||||
<p className="mb-2">
|
||||
Funds in the network treasury can be used by creating governance
|
||||
initiated transfers via{' '}
|
||||
<GovernanceLink text={t('community governance')} />. These transfers
|
||||
can be initiated by anyone and be used to fund reward pools, or can
|
||||
be used to fund other activities the{' '}
|
||||
<abbr className="decoration-dotted" title="Community Adoption Fund">
|
||||
CAF
|
||||
</abbr>{' '}
|
||||
is exploring.
|
||||
</p>
|
||||
<p>
|
||||
This page shows details of the balances in the treasury, pending
|
||||
transfers, and historic transfer movements to and from the treasury.
|
||||
</p>
|
||||
</section>
|
||||
</details>
|
||||
<div className="mt-6">
|
||||
<NetworkAccountsTable />
|
||||
</div>
|
||||
<div className="mt-5">
|
||||
|
||||
@@ -34,12 +34,6 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
});
|
||||
|
||||
it('Able to connect public key using url', function () {
|
||||
cy.getByTestId('exit-view').click();
|
||||
cy.visit(`/?address=${vegaWalletPubKey}`);
|
||||
verifyConnectedToPubKey();
|
||||
});
|
||||
|
||||
it.skip('Able to connect public key via wallet and view assets in wallet', function () {
|
||||
verifyConnectedToPubKey();
|
||||
cy.getByTestId('currency-title', { timeout: 10000 })
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet-react';
|
||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React, { Suspense } from 'react';
|
||||
@@ -15,20 +15,6 @@ import {
|
||||
} from './contexts/app-state/app-state-context';
|
||||
import { useContracts } from './contexts/contracts/contracts-context';
|
||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
||||
import { useConnectors } from './lib/vega-connectors';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const useVegaWalletEagerConnect = () => {
|
||||
const connectors = useConnectors();
|
||||
const vegaConnecting = useEagerConnect(connectors);
|
||||
const { pubKey, connect } = useVegaWallet();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [query] = React.useState(searchParams.get('address'));
|
||||
if (query && !pubKey) {
|
||||
connect(connectors.view);
|
||||
}
|
||||
return vegaConnecting;
|
||||
};
|
||||
|
||||
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
@@ -40,7 +26,7 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
const { token, staking, vesting } = useContracts();
|
||||
const setAssociatedBalances = useRefreshAssociatedBalances();
|
||||
const [balancesLoaded, setBalancesLoaded] = React.useState(false);
|
||||
const vegaConnecting = useVegaWalletEagerConnect();
|
||||
const vegaConnecting = useEagerConnect();
|
||||
|
||||
const loaded = balancesLoaded && !vegaConnecting;
|
||||
|
||||
|
||||
+31
-141
@@ -1,7 +1,6 @@
|
||||
import './i18n';
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||
import { AppLoader } from './app-loader';
|
||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||
@@ -26,7 +25,7 @@ import {
|
||||
} from '@vegaprotocol/web3';
|
||||
import { Web3Provider } from '@vegaprotocol/web3';
|
||||
import { VegaWalletDialogs } from './components/vega-wallet-dialogs';
|
||||
import { VegaWalletProvider, useChainId } from '@vegaprotocol/wallet';
|
||||
import { WalletProvider } from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
useVegaTransactionManager,
|
||||
useVegaTransactionUpdater,
|
||||
@@ -36,26 +35,21 @@ import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import {
|
||||
useEnvironment,
|
||||
NetworkLoader,
|
||||
useInitializeEnv,
|
||||
NodeGuard,
|
||||
NodeSwitcherDialog,
|
||||
useNodeSwitcherStore,
|
||||
DocsLinks,
|
||||
NodeFailure,
|
||||
AppLoader as Loader,
|
||||
useInitializeEnv,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { ENV } from './config';
|
||||
import type { InMemoryCacheConfig } from '@apollo/client';
|
||||
import { CreateWithdrawalDialog } from '@vegaprotocol/withdraws';
|
||||
import { SplashLoader } from './components/splash-loader';
|
||||
import { ToastsManager } from './toasts-manager';
|
||||
import {
|
||||
TelemetryDialog,
|
||||
TELEMETRY_ON,
|
||||
} from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { TelemetryDialog } from './components/telemetry-dialog/telemetry-dialog';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { isPartyNotFoundError } from './lib/party';
|
||||
import { useSentryInit } from './hooks/use-sentry-init';
|
||||
import { useVegaWalletConfig } from './hooks/use-vega-wallet-config';
|
||||
|
||||
const cache: InMemoryCacheConfig = {
|
||||
typePolicies: {
|
||||
@@ -104,32 +98,12 @@ const Web3Container = ({
|
||||
/** Ethereum provider url */
|
||||
providerUrl: string;
|
||||
}) => {
|
||||
const InitializeHandlers = () => {
|
||||
useVegaTransactionManager();
|
||||
useVegaTransactionUpdater();
|
||||
useEthTransactionManager();
|
||||
useEthTransactionUpdater();
|
||||
useEthWithdrawApprovalsManager();
|
||||
return null;
|
||||
};
|
||||
|
||||
const [connectors, initializeConnectors] = useWeb3ConnectStore((store) => [
|
||||
store.connectors,
|
||||
store.initialize,
|
||||
]);
|
||||
const {
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC,
|
||||
VEGA_ENV,
|
||||
VEGA_URL,
|
||||
VEGA_EXPLORER_URL,
|
||||
CHROME_EXTENSION_URL,
|
||||
MOZILLA_EXTENSION_URL,
|
||||
VEGA_WALLET_URL,
|
||||
} = useEnvironment();
|
||||
|
||||
const vegaChainId = useChainId(VEGA_URL);
|
||||
const { ETHEREUM_PROVIDER_URL, ETH_LOCAL_PROVIDER_URL, ETH_WALLET_MNEMONIC } =
|
||||
useEnvironment();
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId) {
|
||||
@@ -150,50 +124,31 @@ const Web3Container = ({
|
||||
ETH_LOCAL_PROVIDER_URL,
|
||||
ETH_WALLET_MNEMONIC,
|
||||
]);
|
||||
const sideBar = React.useMemo(() => {
|
||||
return [<EthWallet />, <VegaWallet />];
|
||||
}, []);
|
||||
|
||||
if (connectors.length === 0) {
|
||||
const vegaWalletConfig = useVegaWalletConfig();
|
||||
|
||||
if (!vegaWalletConfig || connectors.length === 0) {
|
||||
// Prevent loading when the connectors are not initialized
|
||||
return <SplashLoader />;
|
||||
}
|
||||
|
||||
if (
|
||||
!VEGA_URL ||
|
||||
!VEGA_WALLET_URL ||
|
||||
!VEGA_EXPLORER_URL ||
|
||||
!DocsLinks ||
|
||||
!CHROME_EXTENSION_URL ||
|
||||
!MOZILLA_EXTENSION_URL ||
|
||||
!vegaChainId
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Web3Provider connectors={connectors}>
|
||||
<Web3Connector connectors={connectors} chainId={Number(chainId)}>
|
||||
<VegaWalletProvider
|
||||
config={{
|
||||
network: VEGA_ENV,
|
||||
vegaUrl: VEGA_URL,
|
||||
chainId: vegaChainId,
|
||||
vegaWalletServiceUrl: VEGA_WALLET_URL,
|
||||
links: {
|
||||
explorer: VEGA_EXPLORER_URL,
|
||||
concepts: DocsLinks?.VEGA_WALLET_CONCEPTS_URL,
|
||||
chromeExtensionUrl: CHROME_EXTENSION_URL,
|
||||
mozillaExtensionUrl: MOZILLA_EXTENSION_URL,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<WalletProvider config={vegaWalletConfig}>
|
||||
<ContractsProvider>
|
||||
<AppLoader>
|
||||
<BalanceManager>
|
||||
<>
|
||||
<AppLayout>
|
||||
<TemplateSidebar sidebar={sideBar}>
|
||||
<TemplateSidebar
|
||||
sidebar={
|
||||
<>
|
||||
<EthWallet />
|
||||
<VegaWallet />
|
||||
</>
|
||||
}
|
||||
>
|
||||
<AppRouter />
|
||||
</TemplateSidebar>
|
||||
<footer className="p-4 break-all border-t border-neutral-700">
|
||||
@@ -211,7 +166,7 @@ const Web3Container = ({
|
||||
</BalanceManager>
|
||||
</AppLoader>
|
||||
</ContractsProvider>
|
||||
</VegaWalletProvider>
|
||||
</WalletProvider>
|
||||
</Web3Connector>
|
||||
</Web3Provider>
|
||||
);
|
||||
@@ -231,20 +186,9 @@ const ScrollToTop = () => {
|
||||
return null;
|
||||
};
|
||||
|
||||
const removeQueryParams = (url: string) => {
|
||||
return url.split('?')[0];
|
||||
};
|
||||
|
||||
const AppContainer = () => {
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const {
|
||||
VEGA_ENV,
|
||||
VEGA_URL,
|
||||
GIT_COMMIT_HASH,
|
||||
GIT_BRANCH,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
} = useEnvironment();
|
||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
const { VEGA_URL, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||
const { t } = useTranslation();
|
||||
const [nodeSwitcherOpen, setNodeSwitcher] = useNodeSwitcherStore((store) => [
|
||||
store.dialogOpen,
|
||||
@@ -254,70 +198,7 @@ const AppContainer = () => {
|
||||
// Hacky skip all the loading & web3 init for geo restricted users
|
||||
const isRestricted = document?.location?.pathname?.includes('/restricted');
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn && telemetryOn === 'true') {
|
||||
Sentry.init({
|
||||
dsn: ENV.dsn,
|
||||
tracesSampleRate: 0.1,
|
||||
enabled: true,
|
||||
environment: VEGA_ENV,
|
||||
release: GIT_COMMIT_HASH,
|
||||
beforeSend(event, hint) {
|
||||
const error = hint?.originalException;
|
||||
const errorIsString = typeof error === 'string';
|
||||
const errorIsObject = error instanceof Error;
|
||||
const requestUrl = event.request?.url;
|
||||
const transaction = event.transaction;
|
||||
|
||||
if (
|
||||
(errorIsString && isPartyNotFoundError({ message: error })) ||
|
||||
(errorIsObject && isPartyNotFoundError(error))
|
||||
) {
|
||||
// This error is caused by a pubkey making an API request before
|
||||
// it has interacted with the chain. This isn't needed in Sentry.
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedRequest =
|
||||
requestUrl && requestUrl.includes('/claim?')
|
||||
? { ...event.request, url: removeQueryParams(requestUrl) }
|
||||
: event.request;
|
||||
|
||||
const updatedTransaction =
|
||||
transaction && transaction.includes('/claim?')
|
||||
? removeQueryParams(transaction)
|
||||
: transaction;
|
||||
|
||||
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
||||
if (
|
||||
breadcrumb.type === 'navigation' &&
|
||||
breadcrumb.data?.to?.includes('/claim?')
|
||||
) {
|
||||
return {
|
||||
...breadcrumb,
|
||||
data: {
|
||||
...breadcrumb.data,
|
||||
to: removeQueryParams(breadcrumb.data.to),
|
||||
},
|
||||
};
|
||||
}
|
||||
return breadcrumb;
|
||||
});
|
||||
|
||||
return {
|
||||
...event,
|
||||
request: updatedRequest,
|
||||
transaction: updatedTransaction,
|
||||
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
||||
};
|
||||
},
|
||||
});
|
||||
Sentry.setTag('branch', GIT_BRANCH);
|
||||
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
||||
} else {
|
||||
Sentry.close();
|
||||
}
|
||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||
useSentryInit();
|
||||
|
||||
if (isRestricted) {
|
||||
return (
|
||||
@@ -359,6 +240,15 @@ const AppContainer = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const InitializeHandlers = () => {
|
||||
useVegaTransactionManager();
|
||||
useVegaTransactionUpdater();
|
||||
useEthTransactionManager();
|
||||
useEthTransactionUpdater();
|
||||
useEthWithdrawApprovalsManager();
|
||||
return null;
|
||||
};
|
||||
|
||||
function App() {
|
||||
useInitializeEnv();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useGetAssociationBreakdown } from '../../hooks/use-get-association-brea
|
||||
import { useGetUserBalances } from '../../hooks/use-get-user-balances';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useListenForStakingEvents as useListenForAssociationEvents } from '../../hooks/use-listen-for-staking-events';
|
||||
import { useTranches } from '../../lib/tranches/tranches-store';
|
||||
import { useUserTrancheBalances } from '../../routes/redemption/hooks';
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
|
||||
export const ConnectToVega = () => {
|
||||
const { t } = useTranslation();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
onClick={openVegaWalletDialog}
|
||||
data-testid="connect-to-vega-wallet-btn"
|
||||
variant="primary"
|
||||
>
|
||||
|
||||
+3
-3
@@ -6,8 +6,8 @@ import {
|
||||
Intent,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '../use-vega-wallet';
|
||||
import { useT } from '../use-t';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export interface VegaManageDialogProps {
|
||||
dialogOpen: boolean;
|
||||
@@ -18,7 +18,7 @@ export const VegaManageDialog = ({
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
}: VegaManageDialogProps) => {
|
||||
const t = useT();
|
||||
const { t } = useTranslation();
|
||||
const { pubKey, pubKeys, selectPubKey, disconnect } = useVegaWallet();
|
||||
return (
|
||||
<Dialog
|
||||
@@ -1,5 +1,5 @@
|
||||
import classNames from 'classnames';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||
import { Nav } from '../nav';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
import { Children, type ReactNode } from 'react';
|
||||
|
||||
export interface TemplateSidebarProps {
|
||||
children: React.ReactNode;
|
||||
sidebar: React.ReactNode[];
|
||||
children: ReactNode;
|
||||
sidebar: ReactNode;
|
||||
}
|
||||
|
||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
@@ -12,9 +12,9 @@ export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||
{children}
|
||||
</main>
|
||||
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
||||
{sidebar.map((Component, i) => (
|
||||
{Children.map(sidebar, (child, i) => (
|
||||
<section className="mb-4 last:mb-0" key={i}>
|
||||
{Component}
|
||||
{child}
|
||||
</section>
|
||||
))}
|
||||
</aside>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
@@ -10,9 +10,7 @@ interface VegaWalletContainerProps {
|
||||
export const VegaWalletContainer = ({ children }: VegaWalletContainerProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
|
||||
if (!pubKey) {
|
||||
return (
|
||||
|
||||
@@ -9,7 +9,7 @@ import Routes from '../../routes/routes';
|
||||
export const RiskMessage = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6 mb-6">
|
||||
<div className="bg-vega-light-100 dark:bg-vega-dark-100 p-6">
|
||||
<ul className="list-[square] ml-4">
|
||||
<li>
|
||||
{t(
|
||||
@@ -23,7 +23,7 @@ export const RiskMessage = () => {
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<p className="mb-8">
|
||||
<p>
|
||||
{t(
|
||||
'By using the Vega Governance App, you acknowledge that you have read and understood the'
|
||||
)}{' '}
|
||||
|
||||
@@ -1,25 +1,39 @@
|
||||
import {
|
||||
VegaConnectDialog,
|
||||
VegaManageDialog,
|
||||
ViewAsDialog,
|
||||
} from '@vegaprotocol/wallet';
|
||||
ConnectDialogWithRiskAck,
|
||||
useDialogStore,
|
||||
} from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { useConnectors } from '../../lib/vega-connectors';
|
||||
import { RiskMessage } from './risk-message';
|
||||
import { VegaManageDialog } from '../manage-dialog';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export const VegaWalletDialogs = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const { appState, appDispatch } = useAppState();
|
||||
const connectors = useConnectors();
|
||||
const [riskAccepted, setRiskAccepted] = useLocalStorage(
|
||||
'vega_wallet_risk_accepted'
|
||||
);
|
||||
const vegaWalletDialogOpen = useDialogStore((store) => store.isOpen);
|
||||
const setVegaWalletDialog = useDialogStore((store) => store.set);
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
connectors={connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
<ConnectDialogWithRiskAck
|
||||
open={vegaWalletDialogOpen}
|
||||
onChange={setVegaWalletDialog}
|
||||
riskAccepted={
|
||||
VEGA_ENV === Networks.TESTNET ? riskAccepted === 'true' : true
|
||||
}
|
||||
riskAckContent={<RiskMessage />}
|
||||
onRiskAccepted={() => setRiskAccepted('true')}
|
||||
onRiskRejected={() => {
|
||||
setRiskAccepted('false');
|
||||
setVegaWalletDialog(false);
|
||||
}}
|
||||
/>
|
||||
|
||||
<VegaManageDialog
|
||||
dialogOpen={appState.vegaWalletManageOverlay}
|
||||
setDialogOpen={(open) =>
|
||||
@@ -29,8 +43,6 @@ export const VegaWalletDialogs = () => {
|
||||
})
|
||||
}
|
||||
/>
|
||||
|
||||
<ViewAsDialog connector={connectors.view} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import vegaBlack from '../../images/vega_black.png';
|
||||
import vegaVesting from '../../images/vega_vesting.png';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { type WalletCardAssetProps } from '../wallet-card';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useContracts } from '../../contexts/contracts/contracts-context';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { useViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { useConnect } from '@vegaprotocol/wallet-react';
|
||||
|
||||
export const VegaWalletPrompt = () => {
|
||||
const { t } = useTranslation();
|
||||
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
|
||||
const { connect } = useConnect();
|
||||
return (
|
||||
<>
|
||||
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
|
||||
@@ -16,7 +16,7 @@ export const VegaWalletPrompt = () => {
|
||||
<ButtonLink
|
||||
className="text-neutral-500"
|
||||
data-testid="view-as-user"
|
||||
onClick={() => setViewAsDialog(true)}
|
||||
onClick={() => connect('viewParty')}
|
||||
>
|
||||
{t('viewAsParty')}
|
||||
</ButtonLink>
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from '../wallet-card';
|
||||
import { VegaWalletPrompt } from './vega-wallet-prompt';
|
||||
import { usePollForDelegations } from './hooks';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { usePendingBalancesStore } from '../../hooks/use-pending-balances-manager';
|
||||
@@ -34,16 +34,17 @@ import omit from 'lodash/omit';
|
||||
|
||||
export const VegaWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey, pubKeys } = useVegaWallet();
|
||||
const { status, pubKey, pubKeys } = useVegaWallet();
|
||||
const pubKeyObj = useMemo(() => {
|
||||
return pubKeys?.find((pk) => pk.publicKey === pubKey);
|
||||
}, [pubKey, pubKeys]);
|
||||
|
||||
const child = !pubKeys ? (
|
||||
<VegaWalletNotConnected />
|
||||
) : (
|
||||
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
||||
);
|
||||
const child =
|
||||
status === 'connected' ? (
|
||||
<VegaWalletConnected vegaKeys={pubKeys.map((pk) => pk.publicKey)} />
|
||||
) : (
|
||||
<VegaWalletNotConnected />
|
||||
);
|
||||
|
||||
return (
|
||||
<section className="vega-wallet" data-testid="vega-wallet">
|
||||
@@ -75,9 +76,7 @@ export const VegaWallet = () => {
|
||||
|
||||
const VegaWalletNotConnected = () => {
|
||||
const { t } = useTranslation();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useWeb3React } from '@web3-react/core';
|
||||
|
||||
export const useListenForStakingEvents = (
|
||||
contract: Contract | undefined,
|
||||
vegaPublicKey: string | null,
|
||||
vegaPublicKey: string | undefined,
|
||||
numberOfConfirmations: number
|
||||
) => {
|
||||
const { account } = useWeb3React();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import React from 'react';
|
||||
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
import { useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { TELEMETRY_ON } from '../components/telemetry-dialog/telemetry-dialog';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ENV } from '../config';
|
||||
import { isPartyNotFoundError } from '../lib/party';
|
||||
|
||||
export const useSentryInit = () => {
|
||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH } = useEnvironment();
|
||||
const [telemetryOn] = useLocalStorage(TELEMETRY_ON);
|
||||
|
||||
useEffect(() => {
|
||||
if (ENV.dsn && telemetryOn === 'true') {
|
||||
Sentry.init({
|
||||
dsn: ENV.dsn,
|
||||
tracesSampleRate: 0.1,
|
||||
enabled: true,
|
||||
environment: VEGA_ENV,
|
||||
release: GIT_COMMIT_HASH,
|
||||
beforeSend(event, hint) {
|
||||
const error = hint?.originalException;
|
||||
const errorIsString = typeof error === 'string';
|
||||
const errorIsObject = error instanceof Error;
|
||||
const requestUrl = event.request?.url;
|
||||
const transaction = event.transaction;
|
||||
|
||||
if (
|
||||
(errorIsString && isPartyNotFoundError({ message: error })) ||
|
||||
(errorIsObject && isPartyNotFoundError(error))
|
||||
) {
|
||||
// This error is caused by a pubkey making an API request before
|
||||
// it has interacted with the chain. This isn't needed in Sentry.
|
||||
return null;
|
||||
}
|
||||
|
||||
const updatedRequest =
|
||||
requestUrl && requestUrl.includes('/claim?')
|
||||
? { ...event.request, url: removeQueryParams(requestUrl) }
|
||||
: event.request;
|
||||
|
||||
const updatedTransaction =
|
||||
transaction && transaction.includes('/claim?')
|
||||
? removeQueryParams(transaction)
|
||||
: transaction;
|
||||
|
||||
const updatedBreadcrumbs = event.breadcrumbs?.map((breadcrumb) => {
|
||||
if (
|
||||
breadcrumb.type === 'navigation' &&
|
||||
breadcrumb.data?.to?.includes('/claim?')
|
||||
) {
|
||||
return {
|
||||
...breadcrumb,
|
||||
data: {
|
||||
...breadcrumb.data,
|
||||
to: removeQueryParams(breadcrumb.data.to),
|
||||
},
|
||||
};
|
||||
}
|
||||
return breadcrumb;
|
||||
});
|
||||
|
||||
return {
|
||||
...event,
|
||||
request: updatedRequest,
|
||||
transaction: updatedTransaction,
|
||||
breadcrumbs: updatedBreadcrumbs ?? event.breadcrumbs,
|
||||
};
|
||||
},
|
||||
});
|
||||
Sentry.setTag('branch', GIT_BRANCH);
|
||||
Sentry.setTag('commit', GIT_COMMIT_HASH);
|
||||
} else {
|
||||
Sentry.close();
|
||||
}
|
||||
}, [GIT_COMMIT_HASH, GIT_BRANCH, VEGA_ENV, telemetryOn]);
|
||||
};
|
||||
|
||||
const removeQueryParams = (url: string) => {
|
||||
return url.split('?')[0];
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
InjectedConnector,
|
||||
JsonRpcConnector,
|
||||
SnapConnector,
|
||||
ViewPartyConnector,
|
||||
createConfig,
|
||||
fairground,
|
||||
stagnet,
|
||||
mainnet,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
export const useVegaWalletConfig = () => {
|
||||
const { VEGA_ENV, VEGA_URL, VEGA_WALLET_URL } = useEnvironment();
|
||||
return useMemo(() => {
|
||||
if (!VEGA_ENV || !VEGA_URL || !VEGA_WALLET_URL) return;
|
||||
|
||||
const injected = new InjectedConnector();
|
||||
|
||||
const jsonRpc = new JsonRpcConnector({
|
||||
url: VEGA_WALLET_URL,
|
||||
});
|
||||
|
||||
const snap = new SnapConnector({
|
||||
node: new URL(VEGA_URL).origin,
|
||||
snapId: 'npm:@vegaprotocol/snap',
|
||||
version: '1.0.1',
|
||||
});
|
||||
|
||||
const viewParty = new ViewPartyConnector();
|
||||
|
||||
const config = createConfig({
|
||||
chains: [mainnet, fairground, stagnet],
|
||||
defaultChainId: fairground.id,
|
||||
connectors: [injected, snap, jsonRpc, viewParty],
|
||||
});
|
||||
|
||||
return config;
|
||||
}, [VEGA_ENV, VEGA_URL, VEGA_WALLET_URL]);
|
||||
};
|
||||
@@ -31,7 +31,7 @@ i18n
|
||||
load: 'languageOnly',
|
||||
debug: isInDev,
|
||||
// have a common namespace used around the full app
|
||||
ns: ['governance'],
|
||||
ns: ['governance', 'wallet', 'wallet-react'],
|
||||
defaultNS: 'governance',
|
||||
keySeparator: false, // we use content as keys
|
||||
nsSeparator: false,
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
InjectedConnector,
|
||||
SnapConnector,
|
||||
DEFAULT_SNAP_ID,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||
|
||||
export const useConnectors = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return useMemo(
|
||||
() => ({
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||
}),
|
||||
[featureFlags.METAMASK_SNAPS]
|
||||
);
|
||||
};
|
||||
+7
-12
@@ -5,7 +5,6 @@ import {
|
||||
ProposalState,
|
||||
VoteValue,
|
||||
} from '@vegaprotocol/types';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import {
|
||||
generateNoVotes,
|
||||
@@ -16,9 +15,7 @@ import { ProposalHeader, NewTransferSummary } from './proposal-header';
|
||||
import {
|
||||
lastWeek,
|
||||
nextWeek,
|
||||
mockWalletContext,
|
||||
createUserVoteQueryMock,
|
||||
networkParamsQueryMock,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { VoteState } from '../vote-details/use-user-vote';
|
||||
@@ -45,14 +42,12 @@ const renderComponent = (
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<BrowserRouter>
|
||||
<MockedProvider mocks={[networkParamsQueryMock, ...mocks]}>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<ProposalHeader
|
||||
proposal={proposal}
|
||||
isListItem={isListItem}
|
||||
voteState={voteState}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<ProposalHeader
|
||||
proposal={proposal}
|
||||
isListItem={isListItem}
|
||||
voteState={voteState}
|
||||
/>
|
||||
</MockedProvider>
|
||||
</BrowserRouter>
|
||||
</AppStateProvider>
|
||||
@@ -160,7 +155,7 @@ describe('Proposal header', () => {
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Update to market: MarketId'
|
||||
/Update to market: MarketId/
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import { type VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { Proposal } from './proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { type Proposal as IProposal } from '../../types';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||
@@ -24,45 +24,44 @@ jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||
error: null,
|
||||
})),
|
||||
}));
|
||||
|
||||
jest.mock('../proposal-detail-header/proposal-header', () => ({
|
||||
ProposalHeader: () => <div data-testid="proposal-header"></div>,
|
||||
}));
|
||||
|
||||
jest.mock('../proposal-change-table', () => ({
|
||||
ProposalChangeTable: () => <div data-testid="proposal-change-table"></div>,
|
||||
}));
|
||||
|
||||
jest.mock('../proposal-json', () => ({
|
||||
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
||||
}));
|
||||
|
||||
jest.mock('../list-asset', () => ({
|
||||
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
|
||||
}));
|
||||
|
||||
jest.mock('./proposal-change-details', () => ({
|
||||
ProposalChangeDetails: () => (
|
||||
<div data-testid="proposal-change-details"></div>
|
||||
),
|
||||
jest.mock('../list-asset', () => ({
|
||||
ListAsset: () => <div data-testid="proposal-list-asset"></div>,
|
||||
}));
|
||||
|
||||
const vegaWalletConfig: VegaWalletConfig = {
|
||||
network: 'TESTNET',
|
||||
vegaUrl: 'https://vega.xyz',
|
||||
vegaWalletServiceUrl: 'https://wallet.vega.xyz',
|
||||
links: {
|
||||
explorer: 'explorer',
|
||||
concepts: 'concepts',
|
||||
chromeExtensionUrl: 'chrome',
|
||||
mozillaExtensionUrl: 'mozilla',
|
||||
},
|
||||
chainId: 'VEGA_CHAIN_ID',
|
||||
};
|
||||
jest.mock('../vote-details', () => ({
|
||||
UserVote: () => <div data-testid="user-vote"></div>,
|
||||
}));
|
||||
|
||||
jest.mock('./proposal-change-details', () => ({
|
||||
ProposalChangeDetails: () => <div data-testid="proposal-change-details" />,
|
||||
}));
|
||||
|
||||
const renderComponent = (proposal: IProposal) => {
|
||||
render(
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<VegaWalletProvider config={vegaWalletConfig}>
|
||||
<Proposal restData={null} proposal={proposal} />
|
||||
</VegaWalletProvider>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<Proposal restData={null} proposal={proposal} />
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
@@ -23,7 +23,7 @@ export interface ProposalProps {
|
||||
|
||||
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
|
||||
const { submit, finalizedVote, transaction } = useVoteSubmit();
|
||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||
|
||||
return (
|
||||
@@ -88,7 +88,6 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||
<UserVote
|
||||
proposal={proposal}
|
||||
submit={submit}
|
||||
dialog={Dialog}
|
||||
transaction={transaction}
|
||||
voteState={voteState}
|
||||
voteDatetime={voteDatetime}
|
||||
|
||||
+3
-4
@@ -1,15 +1,14 @@
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
|
||||
const renderComponent = (id: string) =>
|
||||
render(
|
||||
<Router>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<AppStateProvider>
|
||||
<ProposalsListItemDetails id={id} />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</Router>
|
||||
);
|
||||
|
||||
|
||||
+29
-28
@@ -1,25 +1,32 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { act, render, screen } from '@testing-library/react';
|
||||
import {
|
||||
MockedWalletProvider,
|
||||
mockConfig,
|
||||
} from '@vegaprotocol/wallet-react/testing';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { ProposalFormSubmit } from './proposal-form-submit';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
|
||||
const renderComponent = (
|
||||
context: VegaWalletContextShape,
|
||||
isSubmitting: boolean
|
||||
) => {
|
||||
const renderComponent = (isSubmitting: boolean) => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={context}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe('Proposal Form Submit', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
mockConfig.reset();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display connection message and button if wallet not connected', () => {
|
||||
renderComponent({ pubKey: null } as VegaWalletContextShape, false);
|
||||
renderComponent(false);
|
||||
|
||||
expect(
|
||||
screen.getByText('Connect your wallet to submit a proposal')
|
||||
@@ -30,28 +37,22 @@ describe('Proposal Form Submit', () => {
|
||||
});
|
||||
|
||||
it('should display submit button if wallet is connected', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
renderComponent(
|
||||
{
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape,
|
||||
false
|
||||
);
|
||||
mockConfig.store.setState({
|
||||
pubKey: pubKey.publicKey,
|
||||
keys: [pubKey],
|
||||
});
|
||||
renderComponent(false);
|
||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||
'Submit proposal'
|
||||
);
|
||||
});
|
||||
|
||||
it('should display submitting button text if wallet is connected and submitting', () => {
|
||||
const pubKey = { publicKey: '123456__123456', name: 'test' };
|
||||
renderComponent(
|
||||
{
|
||||
pubKey: pubKey.publicKey,
|
||||
pubKeys: [pubKey],
|
||||
} as VegaWalletContextShape,
|
||||
true
|
||||
);
|
||||
mockConfig.store.setState({
|
||||
pubKey: pubKey.publicKey,
|
||||
keys: [pubKey],
|
||||
});
|
||||
renderComponent(true);
|
||||
expect(screen.getByTestId('proposal-submit')).toHaveTextContent(
|
||||
'Submitting proposal'
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { VegaWalletContainer } from '../../../../components/vega-wallet-container';
|
||||
|
||||
interface ProposalFormSubmitProps {
|
||||
|
||||
+13
-5
@@ -1,19 +1,24 @@
|
||||
import {
|
||||
VegaTransactionDialog,
|
||||
getProposalDialogIcon,
|
||||
getProposalDialogIntent,
|
||||
useGetProposalDialogTitle,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import type { ProposalEventFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import type { DialogProps } from '@vegaprotocol/proposals';
|
||||
import type {
|
||||
ProposalEventFieldsFragment,
|
||||
VegaTxState,
|
||||
} from '@vegaprotocol/proposals';
|
||||
|
||||
interface ProposalFormTransactionDialogProps {
|
||||
finalizedProposal: ProposalEventFieldsFragment | null;
|
||||
TransactionDialog: (props: DialogProps) => JSX.Element;
|
||||
transaction: VegaTxState;
|
||||
onChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const ProposalFormTransactionDialog = ({
|
||||
finalizedProposal,
|
||||
TransactionDialog,
|
||||
transaction,
|
||||
onChange,
|
||||
}: ProposalFormTransactionDialogProps) => {
|
||||
const title = useGetProposalDialogTitle(finalizedProposal?.state);
|
||||
// Render a custom complete UI if the proposal was rejected otherwise
|
||||
@@ -24,13 +29,16 @@ export const ProposalFormTransactionDialog = ({
|
||||
|
||||
return (
|
||||
<div data-testid="proposal-transaction-dialog">
|
||||
<TransactionDialog
|
||||
<VegaTransactionDialog
|
||||
title={title}
|
||||
intent={getProposalDialogIntent(finalizedProposal?.state)}
|
||||
icon={getProposalDialogIcon(finalizedProposal?.state)}
|
||||
content={{
|
||||
Complete: completeContent,
|
||||
}}
|
||||
transaction={transaction}
|
||||
isOpen={transaction.dialogOpen}
|
||||
onChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
+2
-5
@@ -1,10 +1,8 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
lastWeek,
|
||||
mockWalletContext,
|
||||
networkParamsQueryMock,
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
@@ -48,9 +46,7 @@ const renderComponent = (
|
||||
render(
|
||||
<Router>
|
||||
<MockedProvider mocks={mocks}>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteBreakdown proposal={proposal} />
|
||||
</VegaWalletContext.Provider>
|
||||
<VoteBreakdown proposal={proposal} />
|
||||
</MockedProvider>
|
||||
</Router>
|
||||
);
|
||||
@@ -60,6 +56,7 @@ describe('VoteBreakdown', () => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(0);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { captureMessage } from '@sentry/minimal';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { VoteValue } from '@vegaprotocol/types';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useUserVoteQuery } from './__generated__/Vote';
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||
import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type VoteValue } from '@vegaprotocol/types';
|
||||
import { type DialogProps, type VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { type VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { type VoteState } from './use-user-vote';
|
||||
import { type Proposal, type BatchProposal } from '../../types';
|
||||
|
||||
interface UserVoteProps {
|
||||
proposal: Proposal | BatchProposal;
|
||||
transaction: VegaTxState | null;
|
||||
transaction: VegaTxState;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
voteState: VoteState | null;
|
||||
voteDatetime: Date | null;
|
||||
}
|
||||
@@ -23,7 +22,6 @@ export const UserVote = ({
|
||||
proposal,
|
||||
submit,
|
||||
transaction,
|
||||
dialog,
|
||||
voteState,
|
||||
voteDatetime,
|
||||
}: UserVoteProps) => {
|
||||
@@ -56,7 +54,6 @@ export const UserVote = ({
|
||||
className="flex"
|
||||
submit={submit}
|
||||
transaction={transaction}
|
||||
dialog={dialog}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
||||
+36
-34
@@ -2,33 +2,20 @@ import { render, screen } from '@testing-library/react';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import { VegaTxStatus } from '@vegaprotocol/proposals';
|
||||
import { ConnectorErrors, unknownError } from '@vegaprotocol/wallet';
|
||||
|
||||
describe('VoteTransactionDialog', () => {
|
||||
const mockTransactionDialog = jest.fn(({ title, content }) => (
|
||||
<div>
|
||||
<div>{title}</div>
|
||||
<div>{content?.Complete}</div>
|
||||
</div>
|
||||
));
|
||||
|
||||
it('renders without crashing', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('vote-transaction-dialog')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders with txRequested title when voteState is Requested', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Requested}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
transaction={{
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Requested,
|
||||
dialogOpen: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -39,8 +26,13 @@ describe('VoteTransactionDialog', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Pending}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
transaction={{
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Pending,
|
||||
dialogOpen: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -51,8 +43,13 @@ describe('VoteTransactionDialog', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes} // or any other state other than Requested or Pending
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
transaction={{
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Complete,
|
||||
dialogOpen: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -65,17 +62,18 @@ describe('VoteTransactionDialog', () => {
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Failed}
|
||||
transaction={{
|
||||
error: { message: 'Custom error test message', name: 'blah' },
|
||||
error: unknownError(),
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Error,
|
||||
dialogOpen: false,
|
||||
dialogOpen: true,
|
||||
}}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.getByText('Custom error test message')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(ConnectorErrors.unknown.message)
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders default error message when voteState is failed and no error message exists on the tx', () => {
|
||||
@@ -86,10 +84,9 @@ describe('VoteTransactionDialog', () => {
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Error,
|
||||
dialogOpen: false,
|
||||
status: VegaTxStatus.Complete,
|
||||
dialogOpen: true,
|
||||
}}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -100,8 +97,13 @@ describe('VoteTransactionDialog', () => {
|
||||
render(
|
||||
<VoteTransactionDialog
|
||||
voteState={VoteState.Yes}
|
||||
transaction={null}
|
||||
TransactionDialog={mockTransactionDialog}
|
||||
transaction={{
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
status: VegaTxStatus.Default,
|
||||
dialogOpen: true,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
+66
-153
@@ -1,120 +1,77 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { VoteButtons } from './vote-buttons';
|
||||
import { VoteButtons, type VoteButtonsProps } from './vote-buttons';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { MockedProvider } from '@apollo/react-testing';
|
||||
import { VegaTxStatus } from '@vegaprotocol/proposals';
|
||||
import {
|
||||
MockedWalletProvider,
|
||||
mockConfig,
|
||||
} from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
describe('Vote buttons', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(
|
||||
const key = { publicKey: '0x123', name: 'key 1' };
|
||||
const transaction = {
|
||||
status: VegaTxStatus.Default,
|
||||
error: null,
|
||||
txHash: null,
|
||||
signature: null,
|
||||
dialogOpen: false,
|
||||
};
|
||||
const props = {
|
||||
voteState: VoteState.NotCast,
|
||||
voteDatetime: null,
|
||||
proposalState: ProposalState.STATE_OPEN,
|
||||
proposalId: null,
|
||||
minVoterBalance: null,
|
||||
spamProtectionMinTokens: null,
|
||||
currentStakeAvailable: new BigNumber(1),
|
||||
submit: () => Promise.resolve(),
|
||||
transaction,
|
||||
};
|
||||
|
||||
const renderComponent = (testProps?: Partial<VoteButtonsProps>) => {
|
||||
return render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.NotCast}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_OPEN}
|
||||
proposalId={null}
|
||||
minVoterBalance={null}
|
||||
spamProtectionMinTokens={null}
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
<MockedWalletProvider>
|
||||
<VoteButtons {...props} {...testProps} />
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockConfig.store.setState({ pubKey: key.publicKey, keys: [key] });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
mockConfig.reset();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should explain that voting is closed if the proposal is not open', () => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.NotCast}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_PASSED}
|
||||
proposalId={null}
|
||||
minVoterBalance={null}
|
||||
spamProtectionMinTokens={null}
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
renderComponent({ proposalState: ProposalState.STATE_PASSED });
|
||||
expect(screen.getByText('Voting has ended.')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should provide a connect wallet prompt if no pubkey', () => {
|
||||
const mockWalletNoPubKeyContext = {
|
||||
pubKey: null,
|
||||
pubKeys: [],
|
||||
isReadOnly: false,
|
||||
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||
connect: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
selectPubKey: jest.fn(),
|
||||
connector: null,
|
||||
} as unknown as VegaWalletContextShape;
|
||||
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletNoPubKeyContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.NotCast}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_OPEN}
|
||||
proposalId={null}
|
||||
minVoterBalance={null}
|
||||
spamProtectionMinTokens={null}
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
|
||||
mockConfig.reset();
|
||||
renderComponent();
|
||||
expect(screen.getByTestId('connect-wallet')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should tell the user they need tokens if their current stake is 0', () => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.NotCast}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_OPEN}
|
||||
proposalId={null}
|
||||
minVoterBalance={null}
|
||||
spamProtectionMinTokens={null}
|
||||
currentStakeAvailable={new BigNumber(0)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
renderComponent({ currentStakeAvailable: new BigNumber(0) });
|
||||
expect(
|
||||
screen.getByText(
|
||||
'You need some VEGA tokens to participate in governance.'
|
||||
@@ -123,26 +80,10 @@ describe('Vote buttons', () => {
|
||||
});
|
||||
|
||||
it('should tell the user of the minimum requirements if they have some, but not enough tokens', () => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.NotCast}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_OPEN}
|
||||
proposalId={null}
|
||||
minVoterBalance="2000000000000000000"
|
||||
spamProtectionMinTokens="1000000000000000000"
|
||||
currentStakeAvailable={new BigNumber(1)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
renderComponent({
|
||||
minVoterBalance: '2000000000000000000',
|
||||
spamProtectionMinTokens: '1000000000000000000',
|
||||
});
|
||||
expect(
|
||||
screen.getByText(
|
||||
'You must have at least 2 VEGA associated to vote on this proposal'
|
||||
@@ -151,51 +92,23 @@ describe('Vote buttons', () => {
|
||||
});
|
||||
|
||||
it('should show you voted if vote state is correct, and if the proposal is still open, it will display a change vote button', () => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.Yes}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_OPEN}
|
||||
proposalId={null}
|
||||
minVoterBalance="2000000000000000000"
|
||||
spamProtectionMinTokens="1000000000000000000"
|
||||
currentStakeAvailable={new BigNumber(10)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
renderComponent({
|
||||
voteState: VoteState.Yes,
|
||||
minVoterBalance: '2000000000000000000',
|
||||
spamProtectionMinTokens: '1000000000000000000',
|
||||
currentStakeAvailable: new BigNumber(10),
|
||||
});
|
||||
expect(screen.getByTestId('you-voted')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('change-vote-button')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should allow you to change your vote', () => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<VoteButtons
|
||||
voteState={VoteState.No}
|
||||
voteDatetime={null}
|
||||
proposalState={ProposalState.STATE_OPEN}
|
||||
proposalId={null}
|
||||
minVoterBalance="2000000000000000000"
|
||||
spamProtectionMinTokens="1000000000000000000"
|
||||
currentStakeAvailable={new BigNumber(10)}
|
||||
dialog={() => <div>Blah</div>}
|
||||
submit={() => Promise.resolve()}
|
||||
transaction={null}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
renderComponent({
|
||||
voteState: VoteState.No,
|
||||
minVoterBalance: '2000000000000000000',
|
||||
spamProtectionMinTokens: '1000000000000000000',
|
||||
currentStakeAvailable: new BigNumber(10),
|
||||
});
|
||||
fireEvent.click(screen.getByTestId('change-vote-button'));
|
||||
expect(screen.getByTestId('vote-buttons')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { format } from 'date-fns';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet, useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
Button,
|
||||
@@ -17,7 +17,7 @@ import { VoteState } from './use-user-vote';
|
||||
import { ProposalMinRequirements, ProposalUserAction } from '../shared';
|
||||
import { VoteTransactionDialog } from './vote-transaction-dialog';
|
||||
import { useVoteButtonsQuery } from './__generated__/Stake';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||
import type { VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { filterAcceptableGraphqlErrors } from '../../../../lib/party';
|
||||
import {
|
||||
NetworkParams,
|
||||
@@ -32,8 +32,7 @@ interface VoteButtonsContainerProps {
|
||||
proposalId: string | null;
|
||||
proposalState: ProposalState;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
transaction: VegaTxState | null;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
transaction: VegaTxState;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -136,17 +135,16 @@ export const VoteButtonsContainer = (props: VoteButtonsContainerProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface VoteButtonsProps {
|
||||
export interface VoteButtonsProps {
|
||||
voteState: VoteState | null;
|
||||
voteDatetime: Date | null;
|
||||
proposalId: string | null;
|
||||
proposalState: ProposalState;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
transaction: VegaTxState | null;
|
||||
dialog: (props: DialogProps) => JSX.Element;
|
||||
proposalId: string | null;
|
||||
currentStakeAvailable: BigNumber;
|
||||
minVoterBalance: string | null;
|
||||
spamProtectionMinTokens: string | null;
|
||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||
transaction: VegaTxState;
|
||||
}
|
||||
|
||||
export const VoteButtons = ({
|
||||
@@ -159,13 +157,10 @@ export const VoteButtons = ({
|
||||
spamProtectionMinTokens,
|
||||
submit,
|
||||
transaction,
|
||||
dialog: Dialog,
|
||||
}: VoteButtonsProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
const [changeVote, setChangeVote] = React.useState(false);
|
||||
const proposalVotable = useMemo(
|
||||
() =>
|
||||
@@ -184,11 +179,7 @@ export const VoteButtons = ({
|
||||
if (!pubKey) {
|
||||
return (
|
||||
<div data-testid="connect-wallet">
|
||||
<ButtonLink
|
||||
onClick={() => {
|
||||
openVegaWalletDialog();
|
||||
}}
|
||||
>
|
||||
<ButtonLink onClick={openVegaWalletDialog}>
|
||||
{t('connectVegaWallet')}
|
||||
</ButtonLink>{' '}
|
||||
{t('toVote')}
|
||||
@@ -301,11 +292,7 @@ export const VoteButtons = ({
|
||||
</p>
|
||||
)
|
||||
)}
|
||||
<VoteTransactionDialog
|
||||
voteState={voteState}
|
||||
transaction={transaction}
|
||||
TransactionDialog={Dialog}
|
||||
/>
|
||||
<VoteTransactionDialog voteState={voteState} transaction={transaction} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+9
-6
@@ -1,11 +1,13 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { VoteState } from './use-user-vote';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||
import {
|
||||
VegaTransactionDialog,
|
||||
type VegaTxState,
|
||||
} from '@vegaprotocol/proposals';
|
||||
|
||||
interface VoteTransactionDialogProps {
|
||||
voteState: VoteState;
|
||||
transaction: VegaTxState | null;
|
||||
TransactionDialog: (props: DialogProps) => JSX.Element;
|
||||
transaction: VegaTxState;
|
||||
}
|
||||
|
||||
const dialogTitle = (voteState: VoteState): string | undefined => {
|
||||
@@ -22,22 +24,23 @@ const dialogTitle = (voteState: VoteState): string | undefined => {
|
||||
export const VoteTransactionDialog = ({
|
||||
voteState,
|
||||
transaction,
|
||||
TransactionDialog,
|
||||
}: VoteTransactionDialogProps) => {
|
||||
// Render a custom message if the voting fails otherwise
|
||||
// pass undefined so that the default vega transaction dialog UI gets used
|
||||
const customMessage =
|
||||
voteState === VoteState.Failed ? (
|
||||
<p>{transaction?.error?.message || t('voteError')}</p>
|
||||
<p>{transaction.error?.message || t('voteError')}</p>
|
||||
) : undefined;
|
||||
|
||||
return (
|
||||
<div data-testid="vote-transaction-dialog">
|
||||
<TransactionDialog
|
||||
<VegaTransactionDialog
|
||||
title={dialogTitle(voteState)}
|
||||
transaction={transaction}
|
||||
content={{
|
||||
Complete: customMessage,
|
||||
}}
|
||||
isOpen={transaction.dialogOpen}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ProposeFreeform } from './propose-freeform';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
@@ -72,18 +71,19 @@ const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
const renderComponent = () => {
|
||||
return render(
|
||||
<Router>
|
||||
<MockedProvider mocks={[updateMarketNetworkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposeFreeform />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||
// components are tested in their own directory.
|
||||
|
||||
@@ -51,7 +51,8 @@ export const ProposeFreeform = () => {
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<FreeformProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const assembleProposal = (fields: FreeformProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum =
|
||||
@@ -169,7 +170,8 @@ export const ProposeFreeform = () => {
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+10
-10
@@ -1,13 +1,12 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react';
|
||||
import { ProposeNetworkParameter } from './propose-network-parameter';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
@@ -72,26 +71,27 @@ const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
const renderComponent = () => {
|
||||
return render(
|
||||
<Router>
|
||||
<MockedProvider mocks={[updateMarketNetworkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposeNetworkParameter />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||
// components are tested in their own directory.
|
||||
|
||||
describe('Propose Network Parameter', () => {
|
||||
it('should render successfully', async () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
await expect(baseElement).toBeTruthy();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the correct title', async () => {
|
||||
|
||||
+4
-2
@@ -91,7 +91,8 @@ export const ProposeNetworkParameter = () => {
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<NetworkParameterProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const selectedParamEntry = params
|
||||
? Object.entries(params).find(([key]) => key === selectedNetworkParam)
|
||||
@@ -312,7 +313,8 @@ export const ProposeNetworkParameter = () => {
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { ProposeNewAsset } from './propose-new-asset';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
@@ -72,26 +71,27 @@ const newAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
const renderComponent = () => {
|
||||
return render(
|
||||
<Router>
|
||||
<MockedProvider mocks={[newAssetNetworkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposeNewAsset />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||
// components are tested in their own directory.
|
||||
|
||||
describe('Propose New Asset', () => {
|
||||
it('should render successfully', async () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
await expect(baseElement).toBeTruthy();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the title', async () => {
|
||||
|
||||
@@ -64,7 +64,8 @@ export const ProposeNewAsset = () => {
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<NewAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const assembleProposal = (fields: NewAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
@@ -232,7 +233,8 @@ export const ProposeNewAsset = () => {
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+10
-10
@@ -1,13 +1,12 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ProposeNewMarket } from './propose-new-market';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
@@ -72,26 +71,27 @@ const newMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
const renderComponent = () => {
|
||||
return render(
|
||||
<Router>
|
||||
<MockedProvider mocks={[newMarketNetworkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposeNewMarket />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||
// components are tested in their own directory.
|
||||
|
||||
describe('Propose New Market', () => {
|
||||
it('should render successfully', async () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
await expect(baseElement).toBeTruthy();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the form components', async () => {
|
||||
|
||||
@@ -62,7 +62,8 @@ export const ProposeNewMarket = () => {
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<NewMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const assembleProposal = (fields: NewMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
@@ -214,7 +215,8 @@ export const ProposeNewMarket = () => {
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -3,8 +3,6 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { addHours, getTime } from 'date-fns';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ProposeRaw } from './propose-raw';
|
||||
import { ProposalEventDocument } from '@vegaprotocol/proposals';
|
||||
@@ -12,6 +10,11 @@ import type { ProposalEventSubscription } from '@vegaprotocol/proposals';
|
||||
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import {
|
||||
MockedWalletProvider,
|
||||
mockConfig,
|
||||
} from '@vegaprotocol/wallet-react/testing';
|
||||
import { userRejectedError } from '@vegaprotocol/wallet';
|
||||
|
||||
const paramsDelay = 20;
|
||||
|
||||
@@ -103,23 +106,15 @@ describe('Raw proposal form', () => {
|
||||
},
|
||||
delay: 300,
|
||||
};
|
||||
const setup = (mockSendTx = jest.fn()) => {
|
||||
const setup = () => {
|
||||
return render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider
|
||||
mocks={[rawProposalNetworkParamsQueryMock, mockProposalEvent]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={
|
||||
{
|
||||
pubKey,
|
||||
sendTx: mockSendTx,
|
||||
links: { explorer: 'explorer' },
|
||||
} as unknown as VegaWalletContextShape
|
||||
}
|
||||
>
|
||||
<MockedWalletProvider>
|
||||
<ProposeRaw />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
@@ -127,15 +122,22 @@ describe('Raw proposal form', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
jest.useFakeTimers();
|
||||
mockConfig.store.setState({ status: 'connected', pubKey: '0x123' });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
mockConfig.reset();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('handles validation', async () => {
|
||||
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve());
|
||||
setup(mockSendTx);
|
||||
const mockSendTx = jest.spyOn(mockConfig, 'sendTransaction');
|
||||
|
||||
setup();
|
||||
|
||||
expect(await screen.findByTestId('proposal-submit')).toBeTruthy();
|
||||
await act(async () => {
|
||||
@@ -162,20 +164,25 @@ describe('Raw proposal form', () => {
|
||||
});
|
||||
|
||||
it('sends the transaction', async () => {
|
||||
const mockSendTx = jest.fn().mockReturnValue(
|
||||
new Promise((resolve) => {
|
||||
setTimeout(
|
||||
() =>
|
||||
resolve({
|
||||
transactionHash: 'tx-hash',
|
||||
signature:
|
||||
'cfe592d169f87d0671dd447751036d0dddc165b9c4b65e5a5060e2bbadd1aa726d4cbe9d3c3b327bcb0bff4f83999592619a2493f9bbd251fae99ce7ce766909',
|
||||
}),
|
||||
100
|
||||
);
|
||||
})
|
||||
);
|
||||
setup(mockSendTx);
|
||||
const mockSendTx = jest
|
||||
.spyOn(mockConfig, 'sendTransaction')
|
||||
.mockReturnValue(
|
||||
new Promise((resolve) => {
|
||||
setTimeout(
|
||||
() =>
|
||||
resolve({
|
||||
transactionHash: 'tx-hash',
|
||||
signature:
|
||||
'cfe592d169f87d0671dd447751036d0dddc165b9c4b65e5a5060e2bbadd1aa726d4cbe9d3c3b327bcb0bff4f83999592619a2493f9bbd251fae99ce7ce766909',
|
||||
sentAt: new Date().toISOString(),
|
||||
receivedAt: new Date().toISOString(),
|
||||
}),
|
||||
100
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
setup();
|
||||
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(paramsDelay);
|
||||
@@ -206,8 +213,12 @@ describe('Raw proposal form', () => {
|
||||
fireEvent.click(screen.getByTestId('proposal-submit'));
|
||||
});
|
||||
|
||||
expect(mockSendTx).toHaveBeenCalledWith(pubKey, {
|
||||
proposalSubmission: JSON.parse(inputJSON),
|
||||
expect(mockSendTx).toHaveBeenCalledWith({
|
||||
publicKey: pubKey,
|
||||
sendingMode: 'TYPE_SYNC',
|
||||
transaction: {
|
||||
proposalSubmission: JSON.parse(inputJSON),
|
||||
},
|
||||
});
|
||||
|
||||
expect(screen.getByTestId('dialog-title')).toHaveTextContent(
|
||||
@@ -232,12 +243,12 @@ describe('Raw proposal form', () => {
|
||||
});
|
||||
|
||||
it('can be rejected by the user', async () => {
|
||||
const mockSendTx = jest.fn().mockReturnValue(
|
||||
new Promise((resolve) => {
|
||||
setTimeout(() => resolve(null), 100);
|
||||
jest.spyOn(mockConfig, 'sendTransaction').mockReturnValue(
|
||||
new Promise((_, reject) => {
|
||||
setTimeout(() => reject(userRejectedError()), 100);
|
||||
})
|
||||
);
|
||||
setup(mockSendTx);
|
||||
setup();
|
||||
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(paramsDelay);
|
||||
|
||||
@@ -52,7 +52,8 @@ export const ProposeRaw = () => {
|
||||
handleSubmit,
|
||||
formState: { isSubmitting, errors },
|
||||
} = useForm<RawProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const hasError = Boolean(errors.rawProposalData?.message);
|
||||
|
||||
@@ -152,7 +153,8 @@ export const ProposeRaw = () => {
|
||||
<ProposalFormSubmit isSubmitting={isSubmitting} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+10
-10
@@ -1,13 +1,12 @@
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { ProposeUpdateAsset } from './propose-update-asset';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
...jest.requireActual('@vegaprotocol/environment'),
|
||||
@@ -72,26 +71,27 @@ const updateAssetNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
const renderComponent = () => {
|
||||
return render(
|
||||
<Router>
|
||||
<MockedProvider mocks={[updateAssetNetworkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposeUpdateAsset />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</MockedProvider>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||
// components are tested in their own directory.
|
||||
|
||||
describe('Propose Update Asset', () => {
|
||||
it('should render successfully', async () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
await expect(baseElement).toBeTruthy();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the title', async () => {
|
||||
|
||||
@@ -62,7 +62,8 @@ export const ProposeUpdateAsset = () => {
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<UpdateAssetProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const assembleProposal = (fields: UpdateAssetProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
@@ -218,7 +219,8 @@ export const ProposeUpdateAsset = () => {
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
+10
-10
@@ -2,15 +2,14 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { ProposeUpdateMarket } from './propose-update-market';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import type { ProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
||||
import { ProposalMarketsQueryDocument } from './__generated__/UpdateMarket';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { MockedWalletProvider } from '@vegaprotocol/wallet-react/testing';
|
||||
|
||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
@@ -217,29 +216,30 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = () =>
|
||||
render(
|
||||
const renderComponent = () => {
|
||||
return render(
|
||||
<MockedProvider
|
||||
mocks={[updateMarketNetworkParamsQueryMock, marketQueryMock]}
|
||||
addTypename={false}
|
||||
>
|
||||
<Router>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<MockedWalletProvider>
|
||||
<AppStateProvider>
|
||||
<ProposeUpdateMarket />
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</AppStateProvider>
|
||||
</MockedWalletProvider>
|
||||
</Router>
|
||||
</MockedProvider>
|
||||
);
|
||||
};
|
||||
|
||||
// Note: form submission is tested in propose-raw.spec.tsx. Reusable form
|
||||
// components are tested in their own directory.
|
||||
|
||||
describe('Propose Update Market', () => {
|
||||
it('should render successfully', async () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = renderComponent();
|
||||
await expect(baseElement).toBeTruthy();
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the title', async () => {
|
||||
|
||||
+4
-2
@@ -109,7 +109,8 @@ export const ProposeUpdateMarket = () => {
|
||||
watch,
|
||||
trigger,
|
||||
} = useForm<UpdateMarketProposalFormFields>();
|
||||
const { finalizedProposal, submit, Dialog } = useProposalSubmit();
|
||||
const { finalizedProposal, transaction, submit, setTransaction } =
|
||||
useProposalSubmit();
|
||||
|
||||
const assembleProposal = (fields: UpdateMarketProposalFormFields) => {
|
||||
const isVoteDeadlineAtMinimum = doesValueEquateToParam(
|
||||
@@ -323,7 +324,8 @@ export const ProposeUpdateMarket = () => {
|
||||
<ProposalFormDownloadJson downloadJson={viewJson} />
|
||||
<ProposalFormTransactionDialog
|
||||
finalizedProposal={finalizedProposal}
|
||||
TransactionDialog={Dialog}
|
||||
transaction={transaction}
|
||||
onChange={(open) => setTransaction({ dialogOpen: open })}
|
||||
/>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -1,28 +1,17 @@
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import type { PubKey, VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import type { VoteValue } from '@vegaprotocol/types';
|
||||
import type { UserVoteQuery } from '../components/vote-details/__generated__/Vote';
|
||||
import { UserVoteDocument } from '../components/vote-details/__generated__/Vote';
|
||||
import faker from 'faker';
|
||||
import { type Key } from '@vegaprotocol/wallet';
|
||||
|
||||
export const mockPubkey: PubKey = {
|
||||
export const mockPubkey: Key = {
|
||||
publicKey: '0x123',
|
||||
name: 'test key 1',
|
||||
};
|
||||
|
||||
export const mockWalletContext = {
|
||||
pubKey: mockPubkey.publicKey,
|
||||
pubKeys: [mockPubkey],
|
||||
isReadOnly: false,
|
||||
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
|
||||
connect: jest.fn(),
|
||||
disconnect: jest.fn(),
|
||||
selectPubKey: jest.fn(),
|
||||
connector: null,
|
||||
} as unknown as VegaWalletContextShape;
|
||||
|
||||
const mockEthereumConfig = {
|
||||
network_id: '3',
|
||||
chain_id: '3',
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import classNames from 'classnames';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../components/heading';
|
||||
|
||||
export const ConnectToSeeRewards = () => {
|
||||
const { openVegaWalletDialog } = useVegaWalletDialogStore((store) => ({
|
||||
openVegaWalletDialog: store.openVegaWalletDialog,
|
||||
}));
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const classes = classNames(
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import { AsyncRenderer, Pagination } from '@vegaprotocol/ui-toolkit';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import type { EpochFieldsFragment } from '../home/__generated__/Rewards';
|
||||
import { useRewardsQuery } from '../home/__generated__/Rewards';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { EpochIndividualRewardsTable } from './epoch-individual-rewards-table';
|
||||
import { generateEpochIndividualRewardsList } from './generate-epoch-individual-rewards-list';
|
||||
import { calculateEpochOffset } from '../../../lib/epoch-pagination';
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
Toggle,
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
useNetworkParams,
|
||||
NetworkParams,
|
||||
|
||||
+2
-2
@@ -33,8 +33,8 @@ let mockVegaWalletHookValue: {
|
||||
pubKey: null,
|
||||
};
|
||||
|
||||
jest.mock('@vegaprotocol/wallet', () => ({
|
||||
...jest.requireActual('@vegaprotocol/wallet'),
|
||||
jest.mock('@vegaprotocol/wallet-react', () => ({
|
||||
...jest.requireActual('@vegaprotocol/wallet-react'),
|
||||
useVegaWallet: jest.fn(() => mockVegaWalletHookValue),
|
||||
}));
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ import { useWeb3React } from '@web3-react/core';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { EthConnectPrompt } from '../../../../../components/eth-connect-prompt';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { ConnectToVega } from '../../../../../components/connect-to-vega';
|
||||
|
||||
export const StakingWalletsContainer = ({
|
||||
|
||||
@@ -33,8 +33,8 @@ let mockVegaWalletHookValue: {
|
||||
pubKey: null,
|
||||
};
|
||||
|
||||
jest.mock('@vegaprotocol/wallet', () => ({
|
||||
...jest.requireActual('@vegaprotocol/wallet'),
|
||||
jest.mock('@vegaprotocol/wallet-react', () => ({
|
||||
...jest.requireActual('@vegaprotocol/wallet-react'),
|
||||
useVegaWallet: jest.fn(() => mockVegaWalletHookValue),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import { EthConnectPrompt } from '../../../components/eth-connect-prompt';
|
||||
import { DisassociatePage } from './components/disassociate-page';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useStakingQuery } from '../__generated__/Staking';
|
||||
import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import { ValidatorTables } from './validator-tables';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { getMultisigStatusInfo } from '../../../lib/get-multisig-status-info';
|
||||
import { MultisigIncorrectNotice } from '../../../components/multisig-incorrect-notice';
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
addDecimal,
|
||||
removePaginationWrapper,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import React from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Button, Callout, Intent, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import type { BigNumber } from '../../../lib/bignumber';
|
||||
import type { UndelegateSubmissionBody } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { removeDecimal } from '@vegaprotocol/utils';
|
||||
|
||||
interface PendingStakeProps {
|
||||
pendingAmount: BigNumber;
|
||||
nodeId: string;
|
||||
pubKey: string;
|
||||
}
|
||||
|
||||
enum FormState {
|
||||
Default,
|
||||
Pending,
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
export const PendingStake = ({
|
||||
pendingAmount,
|
||||
nodeId,
|
||||
pubKey,
|
||||
}: PendingStakeProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { sendTx } = useVegaWallet();
|
||||
const { appState } = useAppState();
|
||||
const [formState, setFormState] = React.useState(FormState.Default);
|
||||
|
||||
const removeStakeNow = async () => {
|
||||
setFormState(FormState.Pending);
|
||||
try {
|
||||
const command: UndelegateSubmissionBody = {
|
||||
undelegateSubmission: {
|
||||
nodeId,
|
||||
amount: removeDecimal(pendingAmount.toString(), appState.decimals),
|
||||
method: 'METHOD_NOW',
|
||||
},
|
||||
};
|
||||
await sendTx(pubKey, command);
|
||||
} catch (err) {
|
||||
setFormState(FormState.Failure);
|
||||
Sentry.captureException(err);
|
||||
}
|
||||
};
|
||||
|
||||
if (formState === FormState.Failure) {
|
||||
return (
|
||||
<Callout
|
||||
intent={Intent.Danger}
|
||||
title={t('failedToRemovePendingStake', { pendingAmount })}
|
||||
>
|
||||
<p>{t('pleaseTryAgain')}</p>
|
||||
</Callout>
|
||||
);
|
||||
} else if (formState === FormState.Pending) {
|
||||
return (
|
||||
<Callout
|
||||
icon={<Loader size="small" />}
|
||||
title={t('removingPendingStake', { pendingAmount })}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="py-4">
|
||||
<h2>{t('pendingNomination')}</h2>
|
||||
<p>{t('pendingNominationNextEpoch', { pendingAmount })}</p>
|
||||
<Button onClick={() => removeStakeNow()}>
|
||||
{t('cancelPendingEpochNomination')}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -27,11 +27,11 @@ import {
|
||||
NetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useBalances } from '../../../lib/balances/balances-store';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { SubHeading } from '../../../components/heading';
|
||||
import type {
|
||||
DelegateSubmissionBody,
|
||||
UndelegateSubmissionBody,
|
||||
import {
|
||||
type DelegateSubmissionBody,
|
||||
type UndelegateSubmissionBody,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import Routes from '../../routes';
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
useWithdrawalDialog,
|
||||
WithdrawalsTable,
|
||||
} from '@vegaprotocol/withdraws';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet-react';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useDocumentTitle } from '../../hooks/use-document-title';
|
||||
import type { RouteChildProps } from '../index';
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
VegaIconNames,
|
||||
useToasts,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWalletDialogStore } from '@vegaprotocol/wallet';
|
||||
import { useDialogStore } from '@vegaprotocol/wallet-react';
|
||||
import {
|
||||
useEthereumTransactionToasts,
|
||||
useEthereumWithdrawApprovalsToasts,
|
||||
@@ -19,9 +19,7 @@ import { useTranslation } from 'react-i18next';
|
||||
const WalletDisconnectAdditionalContent = () => {
|
||||
const { t } = useTranslation();
|
||||
const { hideToast } = useWalletDisconnectToastActions();
|
||||
const openVegaWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
const openVegaWalletDialog = useDialogStore((store) => store.open);
|
||||
return (
|
||||
<p className="mt-2">
|
||||
<TradingButton
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_VEGA_CONFIG_URL=''
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SENTRY_DSN=https://dummy@o999999.ingest.sentry.io/9999999
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# Expose some env vars to cypress environment for market setup
|
||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
|
||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
||||
CYPRESS_ORACLE_PUBKEY=6d9d35f657589e40ddfb448b7ad4a7463b66efb307527fedd2aa7df1bbd5ea61
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
||||
CYPRESS_VEGA_ENV=CUSTOM
|
||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
||||
CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||
@@ -1,33 +0,0 @@
|
||||
NX_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
|
||||
NX_VEGA_CONFIG_URL=''
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://trading.stagnet1.vega.rocks\"}
|
||||
NX_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
NX_ORACLE_PROOFS_URL=https://raw.githubusercontent.com/vegaprotocol/well-known/main/__generated__/oracle-proofs.json
|
||||
|
||||
|
||||
# Expose some env vars to cypress environment for market setup
|
||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
CYPRESS_ETHEREUM_WALLET_ADDRESS=0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F
|
||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
CYPRESS_CONSOLE_URL=https://console.fairground.wtf
|
||||
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY=02ecea…342f65
|
||||
CYPRESS_TRUNCATED_VEGA_PUBLIC_KEY2=7f9cf0…c25535
|
||||
CYPRESS_VEGA_ENV=CUSTOM
|
||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
||||
CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"extends": ["plugin:cypress/recommended", "../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"cypress/unsafe-to-chain-command": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["src/plugins/index.js"],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-var-requires": "off",
|
||||
"no-undef": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
const { defineConfig } = require('cypress');
|
||||
|
||||
module.exports = defineConfig({
|
||||
reporter: '../../node_modules/cypress-mochawesome-reporter',
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
require('cypress-mochawesome-reporter/plugin')(on);
|
||||
require('@cypress/grep/src/plugin')(config);
|
||||
return config;
|
||||
},
|
||||
baseUrl: 'http://localhost:4200',
|
||||
fileServerFolder: '.',
|
||||
fixturesFolder: false,
|
||||
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
|
||||
supportFile: './src/support/index.js',
|
||||
video: false,
|
||||
videosFolder: '../../dist/cypress/apps/trading-e2e/videos',
|
||||
videoUploadOnPasses: false,
|
||||
screenshotsFolder: '../../dist/cypress/apps/trading-e2e/screenshots',
|
||||
chromeWebSecurity: false,
|
||||
projectId: 'et4snf',
|
||||
defaultCommandTimeout: 10000,
|
||||
viewportWidth: 1800,
|
||||
viewportHeight: 900,
|
||||
responseTimeout: 50000,
|
||||
requestTimeout: 20000,
|
||||
retries: 1,
|
||||
testIsolation: false,
|
||||
experimentalMemoryManagement: true,
|
||||
},
|
||||
env: {
|
||||
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
|
||||
ETHEREUM_CHAIN_ID: 11155111,
|
||||
TRADING_MODE_LINK:
|
||||
'https://docs.vega.xyz/testnet/concepts/trading-on-vega/trading-modes#auction-type-liquidity-monitoring',
|
||||
grepTags: '@regression @smoke @slow',
|
||||
grepFilterSpecs: true,
|
||||
grepOmitFiltered: true,
|
||||
txTimeout: { timeout: 70000 },
|
||||
},
|
||||
});
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
declare module '*.scss';
|
||||
@@ -1,39 +0,0 @@
|
||||
{
|
||||
"name": "trading-e2e",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/trading-e2e/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"e2e": {
|
||||
"executor": "@nx/cypress:cypress",
|
||||
"options": {
|
||||
"cypressConfig": "apps/trading-e2e/cypress.config.js",
|
||||
"devServerTarget": "trading:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "trading:serve:production"
|
||||
},
|
||||
"live": {
|
||||
"devServerTarget": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/trading-e2e/**/*.{js,ts}"]
|
||||
}
|
||||
},
|
||||
"build": {
|
||||
"executor": "nx:run-commands",
|
||||
"outputs": [],
|
||||
"options": {
|
||||
"command": "yarn tsc --project ./apps/trading-e2e/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [],
|
||||
"implicitDependencies": ["trading"]
|
||||
}
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
declare namespace Cypress {
|
||||
// specify additional properties in the TestConfig object
|
||||
// in our case we will add "tags" property
|
||||
interface SuiteConfigOverrides {
|
||||
/**
|
||||
* List of tags for this test
|
||||
* @example a single tag
|
||||
* it('logs in', { tags: '@smoke' }, () => { ... })
|
||||
* @example multiple tags
|
||||
* it('works', { tags: ['@smoke', '@slow'] }, () => { ... })
|
||||
*/
|
||||
tags?: string | string[];
|
||||
}
|
||||
|
||||
interface Cypress {
|
||||
grep?: (grep?: string, tags?: string, burn?: string) => void;
|
||||
}
|
||||
}
|
||||
@@ -1,337 +0,0 @@
|
||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const amountField = 'input[name="amount"]';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const sepoliaUrl = Cypress.env('ETHERSCAN_URL');
|
||||
const btcName = 0;
|
||||
const vegaName = 4;
|
||||
const btcSymbol = 'tBTC';
|
||||
const vegaSymbol = 'VEGA';
|
||||
const toastContent = 'toast-content';
|
||||
const depositsTab = 'Deposits';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
const completeWithdrawalBtn = 'complete-withdrawal';
|
||||
const depositSubmit = 'deposit-submit';
|
||||
const approveSubmit = 'approve-submit';
|
||||
const dialogContent = 'dialog-content';
|
||||
|
||||
// Because the tests are run on a live network to optimize time, the tests are interdependent and must be run in the given order.
|
||||
describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
||||
before(() => {
|
||||
cy.createMarket();
|
||||
cy.get('@markets').then((markets) => {
|
||||
cy.wrap(markets[0]).as('market');
|
||||
});
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/portfolio');
|
||||
});
|
||||
|
||||
it('can deposit', function () {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
|
||||
// 1001-DEPO-001
|
||||
// 1001-DEPO-002
|
||||
// 1001-DEPO-003
|
||||
// 1001-DEPO-005
|
||||
// 1001-DEPO-006
|
||||
// 1001-DEPO-007
|
||||
// 1001-DEPO-008
|
||||
// 1001-DEPO-009
|
||||
// 1001-DEPO-010
|
||||
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||
|
||||
cy.getByTestId('approve-default').should(
|
||||
'contain.text',
|
||||
`Before you can make a deposit of your chosen asset, ${btcSymbol}, you need to approve its use in your Ethereum wallet`
|
||||
);
|
||||
cy.getByTestId(approveSubmit).click();
|
||||
cy.getByTestId('approve-pending').should('exist');
|
||||
cy.getByTestId('approve-confirmed').should('exist');
|
||||
cy.get(amountField).focus();
|
||||
cy.get(amountField).clear().type('10');
|
||||
cy.getByTestId(depositSubmit).click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
`Transaction confirmedYour transaction has been confirmed.View on EtherscanDeposit 10.00 ${btcSymbol}`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
cy.highlight('deposit verification');
|
||||
|
||||
cy.get('[col-id="asset.symbol"]', txTimeout).should(
|
||||
'contain.text',
|
||||
btcSymbol
|
||||
);
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', btcSymbol);
|
||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
||||
|
||||
cy.get('[col-id="txHash"]')
|
||||
.should('have.length.above', 2)
|
||||
.eq(1)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '10.00');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
||||
cy.get('[col-id="txHash"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
});
|
||||
|
||||
it('can not withdrawal because of no MultiSign', function () {
|
||||
// 1002-WITH-022
|
||||
// 1002-WITH-023
|
||||
// 0003-WTXN-011
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
selectAsset(btcName);
|
||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||
cy.get(amountField).focus();
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Funds unlocked'
|
||||
);
|
||||
// cy.getByTestId(toastCloseBtn).click();
|
||||
cy.highlight('withdrawals verification');
|
||||
cy.getByTestId('toast-complete-withdrawal').last().click();
|
||||
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Error occurredcannot estimate gas'
|
||||
);
|
||||
cy.getByTestId(completeWithdrawalBtn).should(
|
||||
'contain.text',
|
||||
'Complete withdrawal'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.updateCapsuleMultiSig();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.createMarket();
|
||||
cy.get('@markets').then((markets) => {
|
||||
cy.wrap(markets[0]).as('market');
|
||||
});
|
||||
cy.setOnBoardingViewed();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
it('can withdrawal', function () {
|
||||
// 1002-WITH-0014
|
||||
// 1002-WITH-006
|
||||
// 1002-WITH-009
|
||||
// 1002-WITH-011
|
||||
// 1002-WITH-024
|
||||
// 1002-WITH-012
|
||||
// 1002-WITH-013
|
||||
// 1002-WITH-014
|
||||
// 1002-WITH-015
|
||||
// 1002-WITH-016
|
||||
// 1002-WITH-017
|
||||
// 1002-WITH-019
|
||||
// 1002-WITH-020
|
||||
// 1002-WITH-021
|
||||
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||
cy.get(amountField).clear().type('1');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Funds unlocked'
|
||||
);
|
||||
|
||||
cy.highlight('withdrawals verification');
|
||||
cy.getByTestId('toast-complete-withdrawal').click();
|
||||
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Transaction confirmed'
|
||||
);
|
||||
cy.getByTestId(toastContent, txTimeout)
|
||||
.should('contain.text', 'Funds unlocked')
|
||||
.and('contain.text', 'Your funds have been unlocked for withdrawal.')
|
||||
.and(
|
||||
'contain.text',
|
||||
'View in block explorerYou can save your withdrawal details for extra security.'
|
||||
)
|
||||
.and('contain.text', 'Withdraw 1.00 tBTCComplete withdrawal');
|
||||
cy.getByTestId('toast-withdrawal-details').click();
|
||||
cy.getByTestId(dialogContent)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'contain.text',
|
||||
'Save withdrawal details'
|
||||
);
|
||||
cy.getByTestId('copy-button').should('be.visible');
|
||||
cy.getByTestId('assetSource_value').should(
|
||||
'have.text',
|
||||
'0xb63D135B0a6854EEb765d69ca36210cC70BECAE0'
|
||||
);
|
||||
cy.getByTestId('amount_value').should('have.text', '100000');
|
||||
cy.getByTestId('nonce_value').invoke('text').should('not.be.empty');
|
||||
cy.getByTestId('signatures_value')
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
cy.getByTestId('targetAddress_value').should(
|
||||
'have.text',
|
||||
ethWalletAddress
|
||||
);
|
||||
cy.getByTestId('creation_value').invoke('text').should('not.be.empty');
|
||||
});
|
||||
cy.getByTestId('close-withdrawal-approval-dialog').click();
|
||||
|
||||
cy.get('.ag-center-cols-container')
|
||||
.find('[col-id="status"]')
|
||||
.eq(0, txTimeout)
|
||||
.should('contain.text', 'Completed');
|
||||
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
.eq(1)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', btcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '1.00');
|
||||
cy.get('[col-id="details.receiverAddress"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/address/`);
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="withdrawnTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Completed');
|
||||
cy.get('[col-id="txHash"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
|
||||
cy.getByTestId('withdraw-dialog-button').click({ force: true });
|
||||
// cy.getByTestId('BALANCE_AVAILABLE_value').should('have.text', '6.999');
|
||||
});
|
||||
|
||||
it('approved amount is less than deposit', function () {
|
||||
// 1001-DEPO-006
|
||||
// 1001-DEPO-007
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(btcName);
|
||||
cy.get('[data-testid="rich-select-option"]').eq(btcName).click();
|
||||
cy.contains('Deposits of tBTC not approved').should('not.exist');
|
||||
cy.contains('Use maximum').should('be.visible');
|
||||
cy.get(amountField).clear().type('20000000');
|
||||
cy.getByTestId(depositSubmit).should('be.visible');
|
||||
cy.getByTestId(depositSubmit).click();
|
||||
cy.getByTestId('input-error-text').should(
|
||||
'contain.text',
|
||||
`You can't deposit more than you have in your Ethereum wallet`
|
||||
);
|
||||
});
|
||||
|
||||
it('withdraw - delay verification', function () {
|
||||
// 1001-DEPO-024
|
||||
// 1002-WITH-007
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist');
|
||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('Unknown');
|
||||
selectAsset(vegaName);
|
||||
cy.getByTestId('approve-submit').click();
|
||||
cy.getByTestId('approve-confirmed').should(
|
||||
'contain.text',
|
||||
'You approved deposits of up to VEGA'
|
||||
);
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('deposit-submit').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
`Your transaction has been confirmed.`,
|
||||
{ matchCase: false }
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click({ multiple: true });
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
cy.highlight('deposit verification');
|
||||
|
||||
cy.get('[col-id="asset.symbol"]', txTimeout).should(
|
||||
'contain.text',
|
||||
vegaSymbol
|
||||
);
|
||||
cy.getByTestId(depositsTab).click();
|
||||
cy.get('.ag-cell-value', txTimeout).should('contain.text', vegaSymbol);
|
||||
cy.get('[col-id="status"]').should('not.have.text', 'Open', txTimeout);
|
||||
|
||||
cy.get('[col-id="txHash"]')
|
||||
.should('have.length.above', 2)
|
||||
.eq(1)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', vegaSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '10,000.00');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Finalized');
|
||||
cy.get('[col-id="txHash"]')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', `${sepoliaUrl}/tx/0x`);
|
||||
});
|
||||
|
||||
cy.getByTestId('Withdrawals').click(txTimeout);
|
||||
cy.getByTestId('withdraw-dialog-button').click();
|
||||
selectAsset(1);
|
||||
cy.get(amountField).clear().type('10000');
|
||||
cy.getByTestId('DELAY_TIME_value').should('have.text', '5 days');
|
||||
cy.getByTestId('submit-withdrawal').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should(
|
||||
'contain.text',
|
||||
'Your funds have been unlocked'
|
||||
);
|
||||
cy.getByTestId(toastCloseBtn).click();
|
||||
cy.getByTestId(completeWithdrawalBtn).first().should('be.visible').click();
|
||||
cy.getByTestId(toastContent, txTimeout).should('contain.text', 'Delayed');
|
||||
cy.getByTestId('tab-withdrawals').within(() => {
|
||||
cy.get('.ag-center-cols-container')
|
||||
.children()
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('[col-id="status"]').contains(
|
||||
/Delayed \(ready in (\d{1,2}:\d{2}:\d{2}:\d{2})\)/
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,43 +0,0 @@
|
||||
import { connectEthereumWallet } from '../support/ethereum-wallet';
|
||||
|
||||
const connectEthWalletBtn = 'connect-eth-wallet-btn';
|
||||
|
||||
describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
// Using portfolio withdrawals tab is it requires Ethereum wallet connection
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
cy.getByTestId('Withdrawals').click();
|
||||
});
|
||||
|
||||
it('can connect', () => {
|
||||
// 0004-EWAL-001
|
||||
|
||||
cy.getByTestId('Deposits').click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
||||
cy.getByTestId('web3-connector-list').should('exist');
|
||||
cy.getByTestId('web3-connector-MetaMask').click();
|
||||
cy.getByTestId('web3-connector-list').should('not.exist');
|
||||
cy.getByTestId('tab-deposits').should('not.be.empty');
|
||||
});
|
||||
|
||||
it('able to disconnect eth wallet', () => {
|
||||
// 0004-EWAL-004
|
||||
// 0004-EWAL-005
|
||||
// 0004-EWAL-006
|
||||
|
||||
cy.getByTestId('Deposits').click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
connectEthereumWallet('MetaMask');
|
||||
cy.getByTestId('ethereum-address').should('have.text', '0xEe7D…d94F');
|
||||
cy.getByTestId('disconnect-ethereum-wallet')
|
||||
.should('have.text', 'Disconnect')
|
||||
.click();
|
||||
cy.getByTestId(connectEthWalletBtn).should('exist');
|
||||
});
|
||||
});
|
||||
@@ -1,29 +0,0 @@
|
||||
import { OrderType } from '@vegaprotocol/types';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
|
||||
const orderSizeField = 'order-size';
|
||||
const orderPriceField = 'order-price';
|
||||
const orderTIFDropDown = 'order-tif';
|
||||
const placeOrderBtn = 'place-order';
|
||||
|
||||
export const createOrder = (order: OrderSubmission): void => {
|
||||
cy.log('Placing order', order);
|
||||
const { type, side, size, price, timeInForce, expiresAt } = order;
|
||||
|
||||
cy.getByTestId(
|
||||
`order-type-${type === OrderType.TYPE_LIMIT ? 'Limit' : 'Market'}`
|
||||
).click();
|
||||
cy.getByTestId(`order-side-${side}`).click();
|
||||
cy.getByTestId(orderSizeField).clear().type(size);
|
||||
if (price) {
|
||||
cy.getByTestId(orderPriceField).clear().type(price);
|
||||
}
|
||||
cy.getByTestId(orderTIFDropDown).select(timeInForce);
|
||||
if (timeInForce === 'TIME_IN_FORCE_GTT') {
|
||||
if (!expiresAt) {
|
||||
throw new Error('Specify expiresAt if using GTT');
|
||||
}
|
||||
cy.getByTestId('date-picker-field').type(expiresAt);
|
||||
}
|
||||
cy.getByTestId(placeOrderBtn).click();
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
export const orderSizeField = 'order-size';
|
||||
export const orderPriceField = 'order-price';
|
||||
export const orderTIFDropDown = 'order-tif';
|
||||
export const placeOrderBtn = 'place-order';
|
||||
export const toggleShort = 'order-side-SIDE_SELL';
|
||||
export const toggleLong = 'order-side-SIDE_BUY';
|
||||
export const toggleLimit = 'order-type-Limit';
|
||||
export const toggleMarket = 'order-type-Market';
|
||||
|
||||
export const TIFlist = Object.values(Schema.OrderTimeInForce).map((value) => {
|
||||
return {
|
||||
code: Schema.OrderTimeInForceCode[value],
|
||||
value,
|
||||
text: Schema.OrderTimeInForceMapping[value],
|
||||
};
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
export const connectEthereumWallet = (connectorName: string) => {
|
||||
cy.getByTestId('connect-eth-wallet-btn').should('be.enabled').click();
|
||||
cy.getByTestId('web3-connector-list').should('be.visible');
|
||||
cy.getByTestId(`web3-connector-${connectorName}`).click();
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
export const selectAsset = (assetIndex: number) => {
|
||||
cy.log(`selecting asset: ${assetIndex}`);
|
||||
cy.getByTestId('select-asset').click();
|
||||
cy.get('[data-testid="rich-select-option"]').eq(assetIndex).click();
|
||||
|
||||
// The asset only gets set once the queries (getWithdrawThreshold, getDelay)
|
||||
// against the Ethereum change resolve, we should fix this but for now just force
|
||||
// some wait time
|
||||
// eslint-disable-next-line
|
||||
cy.wait(100);
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
import '@vegaprotocol/cypress';
|
||||
import 'cypress-real-events/support';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
import { addMockTradingPage } from './trading';
|
||||
import 'cypress-mochawesome-reporter/register';
|
||||
|
||||
registerCypressGrep();
|
||||
addMockTradingPage();
|
||||
@@ -1,36 +0,0 @@
|
||||
import type {
|
||||
OrdersUpdateSubscription,
|
||||
OrdersUpdateSubscriptionVariables,
|
||||
OrderUpdateFieldsFragment,
|
||||
} from '@vegaprotocol/orders';
|
||||
import type { onMessage } from '@vegaprotocol/cypress';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import { orderUpdateSubscription } from '@vegaprotocol/mock';
|
||||
|
||||
const sendOrderUpdate: ((data: OrdersUpdateSubscription) => void)[] = [];
|
||||
const getOnOrderUpdate = () => {
|
||||
const onOrderUpdate: onMessage<
|
||||
OrdersUpdateSubscription,
|
||||
OrdersUpdateSubscriptionVariables
|
||||
> = (send) => {
|
||||
sendOrderUpdate.push(send);
|
||||
};
|
||||
return onOrderUpdate;
|
||||
};
|
||||
|
||||
export const getSubscriptionMocks = () => ({
|
||||
OrdersUpdate: getOnOrderUpdate(),
|
||||
});
|
||||
|
||||
export function updateOrder(
|
||||
override?: PartialDeep<OrderUpdateFieldsFragment>
|
||||
): void {
|
||||
const update: OrdersUpdateSubscription = orderUpdateSubscription({
|
||||
// @ts-ignore partial deep check failing
|
||||
orders: [override],
|
||||
});
|
||||
if (!sendOrderUpdate) {
|
||||
throw new Error('OrderSub not called');
|
||||
}
|
||||
sendOrderUpdate.forEach((send) => send(update));
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
import type {
|
||||
OrderAmendment,
|
||||
OrderAmendmentBody,
|
||||
OrderCancellation,
|
||||
OrderCancellationBody,
|
||||
OrderSubmission,
|
||||
OrderSubmissionBody,
|
||||
Transaction,
|
||||
} from '@vegaprotocol/wallet';
|
||||
|
||||
export const testOrderSubmission = (
|
||||
order: OrderSubmission,
|
||||
expected?: Partial<OrderSubmission>
|
||||
) => {
|
||||
const expectedOrder = {
|
||||
...order,
|
||||
...expected,
|
||||
};
|
||||
|
||||
const transaction: OrderSubmissionBody = {
|
||||
orderSubmission: expectedOrder,
|
||||
};
|
||||
vegaWalletTransaction(transaction);
|
||||
verifyToast();
|
||||
};
|
||||
|
||||
export const testOrderAmendment = (
|
||||
order: OrderAmendment,
|
||||
expected?: Partial<OrderAmendment>
|
||||
) => {
|
||||
const expectedOrder = {
|
||||
...order,
|
||||
...expected,
|
||||
};
|
||||
|
||||
const transaction: OrderAmendmentBody = {
|
||||
orderAmendment: expectedOrder,
|
||||
};
|
||||
vegaWalletTransaction(transaction);
|
||||
verifyToast();
|
||||
};
|
||||
|
||||
export const testOrderCancellation = (
|
||||
order: OrderCancellation,
|
||||
expected?: Partial<OrderCancellation>
|
||||
) => {
|
||||
const expectedOrder = {
|
||||
...order,
|
||||
...expected,
|
||||
};
|
||||
|
||||
const transaction: OrderCancellationBody = {
|
||||
orderCancellation: expectedOrder,
|
||||
};
|
||||
vegaWalletTransaction(transaction);
|
||||
verifyToast();
|
||||
};
|
||||
|
||||
const vegaWalletTransaction = (transaction: Transaction) => {
|
||||
cy.wait('@VegaWalletTransaction')
|
||||
.its('request')
|
||||
.then((req) => {
|
||||
expect(req.body.params).to.deep.equal({
|
||||
publicKey: Cypress.env('VEGA_PUBLIC_KEY'),
|
||||
sendingMode: 'TYPE_SYNC',
|
||||
transaction,
|
||||
});
|
||||
expect(req.headers.authorization).to.equal(
|
||||
`VWT ${Cypress.env('VEGA_WALLET_API_TOKEN')}`
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const verifyToast = () => {
|
||||
cy.getByTestId('toast').should('contain.text', 'Awaiting confirmation');
|
||||
cy.getByTestId('toast')
|
||||
.find('a')
|
||||
.invoke('attr', 'href')
|
||||
.should('include', `${Cypress.env('EXPLORER_URL')}/txs/test-tx-hash`);
|
||||
cy.getByTestId('toast-close').click();
|
||||
};
|
||||
@@ -1,252 +0,0 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { CyHttpMessages } from 'cypress/types/net-stubbing';
|
||||
import type { Provider, Status } from '@vegaprotocol/markets';
|
||||
import {
|
||||
accountsQuery,
|
||||
assetQuery,
|
||||
assetsQuery,
|
||||
candlesQuery,
|
||||
chartQuery,
|
||||
depositsQuery,
|
||||
estimateFeesQuery,
|
||||
marginsQuery,
|
||||
marketCandlesQuery,
|
||||
marketDataQuery,
|
||||
marketDepthQuery,
|
||||
marketInfoQuery,
|
||||
marketsCandlesQuery,
|
||||
marketsDataQuery,
|
||||
marketsQuery,
|
||||
networkParamsQuery,
|
||||
nodeGuardQuery,
|
||||
ordersQuery,
|
||||
estimatePositionQuery,
|
||||
positionsQuery,
|
||||
proposalListQuery,
|
||||
tradesQuery,
|
||||
withdrawalsQuery,
|
||||
protocolUpgradeProposalsQuery,
|
||||
blockStatisticsQuery,
|
||||
networkParamQuery,
|
||||
liquidityProvisionsQuery,
|
||||
successorMarketQuery,
|
||||
parentMarketIdQuery,
|
||||
successorMarketIdsQuery,
|
||||
successorMarketProposalDetailsQuery,
|
||||
liquidityProvidersQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { MarketDataQuery, MarketsQuery } from '@vegaprotocol/markets';
|
||||
|
||||
type MarketPageMockData = {
|
||||
state: Schema.MarketState;
|
||||
tradingMode?: Schema.MarketTradingMode;
|
||||
trigger?: Schema.AuctionTrigger;
|
||||
};
|
||||
|
||||
const ORACLE_PUBKEY = Cypress.env('ORACLE_PUBKEY');
|
||||
|
||||
const marketDataOverride = (
|
||||
data: MarketPageMockData
|
||||
): PartialDeep<MarketDataQuery> => ({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
data: {
|
||||
// @ts-ignore conflict between incoming and outgoing types
|
||||
trigger: data.trigger,
|
||||
// @ts-ignore same as above
|
||||
marketTradingMode: data.tradingMode,
|
||||
marketState: data.state,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const marketsDataOverride = (
|
||||
data: MarketPageMockData
|
||||
): PartialDeep<MarketsQuery> => ({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
// @ts-ignore conflict between incoming and outgoing types
|
||||
tradingMode: data.tradingMode,
|
||||
state: data.state,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const mockTradingPage = (
|
||||
req: CyHttpMessages.IncomingHttpRequest,
|
||||
state: Schema.MarketState = Schema.MarketState.STATE_ACTIVE,
|
||||
tradingMode?: Schema.MarketTradingMode,
|
||||
trigger?: Schema.AuctionTrigger
|
||||
) => {
|
||||
aliasGQLQuery(req, 'NodeGuard', nodeGuardQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'Markets',
|
||||
marketsQuery(marketsDataOverride({ state, tradingMode, trigger }))
|
||||
);
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'MarketData',
|
||||
marketDataQuery(marketDataOverride({ state, tradingMode, trigger }))
|
||||
);
|
||||
aliasGQLQuery(req, 'MarketsData', marketsDataQuery());
|
||||
aliasGQLQuery(req, 'MarketsCandles', marketsCandlesQuery());
|
||||
aliasGQLQuery(req, 'MarketCandles', marketCandlesQuery());
|
||||
aliasGQLQuery(req, 'MarketDepth', marketDepthQuery());
|
||||
aliasGQLQuery(req, 'Orders', ordersQuery());
|
||||
aliasGQLQuery(req, 'Accounts', accountsQuery());
|
||||
aliasGQLQuery(req, 'Positions', positionsQuery());
|
||||
aliasGQLQuery(req, 'Margins', marginsQuery());
|
||||
aliasGQLQuery(req, 'Assets', assetsQuery());
|
||||
aliasGQLQuery(req, 'Asset', assetQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'MarketInfo',
|
||||
marketInfoQuery({
|
||||
market: {
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
__typename: 'Future',
|
||||
dataSourceSpecForSettlementData: {
|
||||
data: {
|
||||
sourceType: {
|
||||
sourceType: {
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
dataSourceSpecForTradingTermination: {
|
||||
data: {
|
||||
sourceType: {
|
||||
sourceType: {
|
||||
signers: [
|
||||
{
|
||||
__typename: 'Signer',
|
||||
signer: {
|
||||
__typename: 'PubKey',
|
||||
key: ORACLE_PUBKEY,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
aliasGQLQuery(req, 'Trades', tradesQuery());
|
||||
aliasGQLQuery(req, 'Chart', chartQuery());
|
||||
aliasGQLQuery(req, 'LiquidityProvisions', liquidityProvisionsQuery());
|
||||
aliasGQLQuery(req, 'LiquidityProviders', liquidityProvidersQuery());
|
||||
aliasGQLQuery(req, 'Candles', candlesQuery());
|
||||
aliasGQLQuery(req, 'Withdrawals', withdrawalsQuery());
|
||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||
aliasGQLQuery(req, 'NetworkParam', networkParamQuery);
|
||||
aliasGQLQuery(req, 'EstimateFees', estimateFeesQuery());
|
||||
aliasGQLQuery(req, 'EstimatePosition', estimatePositionQuery());
|
||||
aliasGQLQuery(req, 'ProposalsList', proposalListQuery());
|
||||
aliasGQLQuery(req, 'Deposits', depositsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'ProtocolUpgradeProposals',
|
||||
protocolUpgradeProposalsQuery()
|
||||
);
|
||||
aliasGQLQuery(req, 'BlockStatistics', blockStatisticsQuery());
|
||||
aliasGQLQuery(req, 'SuccessorMarket', successorMarketQuery());
|
||||
aliasGQLQuery(req, 'ParentMarketId', parentMarketIdQuery());
|
||||
aliasGQLQuery(req, 'SuccessorMarketIds', successorMarketIdsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'SuccessorMarketProposalDetails',
|
||||
successorMarketProposalDetailsQuery()
|
||||
);
|
||||
};
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
mockTradingPage(
|
||||
state?: Schema.MarketState,
|
||||
tradingMode?: Schema.MarketTradingMode,
|
||||
trigger?: Schema.AuctionTrigger,
|
||||
oracleStatus?: Status
|
||||
): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const addMockTradingPage = () => {
|
||||
Cypress.Commands.add(
|
||||
'mockTradingPage',
|
||||
(
|
||||
state = Schema.MarketState.STATE_ACTIVE,
|
||||
tradingMode,
|
||||
trigger,
|
||||
oracleStatus
|
||||
) => {
|
||||
cy.mockChainId();
|
||||
cy.mockGQL((req) => {
|
||||
mockTradingPage(req, state, tradingMode, trigger);
|
||||
});
|
||||
|
||||
const oracle: Provider = {
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: oracleStatus || 'GOOD',
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message',
|
||||
available: true,
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-${ORACLE_PUBKEY}.toml`,
|
||||
};
|
||||
// Prevent request to github, return some dummy content
|
||||
cy.intercept(
|
||||
'GET',
|
||||
/^https:\/\/raw.githubusercontent.com\/vegaprotocol\/well-known/,
|
||||
{
|
||||
body: [oracle],
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"sourceMap": false,
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"allowJs": true,
|
||||
"types": ["cypress", "node", "cypress-real-events", "@cypress/grep"],
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": false,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.js", "./declaration.d.ts"]
|
||||
}
|
||||
@@ -49,10 +49,4 @@ To run the minimal set of unit tests, run the following:
|
||||
yarn nx test trading
|
||||
```
|
||||
|
||||
To run the UI automation tests with a mocked API, run:
|
||||
|
||||
```bash
|
||||
yarn nx run trading-e2e:e2e
|
||||
```
|
||||
|
||||
To run tests with market sim please read [the readme](e2e/README.md).
|
||||
To run the UI automation tests please read [e2e/README.md](e2e/README.md)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user