Compare commits
40
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ed2c82487d | ||
|
|
55143331f1 | ||
|
|
cafb3b1c57 | ||
|
|
019b2d7d89 | ||
|
|
de0dc4af8e | ||
|
|
6765f4d03a | ||
|
|
7bea90e4d1 | ||
|
|
ac4762f9d7 | ||
|
|
1195309b61 | ||
|
|
8ce77e23ba | ||
|
|
5cae2ce400 | ||
|
|
b8c9f9772e | ||
|
|
c3e39b6e15 | ||
|
|
77a391448b | ||
|
|
484d7888cf | ||
|
|
187f1929fe | ||
|
|
0a4333645b | ||
|
|
310506e5af | ||
|
|
0b33ed4299 | ||
|
|
13f2e51798 | ||
|
|
fc047feeee | ||
|
|
005455c870 | ||
|
|
2c2bc391e8 | ||
|
|
445a085190 | ||
|
|
bb1b236cdf | ||
|
|
da66b7b20d | ||
|
|
ec12811f72 | ||
|
|
180de8cf25 | ||
|
|
df88e77cdf | ||
|
|
9441aee8cf | ||
|
|
2353812834 | ||
|
|
bba2b3c177 | ||
|
|
ae57bd92f4 | ||
|
|
115b642140 | ||
|
|
ce3da97a8a | ||
|
|
cabd99d3ef | ||
|
|
597e07608f | ||
|
|
e451dc54b3 | ||
|
|
9703c3b7a6 | ||
|
|
c22b6f3ce9 |
@@ -6,7 +6,9 @@ on:
|
||||
- release/*
|
||||
- develop
|
||||
- main
|
||||
pull_request:
|
||||
# uncomment pull_request and comment pull_request_target to test CI changes against feature branch not target branch (develop)
|
||||
# pull_request:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
- ready_for_review
|
||||
@@ -20,6 +22,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Cache node modules
|
||||
id: cache
|
||||
@@ -45,7 +49,7 @@ jobs:
|
||||
|
||||
lint-pr-title:
|
||||
needs: node-modules
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
|
||||
name: Verify PR title
|
||||
uses: ./.github/workflows/lint-pr.yml
|
||||
secrets: inherit
|
||||
@@ -60,6 +64,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
@@ -107,65 +112,82 @@ jobs:
|
||||
echo "Branch slug: ${branch_slug}"
|
||||
echo ">>>> eof debug"
|
||||
|
||||
projects_e2e=""
|
||||
projects_array=()
|
||||
|
||||
preview_governance="not deployed"
|
||||
preview_trading="not deployed"
|
||||
preview_explorer="not deployed"
|
||||
preview_tools="not deployed"
|
||||
|
||||
# parse if affected is any of three main applications, if none - use all of them
|
||||
if echo "$affected" | grep -q governance; then
|
||||
echo "Governance is affected"
|
||||
projects_e2e+='"governance-e2e" '
|
||||
projects_array+=("governance")
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
fi
|
||||
if echo "$affected" | grep -q trading; then
|
||||
echo "Trading is affected"
|
||||
projects_e2e+='"trading-e2e" '
|
||||
projects_array+=("trading")
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
fi
|
||||
if echo "$affected" | grep -q explorer; then
|
||||
echo "Explorer is affected"
|
||||
projects_e2e+='"explorer-e2e" '
|
||||
projects_array+=("explorer")
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
fi
|
||||
if [[ -z "$projects_e2e" ]]; then
|
||||
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
|
||||
if [[ ${#projects_array[@]} -eq 0 ]]; then
|
||||
projects_array=("governance" "trading" "explorer")
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
fi
|
||||
projects="$(echo $projects_e2e | sed 's|-e2e||g')"
|
||||
|
||||
# applications parsed before this loop are applicable for running e2e-tests
|
||||
projects_e2e_array=()
|
||||
for project in "${projects_array[@]}"; do
|
||||
projects_e2e_array+=("${project}-e2e")
|
||||
done
|
||||
# all applications below this loop are not applicable for running e2e-test
|
||||
|
||||
# check if pull request event to deploy tools
|
||||
if [[ "${{ github.event_name }}" = "pull_request" ]]; then
|
||||
if echo "$affected" | grep -q multisig-signer; then
|
||||
echo "Tools are affected"
|
||||
# tools are only applicable to check previews or deploy from develop to mainnet
|
||||
echo "Deploying tools on preview"
|
||||
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
|
||||
projects+=' "multisig-signer" '
|
||||
|
||||
projects_array+=("multisig-signer")
|
||||
fi
|
||||
# those apps deploy only from develop to mainnet
|
||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
||||
if echo "$affected" | grep -q multisig-signer; then
|
||||
echo "Tools are affected"
|
||||
# tools are only applicable to check previews or deploy from develop to mainnet
|
||||
echo "Deploying tools on s3"
|
||||
projects+=' "multisig-signer" '
|
||||
|
||||
projects_array+=("multisig-signer")
|
||||
fi
|
||||
if echo "$affected" | grep -q static; then
|
||||
echo "static is affected"
|
||||
echo "Deploying static on s3"
|
||||
projects+=' "static" '
|
||||
|
||||
projects_array+=("static")
|
||||
fi
|
||||
if echo "$affected" | grep -q ui-toolkit; then
|
||||
echo "ui-toolkit is affected"
|
||||
echo "Deploying ui-toolkit on s3"
|
||||
projects+=' "ui-toolkit" '
|
||||
|
||||
projects_array+=("ui-toolkit")
|
||||
fi
|
||||
fi
|
||||
|
||||
projects_e2e=${projects_e2e%?}
|
||||
projects_e2e=[${projects_e2e// /,}]
|
||||
projects=[${projects// /,}]
|
||||
echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
|
||||
echo PROJECTS=$projects >> $GITHUB_ENV
|
||||
echo "Projects: ${projects_array[@]}"
|
||||
echo "Projects E2E: ${projects_e2e_array[@]}"
|
||||
projects_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_array[@]}")
|
||||
projects_e2e_json=$(jq -M --compact-output --null-input '$ARGS.positional' --args -- "${projects_e2e_array[@]}")
|
||||
|
||||
echo PROJECTS_E2E=$projects_e2e_json >> $GITHUB_ENV
|
||||
echo PROJECTS=$projects_json >> $GITHUB_ENV
|
||||
|
||||
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
|
||||
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
|
||||
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
|
||||
@@ -182,7 +204,7 @@ jobs:
|
||||
cypress:
|
||||
needs: lint-test-build
|
||||
name: '(CI) cypress'
|
||||
if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
|
||||
# if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
|
||||
uses: ./.github/workflows/cypress-run.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -192,7 +214,7 @@ jobs:
|
||||
publish-dist:
|
||||
needs: lint-test-build
|
||||
name: '(CD) publish dist'
|
||||
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
|
||||
# if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
|
||||
uses: ./.github/workflows/publish-dist.yml
|
||||
secrets: inherit
|
||||
with:
|
||||
@@ -203,7 +225,7 @@ jobs:
|
||||
needs:
|
||||
- publish-dist
|
||||
- lint-test-build
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
|
||||
timeout-minutes: 60
|
||||
name: '(CD) comment preview links'
|
||||
steps:
|
||||
|
||||
@@ -33,6 +33,7 @@ jobs:
|
||||
with:
|
||||
fetch-depth: 0
|
||||
path: './frontend-monorepo'
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
# Restore node_modules from cache if possible
|
||||
- name: Restore node_modules from cache
|
||||
|
||||
@@ -11,6 +11,8 @@ jobs:
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Setup node
|
||||
uses: actions/setup-node@v3
|
||||
|
||||
@@ -19,6 +19,8 @@ jobs:
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Set up QEMU
|
||||
id: quemu
|
||||
@@ -31,7 +33,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Log in to the Container registry (ghcr)
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -143,7 +145,7 @@ jobs:
|
||||
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
|
||||
|
||||
- name: Image digest
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
||||
- name: Sanity check docker image
|
||||
@@ -158,7 +160,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
continue-on-error: true
|
||||
id: ghcr-push
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
|
||||
with:
|
||||
context: .
|
||||
file: docker/node-outside-docker.Dockerfile
|
||||
@@ -228,7 +230,7 @@ jobs:
|
||||
|
||||
- name: Add preview label
|
||||
uses: actions-ecosystem/action-add-labels@v1
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
|
||||
with:
|
||||
labels: ${{ matrix.app }}-preview
|
||||
number: ${{ github.event.number }}
|
||||
@@ -265,7 +267,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Check out ipfs-redirect
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && endsWith(github.ref, 'main') }}
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: 'vegaprotocol/ipfs-redirect'
|
||||
@@ -273,11 +275,12 @@ jobs:
|
||||
fetch-depth: '0'
|
||||
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
|
||||
- name: Update console.vega.xyz DNS to redirect to the new console
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && endsWith(github.ref, 'main') }}
|
||||
- name: Update interstitial page to point to the new console
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && ( endsWith(github.ref, 'main') || endsWith(github.ref, 'testnet') ) }}
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
|
||||
run: |
|
||||
# set CID
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
export PATH="$PATH:$PWD/kubo"
|
||||
@@ -285,64 +288,28 @@ jobs:
|
||||
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
|
||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||
|
||||
ls -al ipfs-redirect
|
||||
|
||||
echo $new_hash > ipfs-redirect/cidv0.txt
|
||||
echo $new_cid > ipfs-redirect/cidv1.txt
|
||||
|
||||
(
|
||||
cd ipfs-redirect
|
||||
|
||||
# configure git
|
||||
git status
|
||||
cat .git/config
|
||||
git config --global user.email "vega-ci-bot@vega.xyz"
|
||||
git config --global user.name "vega-ci-bot"
|
||||
|
||||
branch_name="update-hash-${{ github.ref }}"
|
||||
git checkout -b "$branch_name"
|
||||
# update CID files
|
||||
if echo ${{ github.ref }} | grep -q main; then
|
||||
echo $new_hash > cidv0-mainnet.txt
|
||||
echo $new_cid > cidv1-mainnet.txt
|
||||
git add cidv0-mainnet.txt cidv1-mainnet.txt
|
||||
elif echo ${{ github.ref }} | grep -q release/testnet; then
|
||||
echo $new_hash > cidv0-fairground.txt
|
||||
echo $new_cid > cidv1-fairground.txt
|
||||
git add cidv0-fairground.txt cidv1-fairground.txt
|
||||
fi
|
||||
|
||||
# create commit
|
||||
commit_msg="Automated hash update from ${{ github.ref }}"
|
||||
git add cidv0.txt cidv1.txt
|
||||
git commit -m "$commit_msg"
|
||||
git push -u origin "$branch_name"
|
||||
pr_url="$(gh pr create --title "${commit_msg}" --body 'automated pull request to update CIDs')"
|
||||
echo $pr_url
|
||||
# once auto merge get's enabled on documentation repo let's do follow up
|
||||
sleep 5
|
||||
gh pr merge "${pr_url}" --delete-branch --squash --admin
|
||||
git push -u origin "main"
|
||||
)
|
||||
|
||||
- name: Update console.fairground.wtf DNS to redirect to the new console
|
||||
if: ${{ matrix.app == 'trading' && github.event_name == 'push' && endsWith(github.ref, 'testnet') }}
|
||||
run: |
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
export PATH="$PATH:$PWD/kubo"
|
||||
which ipfs
|
||||
new_hash=$(cat ${{ matrix.app }}-ipfs-hash)
|
||||
new_cid=$(ipfs cid format -v 1 -b base32 $new_hash)
|
||||
|
||||
# Generate console URL
|
||||
|
||||
new_console_url_type=ipfs
|
||||
# new_console_url_type=ipns
|
||||
|
||||
new_console_url_domain=cf-ipfs.com
|
||||
# new_console_url_domain=dweb.link
|
||||
|
||||
new_console_url="http://${new_cid}.${new_console_url_type}.${new_console_url_domain}/"
|
||||
echo "new_console_url=${new_console_url}"
|
||||
|
||||
# Update record in DNSimple
|
||||
# docs: https://developer.dnsimple.com/v2/zones/records/#updateZoneRecord
|
||||
dnsimple_account_id=84895
|
||||
dnsimple_zone_name=fairground.wtf
|
||||
dnsimple_record_id=45300615
|
||||
# see: https://dnsimple.com/a/84895/domains/fairground.wtf/records/45300615/edit
|
||||
curl --fail -H 'Authorization: Bearer ${{ secrets.DNSIMPLE_API_TOKEN }}' \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Content-Type: application/json' \
|
||||
-X PATCH \
|
||||
-d "{
|
||||
\"content\": \"${new_console_url}\"
|
||||
}" \
|
||||
https://api.dnsimple.com/v2/${dnsimple_account_id}/zones/${dnsimple_zone_name}/records/${dnsimple_record_id}
|
||||
|
||||
@@ -77,7 +77,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
hide={window.innerWidth <= BREAKPOINT_MD}
|
||||
valueGetter={({
|
||||
data,
|
||||
}: VegaValueGetterParams<MarketFieldsFragment, 'state'>) => {
|
||||
}: VegaValueGetterParams<MarketFieldsFragment>) => {
|
||||
return data?.state ? MarketStateMapping[data?.state] : '-';
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
describe('TxDetailsLiquidityAmendment', () => {
|
||||
const mockTxData = {
|
||||
hash: 'test',
|
||||
command: {
|
||||
liquidityProvisionAmendment: {
|
||||
marketId: 'BTC-USD',
|
||||
commitmentAmount: 100,
|
||||
fee: '0.01',
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockPubKey = '123';
|
||||
const mockBlockData = {
|
||||
result: {
|
||||
block: {
|
||||
header: {
|
||||
height: '123',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should render the component with correct data', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquidityAmendment
|
||||
txData={mockTxData as BlockExplorerTransactionResult}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(getByText('Market')).toBeInTheDocument();
|
||||
expect(getByText('BTC-USD')).toBeInTheDocument();
|
||||
expect(getByText('Commitment amount')).toBeInTheDocument();
|
||||
expect(getByText('100')).toBeInTheDocument();
|
||||
expect(getByText('Fee')).toBeInTheDocument();
|
||||
expect(getByText('1%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display awaiting message when tx data is undefined', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquidityAmendment
|
||||
txData={undefined}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Awaiting Block Explorer transaction details')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display awaiting message when liquidityProvisionAmendment is undefined', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquidityAmendment
|
||||
txData={{ command: {} } as BlockExplorerTransactionResult}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Awaiting Block Explorer transaction details')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
|
||||
import PriceInMarket from '../../price-in-market/price-in-market';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export type LiquidityAmendment =
|
||||
components['schemas']['v1LiquidityProvisionAmendment'];
|
||||
@@ -34,6 +35,10 @@ export const TxDetailsLiquidityAmendment = ({
|
||||
txData.command.liquidityProvisionAmendment;
|
||||
const marketId: string = amendment.marketId || '-';
|
||||
|
||||
const fee = amendment.fee
|
||||
? new BigNumber(amendment.fee).times(100).toString()
|
||||
: '-';
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
@@ -63,7 +68,7 @@ export const TxDetailsLiquidityAmendment = ({
|
||||
{amendment.fee ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Fee')}</TableCell>
|
||||
<TableCell>{amendment.fee}%</TableCell>
|
||||
<TableCell>{fee}%</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
|
||||
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
|
||||
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
describe('TxDetailsLiquiditySubmission', () => {
|
||||
const mockTxData = {
|
||||
hash: 'test',
|
||||
command: {
|
||||
liquidityProvisionSubmission: {
|
||||
marketId: 'BTC-USD',
|
||||
commitmentAmount: 100,
|
||||
fee: '0.01',
|
||||
},
|
||||
},
|
||||
};
|
||||
const mockPubKey = '123';
|
||||
const mockBlockData = {
|
||||
result: {
|
||||
block: {
|
||||
header: {
|
||||
height: '123',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
it('should render the component with correct data', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquiditySubmission
|
||||
txData={mockTxData as BlockExplorerTransactionResult}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(getByText('Market')).toBeInTheDocument();
|
||||
expect(getByText('BTC-USD')).toBeInTheDocument();
|
||||
expect(getByText('Commitment amount')).toBeInTheDocument();
|
||||
expect(getByText('100')).toBeInTheDocument();
|
||||
expect(getByText('Fee')).toBeInTheDocument();
|
||||
expect(getByText('1%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display awaiting message when tx data is undefined', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquiditySubmission
|
||||
txData={undefined}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Awaiting Block Explorer transaction details')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display awaiting message when liquidityProvisionSubmission is undefined', () => {
|
||||
const { getByText } = render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxDetailsLiquiditySubmission
|
||||
txData={{ command: {} } as BlockExplorerTransactionResult}
|
||||
pubKey={mockPubKey}
|
||||
blockData={mockBlockData as TendermintBlocksResponse}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
getByText('Awaiting Block Explorer transaction details')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -7,6 +7,7 @@ import { TableCell, TableRow, TableWithTbody } from '../../table';
|
||||
import type { components } from '../../../../types/explorer';
|
||||
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
|
||||
import PriceInMarket from '../../price-in-market/price-in-market';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
export type LiquiditySubmission =
|
||||
components['schemas']['v1LiquidityProvisionSubmission'];
|
||||
@@ -33,6 +34,10 @@ export const TxDetailsLiquiditySubmission = ({
|
||||
txData.command.liquidityProvisionSubmission;
|
||||
const marketId: string = submission.marketId || '-';
|
||||
|
||||
const fee = submission.fee
|
||||
? new BigNumber(submission.fee).times(100).toString()
|
||||
: '-';
|
||||
|
||||
return (
|
||||
<>
|
||||
<TableWithTbody className="mb-8" allowWrap={true}>
|
||||
@@ -62,7 +67,7 @@ export const TxDetailsLiquiditySubmission = ({
|
||||
{submission.fee ? (
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell>{t('Fee')}</TableCell>
|
||||
<TableCell>{submission.fee}%</TableCell>
|
||||
<TableCell>{fee}%</TableCell>
|
||||
</TableRow>
|
||||
) : null}
|
||||
</TableWithTbody>
|
||||
|
||||
@@ -49,7 +49,7 @@ module.exports = defineConfig({
|
||||
vegaTokenContractAddress: '0xF41bD86d462D36b997C0bbb4D97a0a3382f205B7',
|
||||
vegaTokenAddress: '0x67175Da1D5e966e40D11c4B2519392B2058373de',
|
||||
txTimeout: { timeout: 70000 },
|
||||
epochTimeout: { timeout: 10000 },
|
||||
epochTimeout: { timeout: 12000 },
|
||||
blockConfirmations: 3,
|
||||
grepTags: '@regression @smoke @slow',
|
||||
grepFilterSpecs: true,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
export const previousEpochData = {
|
||||
epoch: {
|
||||
id: '7611',
|
||||
validatorsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: 'cd96782bc0ad5679869cf69fe7838a92212da7f53b4a214bed68067117494122',
|
||||
stakedTotal: '3154229668720612941799',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.2',
|
||||
performanceScore: '1',
|
||||
multisigScore: '0',
|
||||
validatorScore: '0.2',
|
||||
normalisedScore: '0.2007216887087119',
|
||||
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
__typename: 'RewardScore',
|
||||
},
|
||||
rankingScore: {
|
||||
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
rankingScore: '0.211713765544955625',
|
||||
stakeScore: '0.2016321576618625',
|
||||
performanceScore: '1',
|
||||
votingPower: '2007',
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
__typename: 'NodeEdge',
|
||||
},
|
||||
{
|
||||
node: {
|
||||
id: '887d936f797a47032eceb572a13b69b581d3b1fa595a7d021a3e3cf2a5d2acfd',
|
||||
stakedTotal: '3151161904761904764551',
|
||||
rewardScore: {
|
||||
rawValidatorScore: '0.2',
|
||||
performanceScore: '1',
|
||||
multisigScore: '1',
|
||||
validatorScore: '0.2',
|
||||
normalisedScore: '0.2007216887087119',
|
||||
validatorStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
__typename: 'RewardScore',
|
||||
},
|
||||
rankingScore: {
|
||||
status: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
previousStatus: 'VALIDATOR_NODE_STATUS_TENDERMINT',
|
||||
rankingScore: '0.211507855409133255',
|
||||
stakeScore: '0.2014360527706031',
|
||||
performanceScore: '1',
|
||||
votingPower: '2007',
|
||||
__typename: 'RankingScore',
|
||||
},
|
||||
__typename: 'Node',
|
||||
},
|
||||
__typename: 'NodeEdge',
|
||||
},
|
||||
],
|
||||
__typename: 'NodesConnection',
|
||||
},
|
||||
__typename: 'Epoch',
|
||||
},
|
||||
};
|
||||
@@ -14,25 +14,31 @@ import {
|
||||
submitUniqueRawProposal,
|
||||
voteForProposal,
|
||||
} from '../../../../governance-e2e/src/support/governance.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
|
||||
import {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated,
|
||||
stakingPageAssociateTokens,
|
||||
stakingPageDisassociateAllTokens,
|
||||
} from '../../../../governance-e2e/src/support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
|
||||
import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
} from '../../support/wallet-functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
|
||||
const proposalVoteProgressForPercentage =
|
||||
'[data-testid="vote-progress-indicator-percentage-for"]';
|
||||
'vote-progress-indicator-percentage-for';
|
||||
const proposalVoteProgressAgainstPercentage =
|
||||
'[data-testid="vote-progress-indicator-percentage-against"]';
|
||||
const proposalVoteProgressForTokens =
|
||||
'[data-testid="vote-progress-indicator-tokens-for"]';
|
||||
'vote-progress-indicator-percentage-against';
|
||||
const proposalVoteProgressForTokens = 'vote-progress-indicator-tokens-for';
|
||||
const proposalVoteProgressAgainstTokens =
|
||||
'[data-testid="vote-progress-indicator-tokens-against"]';
|
||||
const changeVoteButton = '[data-testid="change-vote-button"]';
|
||||
const proposalDetailsTitle = '[data-testid="proposal-title"]';
|
||||
const proposalDetailsDescription = '[data-testid="proposal-description"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
'vote-progress-indicator-tokens-against';
|
||||
const changeVoteButton = 'change-vote-button';
|
||||
const proposalDetailsTitle = 'proposal-title';
|
||||
const proposalDetailsDescription = 'proposal-description';
|
||||
const openProposals = 'open-proposals';
|
||||
const viewProposalButton = 'view-proposal-btn';
|
||||
const proposalDescriptionToggle = 'proposal-description-toggle';
|
||||
const voteBreakdownToggle = 'vote-breakdown-toggle';
|
||||
const proposalTermsToggle = 'proposal-json-toggle';
|
||||
@@ -44,7 +50,7 @@ describe(
|
||||
before('connect wallets and set approval limit', function () {
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
cy.associateTokensToVegaWallet('1');
|
||||
// cy.associateTokensToVegaWallet('1');
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
@@ -65,18 +71,18 @@ describe(
|
||||
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.getByTestId(openProposals).within(() => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
cy.get(viewProposalButton).should('be.visible').click();
|
||||
cy.getByTestId(viewProposalButton).should('be.visible').click();
|
||||
});
|
||||
});
|
||||
cy.get(proposalDetailsTitle).should(
|
||||
cy.getByTestId(proposalDetailsTitle).should(
|
||||
'contain.text',
|
||||
rawProposal.rationale.title
|
||||
);
|
||||
cy.getByTestId(proposalDescriptionToggle).click();
|
||||
cy.getByTestId('proposal-description-toggle');
|
||||
cy.get(proposalDetailsDescription)
|
||||
cy.getByTestId(proposalDetailsDescription)
|
||||
.find('p')
|
||||
.should('have.text', proposalDescription);
|
||||
});
|
||||
@@ -110,7 +116,7 @@ describe(
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
cy.wrap(
|
||||
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
|
||||
@@ -132,7 +138,7 @@ describe(
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
});
|
||||
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
|
||||
@@ -159,7 +165,7 @@ describe(
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
});
|
||||
// 3001-VOTE-080
|
||||
@@ -176,14 +182,16 @@ describe(
|
||||
.contains(votedDate)
|
||||
.should('be.visible');
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
|
||||
cy.getByTestId(proposalVoteProgressForPercentage) // 3001-VOTE-072
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
cy.get(proposalVoteProgressAgainstPercentage)
|
||||
cy.getByTestId(proposalVoteProgressAgainstPercentage)
|
||||
.contains('0.00%')
|
||||
.and('be.visible');
|
||||
cy.get(proposalVoteProgressForTokens).contains('1.00').and('be.visible');
|
||||
cy.get(proposalVoteProgressAgainstTokens)
|
||||
cy.getByTestId(proposalVoteProgressForTokens)
|
||||
.contains('1.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(proposalVoteProgressAgainstTokens)
|
||||
.contains('0.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(voteBreakdownToggle).click();
|
||||
@@ -204,15 +212,15 @@ describe(
|
||||
getProposalInformationFromTable('Number of voting parties')
|
||||
.should('have.text', '1')
|
||||
.and('be.visible');
|
||||
cy.get(changeVoteButton).should('be.visible').click();
|
||||
cy.getByTestId(changeVoteButton).should('be.visible').click();
|
||||
voteForProposal('for');
|
||||
// 3001-VOTE-064
|
||||
getProposalInformationFromTable('Tokens for proposal')
|
||||
.should('have.text', (1).toFixed(2))
|
||||
.and('be.visible');
|
||||
cy.get(changeVoteButton).should('be.visible').click();
|
||||
cy.getByTestId(changeVoteButton).should('be.visible').click();
|
||||
voteForProposal('against');
|
||||
cy.get(proposalVoteProgressAgainstPercentage)
|
||||
cy.getByTestId(proposalVoteProgressAgainstPercentage)
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
getProposalInformationFromTable('Tokens against proposal')
|
||||
@@ -229,13 +237,15 @@ describe(
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
});
|
||||
voteForProposal('for');
|
||||
// 3001-VOTE-079
|
||||
cy.contains('You voted: For').should('be.visible');
|
||||
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
|
||||
cy.getByTestId(proposalVoteProgressForTokens)
|
||||
.contains('1')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(voteBreakdownToggle).click();
|
||||
getProposalInformationFromTable('Total Supply')
|
||||
.invoke('text')
|
||||
@@ -251,22 +261,22 @@ describe(
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage)
|
||||
cy.getByTestId(proposalVoteProgressForPercentage)
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
cy.get(proposalVoteProgressAgainstPercentage)
|
||||
cy.getByTestId(proposalVoteProgressAgainstPercentage)
|
||||
.contains('0.00%')
|
||||
.and('be.visible');
|
||||
// 3001-VOTE-065
|
||||
cy.get(changeVoteButton).should('be.visible').click();
|
||||
cy.getByTestId(changeVoteButton).should('be.visible').click();
|
||||
voteForProposal('for');
|
||||
cy.get(proposalVoteProgressForTokens)
|
||||
cy.getByTestId(proposalVoteProgressForTokens)
|
||||
.contains(tokensRequiredToAchieveResult)
|
||||
.and('be.visible');
|
||||
cy.get(proposalVoteProgressAgainstTokens)
|
||||
cy.getByTestId(proposalVoteProgressAgainstTokens)
|
||||
.contains('0.00')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(voteBreakdownToggle).click();
|
||||
@@ -297,5 +307,36 @@ describe(
|
||||
.and('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to vote for proposal twice by switching public key', function () {
|
||||
ensureSpecifiedUnstakedTokensAreAssociated('1');
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
voteForProposal('for');
|
||||
cy.contains('You voted: For').should('be.visible');
|
||||
ethereumWalletConnect();
|
||||
switchVegaWalletPubKey();
|
||||
stakingPageAssociateTokens('2');
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
cy.getByTestId('you-voted').should('not.exist');
|
||||
voteForProposal('against');
|
||||
cy.contains('You voted: Against').should('be.visible');
|
||||
switchVegaWalletPubKey();
|
||||
cy.getByTestId(proposalVoteProgressForTokens).should(
|
||||
'contain.text',
|
||||
'1.00'
|
||||
);
|
||||
// Checking vote status for different public keys is displayed correctly
|
||||
cy.contains('You voted: For').should('be.visible');
|
||||
});
|
||||
switchVegaWalletPubKey();
|
||||
stakingPageDisassociateAllTokens();
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -16,16 +16,16 @@ import {
|
||||
} from '../../support/proposal.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
|
||||
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const closedProposals = '[data-testid="closed-proposals"]';
|
||||
const proposalStatus = '[data-testid="proposal-status"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
const votesTable = '[data-testid="votes-table"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const closedProposals = 'closed-proposals';
|
||||
const proposalStatus = 'proposal-status';
|
||||
const viewProposalButton = 'view-proposal-btn';
|
||||
const votesTable = 'votes-table';
|
||||
const openProposals = 'open-proposals';
|
||||
const proposalVoteProgressForPercentage =
|
||||
'[data-testid="vote-progress-indicator-percentage-for"]';
|
||||
'vote-progress-indicator-percentage-for';
|
||||
const proposalTimeout = { timeout: 8000 };
|
||||
|
||||
context(
|
||||
@@ -55,18 +55,18 @@ context(
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.get(closedProposals).within(() => {
|
||||
cy.getByTestId(closedProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalStatus).should('have.text', 'Enacted');
|
||||
cy.get(viewProposalButton).click();
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
cy.getByTestId('proposal-type').should('have.text', 'New market');
|
||||
cy.get(proposalStatus).should('have.text', 'Enacted');
|
||||
cy.get(votesTable).within(() => {
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
|
||||
cy.getByTestId(votesTable).within(() => {
|
||||
cy.contains('Vote passed.').should('be.visible');
|
||||
cy.contains('Voting has ended.').should('be.visible');
|
||||
});
|
||||
@@ -81,21 +81,27 @@ context(
|
||||
navigateTo(navigation.proposals);
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.getByTestId(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
.within(() => cy.getByTestId(viewProposalButton).click());
|
||||
});
|
||||
cy.get(proposalStatus).should('have.text', 'Open');
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Open');
|
||||
voteForProposal('for');
|
||||
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Passed');
|
||||
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
|
||||
cy.get(votesTable).within(() => {
|
||||
cy.getByTestId(proposalStatus, proposalTimeout)
|
||||
.should('have.text', 'Passed')
|
||||
.then(() => {
|
||||
cy.getByTestId(proposalStatus, proposalTimeout).should(
|
||||
'have.text',
|
||||
'Enacted'
|
||||
);
|
||||
});
|
||||
cy.getByTestId(votesTable).within(() => {
|
||||
cy.contains('Vote passed.').should('be.visible');
|
||||
cy.contains('Voting has ended.').should('be.visible');
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage)
|
||||
cy.getByTestId(proposalVoteProgressForPercentage)
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
});
|
||||
@@ -109,15 +115,18 @@ context(
|
||||
navigateTo(navigation.proposals);
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.get(openProposals, { timeout: 6000 }).within(() => {
|
||||
cy.getByTestId(openProposals, { timeout: 6000 }).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
.within(() => cy.getByTestId(viewProposalButton).click());
|
||||
});
|
||||
cy.get(proposalStatus).should('have.text', 'Open');
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Open');
|
||||
voteForProposal('for');
|
||||
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
|
||||
cy.getByTestId(proposalStatus, proposalTimeout).should(
|
||||
'have.text',
|
||||
'Enacted'
|
||||
);
|
||||
});
|
||||
|
||||
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
|
||||
@@ -128,14 +137,17 @@ context(
|
||||
navigateTo(navigation.proposals);
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.getByTestId(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
.within(() => cy.getByTestId(viewProposalButton).click());
|
||||
});
|
||||
cy.get(proposalStatus).should('have.text', 'Open');
|
||||
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Declined');
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Open');
|
||||
cy.getByTestId(proposalStatus, proposalTimeout).should(
|
||||
'have.text',
|
||||
'Declined'
|
||||
);
|
||||
getProposalInformationFromTable('Rejection reason')
|
||||
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
|
||||
.and('be.visible');
|
||||
|
||||
@@ -32,24 +32,23 @@ import {
|
||||
import {
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import type { testFreeformProposal } from '../../support/common-interfaces';
|
||||
|
||||
const vegaWalletStakedBalances =
|
||||
'[data-testid="vega-wallet-balance-staked-validators"]';
|
||||
const vegaWalletAssociatedBalance = '[data-testid="associated-amount"]';
|
||||
const vegaWalletNameElement = '[data-testid="wallet-name"]';
|
||||
const vegaWallet = '[data-testid="vega-wallet"]';
|
||||
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
const rawProposalData = '[data-testid="proposal-data"]';
|
||||
const voteButtons = '[data-testid="vote-buttons"]';
|
||||
const vegaWalletStakedBalances = 'vega-wallet-balance-staked-validators';
|
||||
const vegaWalletAssociatedBalance = 'associated-amount';
|
||||
const vegaWalletNameElement = 'wallet-name';
|
||||
const vegaWallet = 'vega-wallet';
|
||||
const connectToVegaWalletButton = 'connect-to-vega-wallet-btn';
|
||||
const newProposalSubmitButton = 'proposal-submit';
|
||||
const viewProposalButton = 'view-proposal-btn';
|
||||
const rawProposalData = 'proposal-data';
|
||||
const voteButtons = 'vote-buttons';
|
||||
const rejectProposalsLink = '[href="/proposals/rejected"]';
|
||||
const feedbackError = '[data-testid="Error"]';
|
||||
const noOpenProposals = '[data-testid="no-open-proposals"]';
|
||||
const noClosedProposals = '[data-testid="no-closed-proposals"]';
|
||||
const feedbackError = 'Error';
|
||||
const noOpenProposals = 'no-open-proposals';
|
||||
const noClosedProposals = 'no-closed-proposals';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const epochTimeout = Cypress.env('epochTimeout');
|
||||
const proposalTimeout = { timeout: 14000 };
|
||||
@@ -93,10 +92,10 @@ context(
|
||||
// Test can only pass if run before other proposal tests.
|
||||
it.skip('Should be able to see that no proposals exist', function () {
|
||||
// 3001-VOTE-003
|
||||
cy.get(noOpenProposals)
|
||||
cy.getByTestId(noOpenProposals)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'There are no open or yet to enact proposals');
|
||||
cy.get(noClosedProposals)
|
||||
cy.getByTestId(noClosedProposals)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'There are no enacted or rejected proposals');
|
||||
});
|
||||
@@ -126,11 +125,14 @@ context(
|
||||
stakingValidatorPageAddStake('2');
|
||||
closeStakingDialog();
|
||||
|
||||
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
|
||||
cy.getByTestId(vegaWalletStakedBalances, txTimeout).should(
|
||||
'contain',
|
||||
'2'
|
||||
);
|
||||
createRawProposal();
|
||||
});
|
||||
|
||||
it.skip('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
|
||||
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
|
||||
cy.get('input:invalid')
|
||||
@@ -138,7 +140,7 @@ context(
|
||||
.should('equal', 'Value must be greater than or equal to 1.');
|
||||
});
|
||||
|
||||
it.skip('Creating a proposal - proposal rejected - when closing time later than system default', function () {
|
||||
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
enterUniqueFreeFormProposalBody(
|
||||
'100000',
|
||||
@@ -168,7 +170,7 @@ context(
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
cy.contains('Rejected').should('be.visible');
|
||||
cy.contains('Close time too late').should('be.visible');
|
||||
cy.get(viewProposalButton).click();
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
cy.getByTestId('proposal-status').should('have.text', 'Rejected');
|
||||
@@ -185,14 +187,14 @@ context(
|
||||
const errorMsg =
|
||||
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)';
|
||||
vegaWalletTeardown();
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).contains(
|
||||
'0.00',
|
||||
txTimeout
|
||||
);
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
cy.get(feedbackError).should('have.text', errorMsg);
|
||||
cy.getByTestId(feedbackError).should('have.text', errorMsg);
|
||||
closeDialog();
|
||||
});
|
||||
|
||||
@@ -205,7 +207,7 @@ context(
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
cy.get(feedbackError).should('have.text', errorMsg);
|
||||
cy.getByTestId(feedbackError).should('have.text', errorMsg);
|
||||
closeDialog();
|
||||
});
|
||||
|
||||
@@ -221,17 +223,17 @@ context(
|
||||
createTenDigitUnixTimeStampForSpecifiedDays(8);
|
||||
freeformProposal.unexpected = `i shouldn't be here`;
|
||||
const proposalPayload = JSON.stringify(freeformProposal);
|
||||
cy.get(rawProposalData).type(proposalPayload, {
|
||||
cy.getByTestId(rawProposalData).type(proposalPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
cy.get(feedbackError).should('have.text', errorMsg);
|
||||
cy.getByTestId(feedbackError).should('have.text', errorMsg);
|
||||
closeDialog();
|
||||
cy.get(rawProposalData)
|
||||
cy.getByTestId(rawProposalData)
|
||||
.invoke('val')
|
||||
.should('contain', "i shouldn't be here");
|
||||
});
|
||||
@@ -249,15 +251,15 @@ context(
|
||||
rawProposal.terms.unexpectedField = `i shouldn't be here`;
|
||||
const proposalPayload = JSON.stringify(rawProposal);
|
||||
|
||||
cy.get(rawProposalData).type(proposalPayload, {
|
||||
cy.getByTestId(rawProposalData).type(proposalPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
cy.get(feedbackError).should('have.text', errorMsg);
|
||||
cy.getByTestId(feedbackError).should('have.text', errorMsg);
|
||||
closeDialog();
|
||||
});
|
||||
|
||||
@@ -265,10 +267,10 @@ context(
|
||||
// 3006-PASC-006 3006-PASC-007 3008-PFRO-018 3008-PFRO-019 3003-PMAN-006 3003-PMAN-007
|
||||
it('Unable to submit proposal without valid json', function () {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.getByTestId(newProposalSubmitButton).click();
|
||||
cy.getByTestId('input-error-text').should('have.text', 'Required');
|
||||
cy.get(rawProposalData).type('Not a valid json string');
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.getByTestId(rawProposalData).type('Not a valid json string');
|
||||
cy.getByTestId(newProposalSubmitButton).click();
|
||||
cy.getByTestId('input-error-text').should(
|
||||
'have.text',
|
||||
'Must be valid JSON'
|
||||
@@ -283,22 +285,22 @@ context(
|
||||
submitUniqueRawProposal({ proposalTitle: proposalTitle });
|
||||
ethereumWalletConnect();
|
||||
stakingPageDisassociateTokens('0.0001');
|
||||
cy.get(vegaWallet)
|
||||
cy.getByTestId(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'0.9999'
|
||||
);
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
cy.contains('Vote breakdown').should('be.visible', {
|
||||
timeout: 10000,
|
||||
});
|
||||
cy.get(voteButtons).should('not.exist');
|
||||
cy.getByTestId(voteButtons).should('not.exist');
|
||||
cy.getByTestId('min-proposal-requirements').should(
|
||||
'have.text',
|
||||
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
|
||||
@@ -311,20 +313,20 @@ context(
|
||||
cy.get('[data-testid="disconnect"]').click();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() =>
|
||||
cy.get(viewProposalButton).click()
|
||||
cy.getByTestId(viewProposalButton).click()
|
||||
);
|
||||
});
|
||||
// 3001-VOTE-075
|
||||
// 3001-VOTE-076
|
||||
cy.get(connectToVegaWalletButton)
|
||||
cy.getByTestId(connectToVegaWalletButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet')
|
||||
.click();
|
||||
cy.getByTestId('connector-jsonRpc').click();
|
||||
cy.get(vegaWalletNameElement).should('be.visible');
|
||||
cy.get(connectToVegaWalletButton).should('not.exist');
|
||||
cy.getByTestId(vegaWalletNameElement).should('be.visible');
|
||||
cy.getByTestId(connectToVegaWalletButton).should('not.exist');
|
||||
// 3001-VOTE-100
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).contains(
|
||||
'1.00',
|
||||
txTimeout
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
} from '../../support/common.functions';
|
||||
import {
|
||||
getDownloadedProposalJsonPath,
|
||||
getProposalFromTitle,
|
||||
submitUniqueRawProposal,
|
||||
} from '../../support/governance.functions';
|
||||
import {
|
||||
@@ -23,38 +24,38 @@ import {
|
||||
} from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletFaucetAssetsWithoutCheck,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const proposalType = '[data-testid="proposal-type"]';
|
||||
const proposalDetails = '[data-testid="proposal-details"]';
|
||||
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
|
||||
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
|
||||
const proposalParameterSelect = '[data-testid="proposal-parameter-select"]';
|
||||
const proposalMarketSelect = '[data-testid="proposal-market-select"]';
|
||||
const newProposalTitle = '[data-testid="proposal-title"]';
|
||||
const newProposalDescription = '[data-testid="proposal-description"]';
|
||||
const newProposalTerms = '[data-testid="proposal-terms"]';
|
||||
const newProposedParameterValue =
|
||||
'[data-testid="selected-proposal-param-new-value"]';
|
||||
const minVoteDeadline = '[data-testid="min-vote"]';
|
||||
const maxVoteDeadline = '[data-testid="max-vote"]';
|
||||
const minValidationDeadline = '[data-testid="min-validation"]';
|
||||
const minEnactDeadline = '[data-testid="min-enactment"]';
|
||||
const maxEnactDeadline = '[data-testid="max-enactment"]';
|
||||
const inputError = '[data-testid="input-error-text"]';
|
||||
const enactmentDeadlineError =
|
||||
'[data-testid="enactment-before-voting-deadline"]';
|
||||
const proposalDownloadBtn = '[data-testid="proposal-download-json"]';
|
||||
const openProposals = 'open-proposals';
|
||||
const proposalType = 'proposal-type';
|
||||
const proposalDetails = 'proposal-details';
|
||||
const newProposalSubmitButton = 'proposal-submit';
|
||||
const proposalVoteDeadline = 'proposal-vote-deadline';
|
||||
const proposalParameterSelect = 'proposal-parameter-select';
|
||||
const proposalMarketSelect = 'proposal-market-select';
|
||||
const newProposalTitle = 'proposal-title';
|
||||
const newProposalDescription = 'proposal-description';
|
||||
const newProposalTerms = 'proposal-terms';
|
||||
const newProposedParameterValue = 'selected-proposal-param-new-value';
|
||||
const minVoteDeadline = 'min-vote';
|
||||
const maxVoteDeadline = 'max-vote';
|
||||
const minValidationDeadline = 'min-validation';
|
||||
const minEnactDeadline = 'min-enactment';
|
||||
const maxEnactDeadline = 'max-enactment';
|
||||
const inputError = 'input-error-text';
|
||||
const enactmentDeadlineError = 'enactment-before-voting-deadline';
|
||||
const proposalDownloadBtn = 'proposal-download-json';
|
||||
const feedbackError = '[data-testid="Error"]';
|
||||
const viewProposalBtn = 'view-proposal-btn';
|
||||
const liquidityVoteStatus = 'liquidity-votes-status';
|
||||
const tokenVoteStatus = 'token-votes-status';
|
||||
const proposalTermsSection = 'proposal';
|
||||
const proposalJsonToggle = 'proposal-json-toggle';
|
||||
const proposalJsonSection = 'proposal-json';
|
||||
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
|
||||
const fUSDCId =
|
||||
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
|
||||
@@ -85,18 +86,20 @@ context(
|
||||
it('Unable to submit network parameter with missing/invalid fields', function () {
|
||||
navigateTo(navigation.proposals);
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.get(proposalDownloadBtn).click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.get(newProposalTitle).type(
|
||||
cy.getByTestId(proposalDownloadBtn).click();
|
||||
cy.getByTestId(inputError).should('have.length', 3);
|
||||
cy.getByTestId(newProposalTitle).type(
|
||||
'Invalid update network parameter proposal'
|
||||
);
|
||||
cy.get(newProposalDescription).type('E2E invalid test for proposals');
|
||||
cy.get(proposalParameterSelect).select(
|
||||
cy.getByTestId(newProposalDescription).type(
|
||||
'E2E invalid test for proposals'
|
||||
);
|
||||
cy.getByTestId(proposalParameterSelect).select(
|
||||
'spam_protection_proposal_min_tokens'
|
||||
);
|
||||
cy.get(newProposedParameterValue).type('0');
|
||||
cy.get(proposalVoteDeadline).clear().type('0');
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(newProposedParameterValue).type('0');
|
||||
cy.getByTestId(proposalVoteDeadline).clear().type('0');
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
@@ -106,7 +109,7 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.getByTestId(newProposalSubmitButton).click();
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_CLOSE_TIME_TOO_SOON');
|
||||
});
|
||||
|
||||
@@ -114,29 +117,33 @@ context(
|
||||
it('Able to download and submit network param proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
// 3007-PNEC-006
|
||||
cy.get(newProposalTitle)
|
||||
cy.getByTestId(newProposalTitle)
|
||||
.siblings()
|
||||
.should('contain.text', '(100 characters or less)');
|
||||
// 3007-PNEC-004 3007-PNEC-005
|
||||
cy.get(newProposalTitle).type('Test update network parameter proposal');
|
||||
cy.getByTestId(newProposalTitle).type(
|
||||
'Test update network parameter proposal'
|
||||
);
|
||||
// 3007-PNEC-009
|
||||
cy.get(newProposalDescription)
|
||||
cy.getByTestId(newProposalDescription)
|
||||
.siblings()
|
||||
.should('contain.text', '(20,000 characters or less)');
|
||||
// 3007-PNEC-007 3007-PNEC-008
|
||||
cy.get(newProposalDescription).type('E2E test for downloading proposals');
|
||||
cy.getByTestId(newProposalDescription).type(
|
||||
'E2E test for downloading proposals'
|
||||
);
|
||||
// 3007-PNEC-010
|
||||
cy.get(proposalParameterSelect).select(
|
||||
cy.getByTestId(proposalParameterSelect).select(
|
||||
'governance_proposal_asset_minClose'
|
||||
);
|
||||
// 3007-PNEC-011
|
||||
cy.get(newProposedParameterValue).type('10s');
|
||||
cy.getByTestId(newProposedParameterValue).type('10s');
|
||||
// 3007-PNEC-012
|
||||
cy.get(proposalVoteDeadline).clear().type('2');
|
||||
cy.getByTestId(proposalVoteDeadline).clear().type('2');
|
||||
// 3007-PNEC-013 3007-PNEC-014
|
||||
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
|
||||
// 3007-PNEC-015
|
||||
cy.get(maxEnactDeadline).click();
|
||||
cy.getByTestId(maxEnactDeadline).click();
|
||||
// 3007-PNEC-016
|
||||
cy.getByTestId('enactment-date').invoke('text').should('not.be.empty');
|
||||
// 3007-PNEC-017
|
||||
@@ -145,7 +152,7 @@ context(
|
||||
).should('be.visible');
|
||||
// 3007-PNE-018
|
||||
cy.log('Download updated proposal file');
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -175,19 +182,21 @@ context(
|
||||
it('Unable to submit network parameter proposal with vote deadline above enactment deadline', function () {
|
||||
navigateTo(navigation.proposals);
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.get(newProposalTitle).type('Test update network parameter proposal');
|
||||
cy.get(newProposalDescription).type('invalid deadlines');
|
||||
cy.get(proposalParameterSelect).select(
|
||||
cy.getByTestId(newProposalTitle).type(
|
||||
'Test update network parameter proposal'
|
||||
);
|
||||
cy.getByTestId(newProposalDescription).type('invalid deadlines');
|
||||
cy.getByTestId(proposalParameterSelect).select(
|
||||
'spam_protection_proposal_min_tokens'
|
||||
);
|
||||
cy.get(newProposedParameterValue).type('0');
|
||||
cy.get(proposalVoteDeadline).clear().type('0');
|
||||
cy.get(maxVoteDeadline).click();
|
||||
cy.get(enactmentDeadlineError).should(
|
||||
cy.getByTestId(newProposedParameterValue).type('0');
|
||||
cy.getByTestId(proposalVoteDeadline).clear().type('0');
|
||||
cy.getByTestId(maxVoteDeadline).click();
|
||||
cy.getByTestId(enactmentDeadlineError).should(
|
||||
'have.text',
|
||||
'Proposal will fail if enactment is earlier than the voting deadline'
|
||||
'The proposal will fail if enactment is earlier than the voting deadline'
|
||||
);
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -198,7 +207,7 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).click();
|
||||
cy.getByTestId(newProposalSubmitButton).click();
|
||||
validateFeedBackMsg(
|
||||
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)'
|
||||
);
|
||||
@@ -209,17 +218,18 @@ context(
|
||||
'Able to submit valid new market proposal',
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
const proposalTitle = 'Test new market proposal';
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.get(newProposalTitle).type('Test new market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(newProposalTitle).type('Test new market proposal');
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.get(newProposalTerms).type(newMarketPayload, {
|
||||
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -230,6 +240,23 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath }); // 3003-PMAN-003
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() =>
|
||||
cy.getByTestId('view-proposal-btn').click()
|
||||
);
|
||||
cy.getByTestId('proposal-market-data').within(() => {
|
||||
cy.getByTestId('proposal-market-data-toggle').click();
|
||||
cy.contains('Key details').click();
|
||||
getMarketProposalDetailsFromTable('Name').should(
|
||||
'have.text',
|
||||
'Token test market'
|
||||
);
|
||||
cy.contains('Settlement asset').click();
|
||||
// Settlement asset symbol
|
||||
cy.getByTestId('3_value').should('have.text', 'fBTC');
|
||||
cy.contains('Oracle').click();
|
||||
cy.getByTestId('oracle-spec-links').should('have.attr', 'href');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -238,19 +265,19 @@ context(
|
||||
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
|
||||
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.get(newProposalTitle).type('Test new market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.getByTestId(inputError).should('have.length', 3);
|
||||
cy.getByTestId(newProposalTitle).type('Test new market proposal');
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
|
||||
newMarketProposal.invalid = 'I am an invalid field';
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.get(newProposalTerms).type(newMarketPayload, {
|
||||
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -261,7 +288,7 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
validateFeedBackMsg(errorMsg);
|
||||
});
|
||||
@@ -269,21 +296,22 @@ context(
|
||||
// Will fail if run after 'Able to submit update market proposal and vote for proposal'
|
||||
// 3002-PROP-022
|
||||
it('Unable to submit update market proposal without equity-like share in the market', function () {
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click(); // switch to second wallet pub key
|
||||
switchVegaWalletPubKey();
|
||||
stakingPageAssociateTokens('1');
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.get(newProposalTitle).type('Test update market proposal - rejected');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.get(proposalMarketSelect).select('Test market 1');
|
||||
cy.getByTestId(newProposalTitle).type(
|
||||
'Test update market proposal - rejected'
|
||||
);
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(proposalMarketSelect).select('Test market 1');
|
||||
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
|
||||
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
|
||||
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
|
||||
cy.getByTestId(newProposalTerms).type(newUpdateMarketProposal, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -294,14 +322,13 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
|
||||
closeDialog();
|
||||
ethereumWalletConnect();
|
||||
stakingPageDisassociateAllTokens();
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
switchVegaWalletPubKey();
|
||||
});
|
||||
|
||||
// 3002-PROP-020
|
||||
@@ -313,17 +340,19 @@ context(
|
||||
vegaWalletPublicKey
|
||||
);
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.get(newProposalTitle).type('Test update market proposal - rejected');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.get(proposalMarketSelect).select('Test market 1');
|
||||
cy.getByTestId(newProposalTitle).type(
|
||||
'Test update market proposal - rejected'
|
||||
);
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(proposalMarketSelect).select('Test market 1');
|
||||
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
|
||||
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
|
||||
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
|
||||
cy.getByTestId(newProposalTerms).type(newUpdateMarketProposal, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -334,7 +363,7 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
|
||||
validateFeedBackMsg(
|
||||
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)'
|
||||
@@ -349,9 +378,9 @@ context(
|
||||
vegaWalletPublicKey
|
||||
);
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.get(newProposalTitle).type('Test update market proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.get(proposalMarketSelect).select('Test market 1');
|
||||
cy.getByTestId(newProposalTitle).type('Test update market proposal');
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(proposalMarketSelect).select('Test market 1');
|
||||
cy.get('[data-testid="update-market-details"]').within(() => {
|
||||
cy.get('dd').eq(0).should('have.text', 'Test market 1');
|
||||
cy.get('dd').eq(1).should('have.text', 'TEST.24h');
|
||||
@@ -366,12 +395,12 @@ context(
|
||||
});
|
||||
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
|
||||
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
|
||||
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
|
||||
cy.getByTestId(newProposalTerms).type(newUpdateMarketProposal, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -418,19 +447,19 @@ context(
|
||||
it('Able to submit new asset proposal using min deadlines', function () {
|
||||
const proposalTitle = 'Test new asset proposal';
|
||||
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
|
||||
cy.get(newProposalTitle).type(proposalTitle);
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(newProposalTitle).type(proposalTitle);
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-asset').then((newAssetProposal) => {
|
||||
const newAssetPayload = JSON.stringify(newAssetProposal);
|
||||
cy.get(newProposalTerms).type(newAssetPayload, {
|
||||
cy.getByTestId(newProposalTerms).type(newAssetPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minValidationDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(minVoteDeadline).click();
|
||||
cy.getByTestId(minValidationDeadline).click();
|
||||
cy.getByTestId(minEnactDeadline).click();
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -441,9 +470,9 @@ context(
|
||||
submitUniqueRawProposal({ proposalBody: filePath, submit: false }); // 3005-PASN-003
|
||||
});
|
||||
});
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
closeDialog();
|
||||
cy.get(newProposalSubmitButton).should('be.visible').click();
|
||||
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
|
||||
// cannot submit a proposal with ERC20 address already in use
|
||||
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
|
||||
validateDialogContentMsg('PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE');
|
||||
@@ -455,8 +484,8 @@ context(
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
cy.getByTestId('proposal-terms-toggle').click();
|
||||
cy.getByTestId(proposalTermsSection).within(() => {
|
||||
cy.getByTestId(proposalJsonToggle).click();
|
||||
cy.getByTestId(proposalJsonSection).within(() => {
|
||||
cy.contains('USDT Coin').should('be.visible');
|
||||
cy.contains('USDT').should('be.visible');
|
||||
});
|
||||
@@ -464,8 +493,8 @@ context(
|
||||
|
||||
it('Unable to submit new asset proposal with missing/invalid fields', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.getByTestId(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.getByTestId(inputError).should('have.length', 3);
|
||||
});
|
||||
|
||||
it('Able to submit update asset proposal using min deadline', function () {
|
||||
@@ -474,9 +503,9 @@ context(
|
||||
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(minVoteDeadline).click();
|
||||
cy.get(minEnactDeadline).click();
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(minVoteDeadline).click();
|
||||
cy.getByTestId(minEnactDeadline).click();
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -488,13 +517,16 @@ context(
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalType)
|
||||
cy.getByTestId(openProposals).within(() => {
|
||||
cy.getByTestId(proposalType)
|
||||
.contains('Update asset')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalDetails).should('contain.text', assetId.slice(0, 6)); // 3001-VOTE-029
|
||||
cy.getByTestId(proposalDetails).should(
|
||||
'contain.text',
|
||||
assetId.slice(0, 6)
|
||||
); // 3001-VOTE-029
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
});
|
||||
@@ -502,13 +534,11 @@ context(
|
||||
.invoke('text')
|
||||
.should('not.be.empty');
|
||||
// 3001-VOTE-030 3001-VOTE-031
|
||||
cy.getByTestId('proposal-terms-toggle').click();
|
||||
cy.getByTestId('proposal-terms').within(() => {
|
||||
getProposalInformationFromTable('assetId').should('have.text', assetId);
|
||||
getProposalInformationFromTable('lifetimeLimit').should(
|
||||
'have.text',
|
||||
'10'
|
||||
);
|
||||
cy.getByTestId(proposalJsonToggle).click();
|
||||
cy.getByTestId(proposalJsonSection).within(() => {
|
||||
cy.contains(assetId).should('be.visible');
|
||||
cy.contains('lifetimeLimit').should('be.visible');
|
||||
cy.contains('10').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -516,9 +546,9 @@ context(
|
||||
it('Able to submit update asset proposal using max deadline', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
enterUpdateAssetProposalDetails();
|
||||
cy.get(maxVoteDeadline).click();
|
||||
cy.get(maxEnactDeadline).click();
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(maxVoteDeadline).click();
|
||||
cy.getByTestId(maxEnactDeadline).click();
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -533,36 +563,36 @@ context(
|
||||
|
||||
it('Unable to submit edit asset proposal with missing/invalid fields', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
cy.get(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.get(inputError).should('have.length', 3);
|
||||
cy.getByTestId(proposalDownloadBtn).should('be.visible').click();
|
||||
cy.getByTestId(inputError).should('have.length', 3);
|
||||
});
|
||||
|
||||
it('Able to download and submit freeform proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
// 3008-PFRO-006
|
||||
cy.get(newProposalTitle)
|
||||
cy.getByTestId(newProposalTitle)
|
||||
.siblings()
|
||||
.should('contain.text', '(100 characters or less)'); // 3008-PFRO-007
|
||||
// 3008-PFRO-005
|
||||
cy.get(newProposalTitle).type('Test freeform proposal form');
|
||||
cy.getByTestId(newProposalTitle).type('Test freeform proposal form');
|
||||
// 3008-PFRO-009
|
||||
cy.get(newProposalDescription)
|
||||
cy.getByTestId(newProposalDescription)
|
||||
.siblings()
|
||||
.should('contain.text', '(20,000 characters or less)'); // 3008-PFRO-010
|
||||
// 3008-PFRO-008 3002-PROP-012 3002-PROP-016
|
||||
cy.get(newProposalDescription).type(
|
||||
cy.getByTestId(newProposalDescription).type(
|
||||
'E2E test for downloading freeform proposal'
|
||||
);
|
||||
// 3008-PFRO-012
|
||||
cy.get(minVoteDeadline).should('exist'); // 3002-PROP-008
|
||||
cy.get(maxVoteDeadline).should('exist');
|
||||
cy.getByTestId(minVoteDeadline).should('exist'); // 3002-PROP-008
|
||||
cy.getByTestId(maxVoteDeadline).should('exist');
|
||||
// 3008-PFRO-011
|
||||
cy.get(proposalVoteDeadline).clear().type('2');
|
||||
cy.getByTestId(proposalVoteDeadline).clear().type('2');
|
||||
// 3008-PFRO-013 3008-PFRO-014
|
||||
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
|
||||
// 3008-PFRO-015
|
||||
cy.log('Download updated proposal file');
|
||||
cy.get(proposalDownloadBtn)
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
@@ -596,15 +626,23 @@ context(
|
||||
}
|
||||
|
||||
function enterUpdateAssetProposalDetails() {
|
||||
cy.get(newProposalTitle).type('Test update asset proposal');
|
||||
cy.get(newProposalDescription).type('E2E test for proposals');
|
||||
cy.getByTestId(newProposalTitle).type('Test update asset proposal');
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/update-asset').then((newAssetProposal) => {
|
||||
const newAssetPayload = JSON.stringify(newAssetProposal);
|
||||
cy.get(newProposalTerms).type(newAssetPayload, {
|
||||
cy.getByTestId(newProposalTerms).type(newAssetPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getMarketProposalDetailsFromTable(heading: string) {
|
||||
return cy
|
||||
.getByTestId('key-value-table-row')
|
||||
.contains(heading)
|
||||
.parent()
|
||||
.siblings();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -21,15 +21,15 @@ import {
|
||||
} from '../../support/governance.functions';
|
||||
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
|
||||
|
||||
const proposalListItem = 'proposals-list-item';
|
||||
const openProposals = '[data-testid="open-proposals"]';
|
||||
const openProposals = 'open-proposals';
|
||||
const voteStatus = 'vote-status';
|
||||
const proposalType = 'proposal-type';
|
||||
const proposalStatus = 'proposal-status';
|
||||
const proposalClosingDate = '[data-testid="vote-details"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
const proposalClosingDate = 'vote-details';
|
||||
const viewProposalButton = 'view-proposal-btn';
|
||||
const voteBreakDownToggle = 'vote-breakdown-toggle';
|
||||
|
||||
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
@@ -62,13 +62,13 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
}
|
||||
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalClosingDate)
|
||||
cy.getByTestId(openProposals).within(() => {
|
||||
cy.getByTestId(proposalClosingDate)
|
||||
.first()
|
||||
.invoke('text')
|
||||
.should('match', /days|minutes/);
|
||||
cy.get(proposalClosingDate).should('contain.text', 'months');
|
||||
cy.get(proposalClosingDate).last().should('contain.text', 'year');
|
||||
cy.getByTestId(proposalClosingDate).should('contain.text', 'months');
|
||||
cy.getByTestId(proposalClosingDate).last().should('contain.text', 'year');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
const proposalTitle = generateFreeFormProposalTitle();
|
||||
|
||||
submitUniqueRawProposal({ proposalTitle: proposalTitle });
|
||||
cy.get('[data-testid="set-proposals-filter-visible"]').click();
|
||||
cy.get('[data-testid="proposal-filter-toggle"]').click();
|
||||
cy.get('[data-testid="filter-input"]').type(proposerId);
|
||||
// cy.get(`#${proposalId}`).should('contain', proposalId);
|
||||
cy.contains(proposalTitle).should('be.visible');
|
||||
@@ -106,7 +106,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
createRawProposal(this.minProposerBalance);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
cy.get(viewProposalButton).should('be.visible');
|
||||
cy.getByTestId(viewProposalButton).should('be.visible');
|
||||
cy.getByTestId(proposalType).should('have.text', 'Freeform');
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Open');
|
||||
});
|
||||
@@ -124,13 +124,13 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
'have.text',
|
||||
'Participation not reached'
|
||||
);
|
||||
cy.get(viewProposalButton).click();
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
voteForProposal('for');
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() => {
|
||||
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
|
||||
cy.get(viewProposalButton).click();
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId(voteBreakDownToggle).click();
|
||||
getProposalInformationFromTable('Token participation met')
|
||||
|
||||
@@ -14,11 +14,10 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
depositAsset,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
|
||||
const vegaWalletUnstakedBalance =
|
||||
'[data-testid="vega-wallet-balance-unstaked"]';
|
||||
const vegaWalletUnstakedBalance = 'vega-wallet-balance-unstaked';
|
||||
const rewardsTable = 'epoch-total-rewards-table';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const rewardsTimeOut = { timeout: 60000 };
|
||||
@@ -40,7 +39,7 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
cy.associateTokensToVegaWallet('6000');
|
||||
navigateTo(navigation.validators);
|
||||
cy.VegaWalletTopUpRewardsPool();
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
|
||||
cy.getByTestId(vegaWalletUnstakedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'6,000.0',
|
||||
txTimeout
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
verifyStakedBalance,
|
||||
verifyEthWalletTotalAssociatedBalance,
|
||||
verifyEthWalletAssociatedBalance,
|
||||
waitForSpinner,
|
||||
navigateTo,
|
||||
navigation,
|
||||
turnTelemetryOff,
|
||||
@@ -25,7 +24,7 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
const stakeValidatorListTotalStake = 'total-stake';
|
||||
const stakeValidatorListTotalShare = 'total-stake-share';
|
||||
const stakeValidatorListStakePercentage = 'stake-percentage';
|
||||
@@ -58,8 +57,7 @@ context(
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
// this is a workaround for #2422 which can be removed once issue is resolved
|
||||
cy.associateTokensToVegaWallet('4');
|
||||
cy.connectVegaWallet();
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
});
|
||||
|
||||
@@ -69,10 +67,9 @@ context(
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
turnTelemetryOff();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
ethereumWalletConnect();
|
||||
// Go to homepage to allow wallet teardown without epoch timer refreshing page
|
||||
navigateTo(navigation.home);
|
||||
vegaWalletTeardown();
|
||||
navigateTo(navigation.validators);
|
||||
}
|
||||
);
|
||||
@@ -130,6 +127,7 @@ context(
|
||||
cy.getByTestId('staked-by-user-tooltip')
|
||||
.first()
|
||||
.should('have.text', 'Staked by me: 2.00');
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-pending-stake').first().realHover();
|
||||
cy.getByTestId('pending-user-stake-tooltip')
|
||||
.first()
|
||||
@@ -400,6 +398,7 @@ context(
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
cy.reload();
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
stakingPageAssociateTokens('3');
|
||||
verifyUnstakedBalance(3.0);
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
@@ -505,11 +504,6 @@ context(
|
||||
);
|
||||
});
|
||||
|
||||
afterEach('Teardown Wallet', function () {
|
||||
navigateTo(navigation.home);
|
||||
vegaWalletTeardown();
|
||||
});
|
||||
|
||||
function verifyNextEpochValue(amount: number) {
|
||||
cy.getByTestId('stake-next-epoch', epochTimeout)
|
||||
.contains(amount, epochTimeout)
|
||||
|
||||
@@ -14,31 +14,31 @@ import {
|
||||
} from '../../support/staking.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
switchVegaWalletPubKey,
|
||||
vegaWalletAssociate,
|
||||
vegaWalletDisassociate,
|
||||
vegaWalletSetSpecifiedApprovalAmount,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
|
||||
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
|
||||
const vegaWalletUnstakedBalance =
|
||||
'[data-testid="vega-wallet-balance-unstaked"]';
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
||||
const ethWalletContainer = 'ethereum-wallet';
|
||||
const vegaWalletAssociatedBalance = 'currency-value';
|
||||
const vegaWalletUnstakedBalance = 'vega-wallet-balance-unstaked';
|
||||
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
|
||||
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
|
||||
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
|
||||
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
|
||||
const associateWalletRadioButton = 'associate-radio-wallet';
|
||||
const tokenAmountInputBox = 'token-amount-input';
|
||||
const tokenSubmitButton = 'token-input-submit-button';
|
||||
const ethWalletDissociateButton = '[href="/token/disassociate"]:visible';
|
||||
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
|
||||
const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
|
||||
const connectedVegaKey = '[data-testid="connected-vega-key"]';
|
||||
const associatedKey = '[data-testid="associated-key"]';
|
||||
const associatedAmount = '[data-testid="associated-amount"]';
|
||||
const associateCompleteText = '[data-testid="transaction-complete-body"]';
|
||||
const disassociationWarning = '[data-testid="disassociation-warning"]';
|
||||
const vestingContractSection = 'vega-in-vesting-contract';
|
||||
const vegaInWalletSection = 'vega-in-wallet';
|
||||
const connectedVegaKey = 'connected-vega-key';
|
||||
const associatedKey = 'associated-key';
|
||||
const associatedAmount = 'associated-amount';
|
||||
const associateCompleteText = 'transaction-complete-body';
|
||||
const disassociationWarning = 'disassociation-warning';
|
||||
const vegaWallet = 'aside [data-testid="vega-wallet"]';
|
||||
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
|
||||
context(
|
||||
'Token association flow - with eth and vega wallets connected',
|
||||
@@ -88,12 +88,15 @@ context(
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.getByTestId(vegaWalletUnstakedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -126,7 +129,7 @@ context(
|
||||
verifyEthWalletAssociatedBalance('1,001.00');
|
||||
verifyEthWalletTotalAssociatedBalance('7,001.00');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
@@ -136,14 +139,20 @@ context(
|
||||
it('Able to disassociate a partial amount of tokens currently associated', function () {
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageDisassociateTokens('1');
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
1.0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -153,21 +162,24 @@ context(
|
||||
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
cy.get(ethWalletDissociateButton).click();
|
||||
cy.get(disassociationWarning).should('contain', warningText);
|
||||
cy.getByTestId(disassociationWarning).should('contain', warningText);
|
||||
stakingPageDisassociateAllTokens();
|
||||
cy.get(ethWalletContainer)
|
||||
cy.getByTestId(ethWalletContainer)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(ethWalletContainer)
|
||||
cy.getByTestId(ethWalletContainer)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
@@ -175,7 +187,10 @@ context(
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
0.0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -197,9 +212,15 @@ context(
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.getByTestId(vegaWalletUnstakedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
stakingPageDisassociateTokens('1', {
|
||||
type: 'contract',
|
||||
skipConfirmation: true,
|
||||
@@ -220,45 +241,54 @@ context(
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
cy.getByTestId('epoch-countdown').should('be.visible');
|
||||
stakingPageAssociateTokens('37', { type: 'contract' });
|
||||
cy.get(vestingContractSection)
|
||||
cy.getByTestId(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
cy.getByTestId(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 37);
|
||||
cy.getByTestId(associatedAmount, txTimeout).should('contain', 37);
|
||||
});
|
||||
cy.get(vegaInWalletSection)
|
||||
cy.getByTestId(vegaInWalletSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
cy.getByTestId(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
cy.getByTestId(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
58
|
||||
);
|
||||
});
|
||||
stakingPageDisassociateTokens('6', { type: 'contract' });
|
||||
cy.get(vestingContractSection)
|
||||
cy.getByTestId(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 31);
|
||||
cy.getByTestId(associatedAmount, txTimeout).should('contain', 31);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
52
|
||||
);
|
||||
});
|
||||
navigateTo(navigation.validators);
|
||||
stakingPageDisassociateTokens('9', { type: 'wallet' });
|
||||
cy.get(vegaInWalletSection)
|
||||
cy.getByTestId(vegaInWalletSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 12);
|
||||
cy.getByTestId(associatedAmount, txTimeout).should('contain', 12);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
43
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -267,10 +297,10 @@ context(
|
||||
// 1004-ASSO-010
|
||||
// No warning visible as described in AC, but the button is disabled
|
||||
cy.get(ethWalletAssociateButton).click();
|
||||
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
|
||||
cy.get(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
|
||||
cy.get(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
|
||||
cy.get(tokenSubmitButton, txTimeout).should('be.disabled');
|
||||
cy.getByTestId(associateWalletRadioButton, { timeout: 30000 }).click();
|
||||
cy.getByTestId(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
|
||||
cy.getByTestId(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
|
||||
cy.getByTestId(tokenSubmitButton, txTimeout).should('be.disabled');
|
||||
});
|
||||
|
||||
// 1004-ASSO-004
|
||||
@@ -295,23 +325,25 @@ context(
|
||||
|
||||
it('Able to associate tokens to different public key of connected vega wallet', function () {
|
||||
cy.get(ethWalletAssociateButton).click();
|
||||
cy.get(associateWalletRadioButton).click();
|
||||
cy.get(connectedVegaKey).should(
|
||||
cy.getByTestId(associateWalletRadioButton).click();
|
||||
cy.getByTestId(connectedVegaKey).should(
|
||||
'have.text',
|
||||
Cypress.env('vegaWalletPublicKey')
|
||||
);
|
||||
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
cy.get(connectedVegaKey).should(
|
||||
switchVegaWalletPubKey();
|
||||
cy.getByTestId(connectedVegaKey).should(
|
||||
'have.text',
|
||||
Cypress.env('vegaWalletPublicKey2')
|
||||
);
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(associateCompleteText).should(
|
||||
cy.getByTestId(associateCompleteText).should(
|
||||
'have.text',
|
||||
`Vega key ${Cypress.env(
|
||||
'vegaWalletPublicKey2Short'
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { depositAsset } from '../../support/wallet-teardown.functions';
|
||||
import { depositAsset } from '../../support/wallet-functions';
|
||||
|
||||
const withdraw = 'withdraw';
|
||||
const withdrawalForm = 'withdraw-form';
|
||||
|
||||
@@ -10,9 +10,10 @@ import {
|
||||
} from '../../support/governance.functions';
|
||||
import { mockNetworkUpgradeProposal } from '../../support/proposal.functions';
|
||||
|
||||
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
|
||||
const proposalDocsLink = 'proposal-docs-link';
|
||||
const proposalDocumentationLink = 'proposal-documentation-link';
|
||||
const connectToVegaWalletButton = 'connect-to-vega-wallet-btn';
|
||||
const governanceDocsUrl = 'https://vega.xyz/governance';
|
||||
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
|
||||
context(
|
||||
'Governance Page - verify elements on page',
|
||||
@@ -41,7 +42,7 @@ context(
|
||||
|
||||
it('should be able to see a working link for - find out more about Vega governance', function () {
|
||||
// 3001-VOTE-001
|
||||
cy.get(proposalDocumentationLink)
|
||||
cy.getByTestId(proposalDocumentationLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Find out more about Vega governance')
|
||||
.and('have.attr', 'href')
|
||||
@@ -64,7 +65,7 @@ context(
|
||||
// 3007-PNE-021
|
||||
it('should have documentation links for network parameter proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
cy.getByTestId(proposalDocsLink)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/network-parameter-proposal');
|
||||
@@ -73,7 +74,7 @@ context(
|
||||
// 3003-PMAN-002 3003-PMAN-005
|
||||
it('should have documentation links for new market proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
cy.getByTestId(proposalDocsLink)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/new-market-proposal');
|
||||
@@ -82,7 +83,7 @@ context(
|
||||
// 3004-PMAC-005
|
||||
it('should have documentation links for update market proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
cy.getByTestId(proposalDocsLink)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/update-market-proposal');
|
||||
@@ -91,7 +92,7 @@ context(
|
||||
// 3005-PASN-002 005-PASN-005
|
||||
it('should have documentation links for new asset proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
cy.getByTestId(proposalDocsLink)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/new-asset-proposal');
|
||||
@@ -100,7 +101,7 @@ context(
|
||||
// 3006-PASC-002 3006-PASC-005
|
||||
it('should have documentation links for update asset proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
cy.getByTestId(proposalDocsLink)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/update-asset-proposal');
|
||||
@@ -109,7 +110,7 @@ context(
|
||||
// 3008-PFRO-003 3008-PFRO-017
|
||||
it('should have documentation links for freeform proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.FREEFORM);
|
||||
cy.getByTestId('proposal-docs-link')
|
||||
cy.getByTestId(proposalDocsLink)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/tutorials/proposals/freeform-proposal');
|
||||
@@ -117,7 +118,7 @@ context(
|
||||
|
||||
it('should be able to see a connect wallet button - if vega wallet disconnected and user is submitting new proposal', function () {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
cy.get(connectToVegaWalletButton)
|
||||
cy.getByTestId(connectToVegaWalletButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
});
|
||||
@@ -165,6 +166,7 @@ context(
|
||||
);
|
||||
});
|
||||
});
|
||||
cy.get('[data-testid="closed-proposals-toggle-networkUpgrades"]').click();
|
||||
cy.getByTestId('closed-proposals').within(() => {
|
||||
cy.getByTestId('protocol-upgrade-proposals-list-item').should(
|
||||
'have.length',
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
goToMakeNewProposal,
|
||||
governanceProposalType,
|
||||
} from '../../support/governance.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-functions';
|
||||
|
||||
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2');
|
||||
const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short');
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
} from '../../support/common.functions';
|
||||
import { waitForBeginningOfEpoch } from '../../support/staking.functions';
|
||||
|
||||
const viewToggle = '[data-testid="epoch-reward-view-toggle-total"]';
|
||||
const warning = '[data-testid="callout"]';
|
||||
const viewToggle = 'epoch-reward-view-toggle-total';
|
||||
const warning = 'callout';
|
||||
|
||||
context(
|
||||
'Rewards Page - verify elements on page',
|
||||
@@ -27,7 +27,7 @@ context(
|
||||
});
|
||||
|
||||
it('should have epoch warning', function () {
|
||||
cy.get(warning)
|
||||
cy.getByTestId(warning)
|
||||
.should('be.visible')
|
||||
.and(
|
||||
'have.text',
|
||||
@@ -36,7 +36,7 @@ context(
|
||||
});
|
||||
|
||||
it('should have toggle for seeing total vs individual rewards', function () {
|
||||
cy.get(viewToggle).should('be.visible');
|
||||
cy.getByTestId(viewToggle).should('be.visible');
|
||||
});
|
||||
|
||||
// Skipping due to bug #3471 causing flaky failuress
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { navigateTo, navigation } from '../../support/common.functions';
|
||||
|
||||
const tokenDetailsTable = '.token-details';
|
||||
const address = '[data-testid="token-address"]';
|
||||
const contract = '[data-testid="token-contract"]';
|
||||
const totalSupply = '[data-testid="total-supply"]';
|
||||
const circulatingSupply = '[data-testid="circulating-supply"]';
|
||||
const staked = '[data-testid="staked"]';
|
||||
const tranchesLink = '[data-testid="tranches-link"]';
|
||||
const redeemBtn = '[data-testid="check-vesting-page-btn"]';
|
||||
const getVegaWalletLink = '[data-testid="get-vega-wallet-link"]';
|
||||
const associateVegaLink =
|
||||
'[data-testid="associate-vega-tokens-link-on-homepage"]';
|
||||
const stakingBtn = '[data-testid="staking-button-on-homepage"]';
|
||||
const governanceBtn = '[data-testid="governance-button-on-homepage"]';
|
||||
const address = 'token-address';
|
||||
const contract = 'token-contract';
|
||||
const totalSupply = 'total-supply';
|
||||
const circulatingSupply = 'circulating-supply';
|
||||
const staked = 'staked';
|
||||
const tranchesLink = 'tranches-link';
|
||||
const redeemBtn = 'check-vesting-page-btn';
|
||||
const getVegaWalletLink = 'get-vega-wallet-link';
|
||||
const associateVegaLink = 'associate-vega-tokens-link-on-homepage';
|
||||
const stakingBtn = 'staking-button-on-homepage';
|
||||
const governanceBtn = 'governance-button-on-homepage';
|
||||
|
||||
const vegaTokenAddress = Cypress.env('vegaTokenAddress');
|
||||
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
|
||||
@@ -25,7 +24,7 @@ context('Verify elements on Token page', { tags: '@smoke' }, function () {
|
||||
describe('THE $VEGA TOKEN table', function () {
|
||||
it('should have TOKEN ADDRESS', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(address)
|
||||
cy.getByTestId(address)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('be.equal', vegaTokenAddress);
|
||||
@@ -34,7 +33,7 @@ context('Verify elements on Token page', { tags: '@smoke' }, function () {
|
||||
it('should have VESTING CONTRACT', function () {
|
||||
// 1004-ASSO-001
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(contract)
|
||||
cy.getByTestId(contract)
|
||||
.should('be.visible')
|
||||
.invoke('text')
|
||||
.should('be.equal', vegaTokenContractAddress);
|
||||
@@ -42,56 +41,56 @@ context('Verify elements on Token page', { tags: '@smoke' }, function () {
|
||||
});
|
||||
it('should have TOTAL SUPPLY', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(totalSupply).should('be.visible');
|
||||
cy.getByTestId(totalSupply).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have CIRCULATING SUPPLY', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(circulatingSupply).should('be.visible');
|
||||
cy.getByTestId(circulatingSupply).should('be.visible');
|
||||
});
|
||||
});
|
||||
it('should have STAKED $VEGA', function () {
|
||||
cy.get(tokenDetailsTable).within(() => {
|
||||
cy.get(staked).should('be.visible');
|
||||
cy.getByTestId(staked).should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('links and buttons', function () {
|
||||
it('should have TRANCHES link', function () {
|
||||
cy.get(tranchesLink)
|
||||
cy.getByTestId(tranchesLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/token/tranches');
|
||||
});
|
||||
it('should have REDEEM button', function () {
|
||||
cy.get(redeemBtn)
|
||||
cy.getByTestId(redeemBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/token/redeem');
|
||||
});
|
||||
it('should have GET VEGA WALLET link', function () {
|
||||
cy.get(getVegaWalletLink)
|
||||
cy.getByTestId(getVegaWalletLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', 'https://vega.xyz/wallet');
|
||||
});
|
||||
it('should have ASSOCIATE VEGA TOKENS link', function () {
|
||||
cy.get(associateVegaLink)
|
||||
cy.getByTestId(associateVegaLink)
|
||||
.should('be.visible')
|
||||
.and('have.attr', 'href')
|
||||
.and('equal', '/token/associate');
|
||||
});
|
||||
it('should have STAKING button', function () {
|
||||
cy.get(stakingBtn)
|
||||
cy.getByTestId(stakingBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
.and('equal', '/validators');
|
||||
});
|
||||
it('should have GOVERNANCE button', function () {
|
||||
cy.get(governanceBtn)
|
||||
cy.getByTestId(governanceBtn)
|
||||
.should('be.visible')
|
||||
.parent()
|
||||
.should('have.attr', 'href')
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/// <reference types="cypress" />
|
||||
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import {
|
||||
navigation,
|
||||
verifyPageHeader,
|
||||
@@ -9,29 +10,28 @@ import {
|
||||
clickOnValidatorFromList,
|
||||
waitForBeginningOfEpoch,
|
||||
} from '../../support/staking.functions';
|
||||
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
|
||||
|
||||
const guideLink = '[data-testid="staking-guide-link"]';
|
||||
const validatorTitle = '[data-testid="validator-node-title"]';
|
||||
const validatorId = '[data-testid="validator-id"]';
|
||||
const validatorPubKey = '[data-testid="validator-public-key"]';
|
||||
const ethAddressLink = '[data-testid="link"]';
|
||||
const validatorStatus = '[data-testid="validator-status"]';
|
||||
const totalStake = '[data-testid="total-stake"]';
|
||||
const pendingStake = '[data-testid="pending-stake"]';
|
||||
const stakedByOperator = '[data-testid="staked-by-operator"]';
|
||||
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
|
||||
const stakeShare = '[data-testid="stake-percentage"]';
|
||||
const stakedByOperatorToolTip = '[data-testid="staked-operator-tooltip"]';
|
||||
const stakedByDelegatesToolTip = '[data-testid="staked-delegates-tooltip"]';
|
||||
const totalStakedToolTip = '[data-testid="total-staked-tooltip"]';
|
||||
const unnormalisedVotingPowerToolTip =
|
||||
'[data-testid="unnormalised-voting-power-tooltip"]';
|
||||
const normalisedVotingPowerToolTip =
|
||||
'[data-testid="normalised-voting-power-tooltip"]';
|
||||
const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
|
||||
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
|
||||
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
|
||||
const epochCountDown = '[data-testid="epoch-countdown"]';
|
||||
const guideLink = 'staking-guide-link';
|
||||
const validatorTitle = 'validator-node-title';
|
||||
const validatorId = 'validator-id';
|
||||
const validatorPubKey = 'validator-public-key';
|
||||
const ethAddressLink = 'link';
|
||||
const validatorStatus = 'validator-status';
|
||||
const totalStake = 'total-stake';
|
||||
const pendingStake = 'pending-stake';
|
||||
const stakedByOperator = 'staked-by-operator';
|
||||
const stakedByDelegates = 'staked-by-delegates';
|
||||
const stakeShare = 'stake-percentage';
|
||||
const stakedByOperatorToolTip = 'staked-operator-tooltip';
|
||||
const stakedByDelegatesToolTip = 'staked-delegates-tooltip';
|
||||
const totalStakedToolTip = 'total-staked-tooltip';
|
||||
const unnormalisedVotingPowerToolTip = 'unnormalised-voting-power-tooltip';
|
||||
const normalisedVotingPowerToolTip = 'normalised-voting-power-tooltip';
|
||||
const performancePenaltyToolTip = 'performance-penalty-tooltip';
|
||||
const overstakedPenaltyToolTip = 'overstaked-penalty-tooltip';
|
||||
const multisigPenaltyToolTip = 'multisig-error-tooltip';
|
||||
const epochCountDown = 'epoch-countdown';
|
||||
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
|
||||
|
||||
context('Validators Page - verify elements on page', function () {
|
||||
@@ -50,7 +50,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
it('Should have Staking Guide link visible', function () {
|
||||
// 1002-STKE-003
|
||||
cy.get(guideLink)
|
||||
cy.getByTestId(guideLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Read more about staking on Vega')
|
||||
.and(
|
||||
@@ -93,13 +93,13 @@ context('Validators Page - verify elements on page', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-stake').first().realHover();
|
||||
|
||||
cy.get(stakedByOperatorToolTip)
|
||||
cy.getByTestId(stakedByOperatorToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by operator: 3,000.00');
|
||||
cy.get(stakedByDelegatesToolTip)
|
||||
cy.getByTestId(stakedByDelegatesToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Staked by delegates: 0.00');
|
||||
cy.get(totalStakedToolTip)
|
||||
cy.getByTestId(totalStakedToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total stake: 3,000.00');
|
||||
});
|
||||
@@ -116,10 +116,10 @@ context('Validators Page - verify elements on page', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('normalised-voting-power').first().realHover();
|
||||
|
||||
cy.get(unnormalisedVotingPowerToolTip)
|
||||
cy.getByTestId(unnormalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Unnormalised voting power: 20.00%');
|
||||
cy.get(normalisedVotingPowerToolTip)
|
||||
cy.getByTestId(normalisedVotingPowerToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Normalised voting power: 50.00%');
|
||||
});
|
||||
@@ -137,15 +137,12 @@ context('Validators Page - verify elements on page', function () {
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-penalty').realHover();
|
||||
|
||||
cy.get(performancePenaltyToolTip)
|
||||
cy.getByTestId(performancePenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Performance penalty: 0.00%');
|
||||
cy.get(overstakedPenaltyToolTip)
|
||||
cy.getByTestId(overstakedPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
|
||||
cy.get(totalPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Total penalties: 60.00%');
|
||||
});
|
||||
|
||||
it('Should be able to see validator pending stake', function () {
|
||||
@@ -155,6 +152,22 @@ context('Validators Page - verify elements on page', function () {
|
||||
cy.wrap($pendingStake).should('contain.text', '0.00');
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to see multisig error', function () {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'PreviousEpoch', previousEpochData);
|
||||
});
|
||||
waitForBeginningOfEpoch();
|
||||
cy.getByTestId('total-penalty').first().realHover();
|
||||
cy.getByTestId(multisigPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Multisig penalty: 100%');
|
||||
|
||||
cy.getByTestId('total-penalty').eq(1).realHover();
|
||||
cy.getByTestId(multisigPenaltyToolTip)
|
||||
.invoke('text')
|
||||
.should('contain', 'Multisig penalty: 100%');
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -170,53 +183,59 @@ context('Validators Page - verify elements on page', function () {
|
||||
|
||||
// 1002-STKE-006
|
||||
it('Should be able to see validator name', function () {
|
||||
cy.get(validatorTitle).should('not.be.empty');
|
||||
cy.getByTestId(validatorTitle).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 1002-STKE-007
|
||||
it('Should be able to see validator id', function () {
|
||||
cy.get(validatorId).should('not.be.empty');
|
||||
cy.getByTestId(validatorId).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 1002-STKE-008
|
||||
it('Should be able to see validator public key', function () {
|
||||
cy.get(validatorPubKey).should('not.be.empty');
|
||||
cy.getByTestId(validatorPubKey).should('not.be.empty');
|
||||
});
|
||||
|
||||
// 1002-STKE-010
|
||||
it('Should be able to see Ethereum address', function () {
|
||||
cy.get(ethAddressLink).should('not.be.empty').and('have.attr', 'href');
|
||||
cy.getByTestId(ethAddressLink)
|
||||
.should('not.be.empty')
|
||||
.and('have.attr', 'href');
|
||||
});
|
||||
// TODO validators missing url for more information about them 1002-STKE-09
|
||||
|
||||
it('Should be able to see validator status', function () {
|
||||
cy.get(validatorStatus).should('have.text', 'Consensus');
|
||||
cy.getByTestId(validatorStatus).should('have.text', 'Consensus');
|
||||
});
|
||||
|
||||
// 1002-STKE-012
|
||||
it('Should be able to see total stake', function () {
|
||||
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
|
||||
cy.getByTestId(totalStake)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see pending stake', function () {
|
||||
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
|
||||
cy.getByTestId(pendingStake)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see staked by operator', function () {
|
||||
cy.get(stakedByOperator)
|
||||
cy.getByTestId(stakedByOperator)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
it('Should be able to see staked by delegates', function () {
|
||||
cy.get(stakedByDelegates)
|
||||
cy.getByTestId(stakedByDelegates)
|
||||
.invoke('text')
|
||||
.should('match', stakeNumberRegex);
|
||||
});
|
||||
|
||||
// 1002-STKE-051
|
||||
it('Should be able to see stake share in percentage', function () {
|
||||
cy.get(stakeShare)
|
||||
cy.getByTestId(stakeShare)
|
||||
.invoke('text')
|
||||
.then(($stakePercentage) => {
|
||||
// The pattern must start at a word boundary (\b).
|
||||
@@ -242,7 +261,7 @@ context('Validators Page - verify elements on page', function () {
|
||||
const epochTitle = 'h3';
|
||||
const nextEpochInfo = 'p';
|
||||
|
||||
cy.get(epochCountDown).within(() => {
|
||||
cy.getByTestId(epochCountDown).within(() => {
|
||||
cy.get(epochTitle).should('not.be.empty');
|
||||
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
|
||||
const connectButton = '[data-testid="connect-to-eth-btn"]';
|
||||
const connectButton = 'connect-to-eth-btn';
|
||||
const lockedTokensInVestingContract = '6,499,972.30';
|
||||
|
||||
context(
|
||||
@@ -29,7 +29,7 @@ context(
|
||||
|
||||
// 1005-VEST-018
|
||||
it('should have connect Eth wallet button', function () {
|
||||
cy.get(connectButton)
|
||||
cy.getByTestId(connectButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Ethereum wallet');
|
||||
});
|
||||
|
||||
@@ -5,11 +5,11 @@ const walletContainer = 'aside [data-testid="ethereum-wallet"]';
|
||||
const walletHeader = '[data-testid="wallet-header"] h1';
|
||||
const connectToEthButton =
|
||||
'[data-testid="connect-to-eth-wallet-button"]:visible';
|
||||
const connectorList = '[data-testid="web3-connector-list"]';
|
||||
const connectorList = 'web3-connector-list';
|
||||
const associate = '[href="/token/associate"]';
|
||||
const disassociate = '[href="/token/disassociate"]';
|
||||
const disconnect = '[data-testid="disconnect-from-eth-wallet-button"]';
|
||||
const accountNo = '[data-testid="ethereum-account-truncated"]';
|
||||
const disconnect = 'disconnect-from-eth-wallet-button';
|
||||
const accountNo = 'ethereum-account-truncated';
|
||||
const currencyTitle = '[data-testid="currency-title"]:visible';
|
||||
const currencyValue = '[data-testid="currency-value"]:visible';
|
||||
const vegaInVesting = '[data-testid="vega-in-vesting-contract"]:visible';
|
||||
@@ -18,8 +18,8 @@ const progressBar = '[data-testid="progress-bar"]:visible';
|
||||
const currencyLocked = '[data-testid="currency-locked"]:visible';
|
||||
const currencyUnlocked = '[data-testid="currency-unlocked"]:visible';
|
||||
const dialog = '[role="dialog"]:visible';
|
||||
const dialogHeader = '[data-testid="dialog-title"]';
|
||||
const dialogCloseBtn = '[data-testid="dialog-close"]';
|
||||
const dialogHeader = 'dialog-title';
|
||||
const dialogCloseBtn = 'dialog-close';
|
||||
|
||||
context(
|
||||
'Ethereum Wallet - verify elements on widget',
|
||||
@@ -59,7 +59,7 @@ context(
|
||||
|
||||
it('should have Connect Ethereum header visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogHeader)
|
||||
cy.getByTestId(dialogHeader)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect to your Ethereum wallet');
|
||||
});
|
||||
@@ -73,7 +73,7 @@ context(
|
||||
'WalletConnect',
|
||||
'WalletConnect Legacy',
|
||||
];
|
||||
cy.get(connectorList).within(() => {
|
||||
cy.getByTestId(connectorList).within(() => {
|
||||
cy.get('button').each(($btn, i) => {
|
||||
cy.wrap($btn).should('be.visible').and('have.text', connectList[i]);
|
||||
});
|
||||
@@ -83,7 +83,7 @@ context(
|
||||
after('close popup', function () {
|
||||
cy.get(dialog)
|
||||
.within(() => {
|
||||
cy.get(dialogCloseBtn).click();
|
||||
cy.getByTestId(dialogCloseBtn).click();
|
||||
})
|
||||
.should('not.exist');
|
||||
});
|
||||
@@ -106,7 +106,7 @@ context(
|
||||
// 0004-EWAL-005
|
||||
it('should have account number visible', function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(accountNo)
|
||||
cy.getByTestId(accountNo)
|
||||
.should('be.visible')
|
||||
.and('have.text', Cypress.env('ethWalletPublicKeyTruncated'));
|
||||
});
|
||||
@@ -129,7 +129,7 @@ context(
|
||||
// 0004-EWAL-007
|
||||
it('should have Disconnect button visible', function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(disconnect)
|
||||
cy.getByTestId(disconnect)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Disconnect');
|
||||
});
|
||||
|
||||
@@ -1,32 +1,34 @@
|
||||
import { truncateByChars } from '@vegaprotocol/utils';
|
||||
import { waitForSpinner } from '../../support/common.functions';
|
||||
import { vegaWalletTeardown } from '../../support/wallet-teardown.functions';
|
||||
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
|
||||
import {
|
||||
vegaWalletFaucetAssetsWithoutCheck,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-functions';
|
||||
|
||||
const walletContainer = 'aside [data-testid="vega-wallet"]';
|
||||
const walletHeader = '[data-testid="wallet-header"] h1';
|
||||
const connectButton = '[data-testid="connect-vega-wallet"]';
|
||||
const getVegaLink = '[data-testid="link"]';
|
||||
const connectButton = 'connect-vega-wallet';
|
||||
const getVegaLink = 'link';
|
||||
const dialog = '[role="dialog"]:visible';
|
||||
const dialogHeader = '[data-testid="dialog-title"]';
|
||||
const walletDialogHeader = '[data-testid="wallet-dialog-title"]';
|
||||
const connectorsList = '[data-testid="connectors-list"]';
|
||||
const dialogCloseBtn = '[data-testid="dialog-close"]';
|
||||
const restConnectorForm = '[data-testid="rest-connector-form"]';
|
||||
const dialogHeader = 'dialog-title';
|
||||
const walletDialogHeader = 'wallet-dialog-title';
|
||||
const connectorsList = 'connectors-list';
|
||||
const dialogCloseBtn = 'dialog-close';
|
||||
const restConnectorForm = 'rest-connector-form';
|
||||
const restWallet = '#wallet';
|
||||
const restPassphrase = '#passphrase';
|
||||
const restConnectBtn = '[type="submit"]';
|
||||
const accountNo = '[data-testid="vega-account-truncated"]';
|
||||
const currencyTitle = '[data-testid="currency-title"]';
|
||||
const currencyValue = '[data-testid="currency-value"]';
|
||||
const accountNo = 'vega-account-truncated';
|
||||
const currencyTitle = 'currency-title';
|
||||
const currencyValue = 'currency-value';
|
||||
const vegaUnstaked = '[data-testid="vega-wallet-balance-unstaked"] .text-right';
|
||||
const governanceBtn = '[href="/proposals"]';
|
||||
const stakingBtn = '[href="/validators"]';
|
||||
const manageLink = '[data-testid="manage-vega-wallet"]';
|
||||
const dialogVegaKey = '[data-testid="vega-public-key-full"]';
|
||||
const dialogDisconnectBtn = '[data-testid="disconnect"]';
|
||||
const copyPublicKeyBtn = '[data-testid="copy-vega-public-key"]';
|
||||
const vegaWalletCurrencyTitle = '[data-testid="currency-title"]';
|
||||
const manageLink = 'manage-vega-wallet';
|
||||
const dialogVegaKey = 'vega-public-key-full';
|
||||
const dialogDisconnectBtn = 'disconnect';
|
||||
const copyPublicKeyBtn = 'copy-vega-public-key';
|
||||
const vegaWalletCurrencyTitle = 'currency-title';
|
||||
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
|
||||
const txTimeout = Cypress.env('txTimeout');
|
||||
|
||||
@@ -45,10 +47,10 @@ context(
|
||||
cy.get(walletHeader)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Vega Wallet');
|
||||
cy.get(connectButton)
|
||||
cy.getByTestId(connectButton)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet to use associated $VEGA');
|
||||
cy.get(getVegaLink)
|
||||
cy.getByTestId(getVegaLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Get a Vega wallet')
|
||||
.and('have.attr', 'href', 'https://vega.xyz/wallet');
|
||||
@@ -59,20 +61,20 @@ context(
|
||||
describe('when connect button clicked', () => {
|
||||
before('click connect vega wallet button', () => {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(connectButton).click();
|
||||
cy.getByTestId(connectButton).click();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have Connect Vega header visible', () => {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(walletDialogHeader)
|
||||
cy.getByTestId(walletDialogHeader)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have jsonRpc and hosted connection options visible on list', function () {
|
||||
cy.get(connectorsList).within(() => {
|
||||
cy.getByTestId(connectorsList).within(() => {
|
||||
cy.getByTestId('connector-jsonRpc')
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
@@ -84,33 +86,33 @@ context(
|
||||
|
||||
it('should have close button visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogCloseBtn).should('be.visible');
|
||||
cy.getByTestId(dialogCloseBtn).should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when rest connector form opened', function () {
|
||||
before('click hosted wallet app button', function () {
|
||||
cy.get(connectorsList).within(() => {
|
||||
cy.getByTestId(connectorsList).within(() => {
|
||||
cy.getByTestId('connector-hosted').click();
|
||||
});
|
||||
});
|
||||
|
||||
// 0002-WCON-002
|
||||
it('should have wallet field visible', function () {
|
||||
cy.get(restConnectorForm).within(() => {
|
||||
cy.getByTestId(restConnectorForm).within(() => {
|
||||
cy.get(restWallet).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have password field visible', function () {
|
||||
cy.get(restConnectorForm).within(() => {
|
||||
cy.getByTestId(restConnectorForm).within(() => {
|
||||
cy.get(restPassphrase).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have connect button visible', function () {
|
||||
cy.get(restConnectorForm).within(() => {
|
||||
cy.getByTestId(restConnectorForm).within(() => {
|
||||
cy.get(restConnectBtn)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect');
|
||||
@@ -119,12 +121,12 @@ context(
|
||||
|
||||
it('should have close button visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogCloseBtn).should('be.visible');
|
||||
cy.getByTestId(dialogCloseBtn).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
after('close dialog', function () {
|
||||
cy.get(dialogCloseBtn).click().should('not.exist');
|
||||
cy.getByTestId(dialogCloseBtn).click().should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -150,7 +152,7 @@ context(
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(accountNo)
|
||||
cy.getByTestId(accountNo)
|
||||
.should('be.visible')
|
||||
.and('have.text', Cypress.env('vegaWalletPublicKeyShort'));
|
||||
});
|
||||
@@ -159,7 +161,7 @@ context(
|
||||
|
||||
it('should have Vega Associated currency title visible', function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(currencyTitle)
|
||||
cy.getByTestId(currencyTitle)
|
||||
.should('be.visible')
|
||||
.and('contain.text', `VEGAAssociated`);
|
||||
});
|
||||
@@ -170,7 +172,7 @@ context(
|
||||
{ tags: '@smoke' },
|
||||
function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(currencyValue)
|
||||
cy.getByTestId(currencyValue)
|
||||
.should('be.visible')
|
||||
.and('contain.text', `0.00`);
|
||||
});
|
||||
@@ -202,21 +204,23 @@ context(
|
||||
|
||||
it('should have Manage link visible', function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(manageLink).should('be.visible').and('have.text', 'Manage');
|
||||
cy.getByTestId(manageLink)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Manage');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when Manage dialog opened', function () {
|
||||
before('click Manage link', function () {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(manageLink).click();
|
||||
cy.getByTestId(manageLink).click();
|
||||
});
|
||||
});
|
||||
|
||||
// 0002-WCON-025, 0002-WCON-026
|
||||
it('should have SELECT A VEGA KEY dialog title visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogHeader)
|
||||
cy.getByTestId(dialogHeader)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'SELECT A VEGA KEY');
|
||||
});
|
||||
@@ -236,7 +240,7 @@ context(
|
||||
'contain.text',
|
||||
truncatedPubKey1
|
||||
);
|
||||
cy.get(dialogVegaKey)
|
||||
cy.getByTestId(dialogVegaKey)
|
||||
.should('be.visible')
|
||||
.and('contain.text', truncatedPubKey1)
|
||||
.and('contain.text', truncatedPubKey2);
|
||||
@@ -246,7 +250,7 @@ context(
|
||||
// 0002-WCON-029
|
||||
it('should have copy public key button visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(copyPublicKeyBtn)
|
||||
cy.getByTestId(copyPublicKeyBtn)
|
||||
.should('be.visible')
|
||||
.and('contain.text', 'Copy');
|
||||
});
|
||||
@@ -254,13 +258,13 @@ context(
|
||||
|
||||
it('should have close button visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogCloseBtn).should('be.visible');
|
||||
cy.getByTestId(dialogCloseBtn).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have vega Disconnect all keys button visible', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogDisconnectBtn)
|
||||
cy.getByTestId(dialogDisconnectBtn)
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Disconnect all keys');
|
||||
});
|
||||
@@ -269,10 +273,10 @@ context(
|
||||
// 0002-WCON-022
|
||||
it('should be able to disconnect all keys', function () {
|
||||
cy.get(dialog).within(() => {
|
||||
cy.get(dialogDisconnectBtn).click();
|
||||
cy.getByTestId(dialogDisconnectBtn).click();
|
||||
});
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(connectButton).should('be.visible'); // 0002-WCON-023
|
||||
cy.getByTestId(connectButton).should('be.visible'); // 0002-WCON-023
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -285,28 +289,28 @@ context(
|
||||
name: 'USDC (fake)',
|
||||
symbol: 'fUSDC',
|
||||
amount: '1000000',
|
||||
expectedAmount: '10.00',
|
||||
expectedAmount: 10.0,
|
||||
},
|
||||
{
|
||||
id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665',
|
||||
name: 'DAI (fake)',
|
||||
symbol: 'fDAI',
|
||||
amount: '200000',
|
||||
expectedAmount: '2.00',
|
||||
expectedAmount: 2.0,
|
||||
},
|
||||
{
|
||||
id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
|
||||
name: 'BTC (fake)',
|
||||
symbol: 'fBTC',
|
||||
amount: '600000',
|
||||
expectedAmount: '6.00',
|
||||
expectedAmount: 6.0,
|
||||
},
|
||||
{
|
||||
id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567',
|
||||
name: 'EURO (fake)',
|
||||
symbol: 'fEURO',
|
||||
amount: '800000',
|
||||
expectedAmount: '8.00',
|
||||
expectedAmount: 8.0,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -328,20 +332,20 @@ context(
|
||||
for (const { name, symbol, expectedAmount } of assets) {
|
||||
it(`should see ${name} within vega wallet`, () => {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name, txTimeout)
|
||||
.should('be.visible');
|
||||
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.siblings()
|
||||
.invoke('text')
|
||||
.should('have.length.at.least', 4)
|
||||
.then(parseFloat)
|
||||
.should('be.gte', parseFloat(expectedAmount));
|
||||
.then((elementAmount) => {
|
||||
const displayedAmount = parseFloat(elementAmount.text());
|
||||
expect(displayedAmount).be.gte(expectedAmount);
|
||||
});
|
||||
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.contains(symbol);
|
||||
|
||||
@@ -5,8 +5,6 @@ import {
|
||||
verifyTabHighlighted,
|
||||
} from '../../support/common.functions';
|
||||
|
||||
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
|
||||
|
||||
context(
|
||||
'Withdraw Page - verify elements on page',
|
||||
{ tags: '@smoke' },
|
||||
@@ -26,7 +24,7 @@ context(
|
||||
});
|
||||
|
||||
it('should have connect Vega wallet button', function () {
|
||||
cy.get(connectToVegaBtn)
|
||||
cy.getByTestId('connect-to-vega-wallet-btn')
|
||||
.should('be.visible')
|
||||
.and('have.text', 'Connect Vega wallet');
|
||||
});
|
||||
|
||||
@@ -37,7 +37,7 @@ export function navigateTo(page: navigation) {
|
||||
});
|
||||
} else {
|
||||
return cy.get(navigation.section, { timeout: 10000 }).within(() => {
|
||||
cy.get(page).eq(0).click();
|
||||
cy.get(page).eq(0).click({ force: true });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,8 +5,7 @@ import './common.functions.ts';
|
||||
import './staking.functions.ts';
|
||||
import './governance.functions.ts';
|
||||
import './wallet-eth.functions.ts';
|
||||
import './wallet-teardown.functions.ts';
|
||||
import './wallet-vega.functions.ts';
|
||||
import './wallet-functions.ts';
|
||||
import './proposal.functions.ts';
|
||||
import 'cypress-mochawesome-reporter/register';
|
||||
import registerCypressGrep from '@cypress/grep';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { closeDialog } from './common.functions';
|
||||
import { vegaWalletTeardown } from './wallet-teardown.functions';
|
||||
import { vegaWalletTeardown } from './wallet-functions';
|
||||
|
||||
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
|
||||
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
|
||||
|
||||
+27
@@ -139,6 +139,9 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
$associatedAmount
|
||||
);
|
||||
});
|
||||
// Wait needed to allow Eth transaction to complete - otherwise could result in nonce error
|
||||
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||
cy.wait(2000);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -173,3 +176,27 @@ export async function vegaWalletDisassociate(amount: string) {
|
||||
amount = amount + '0'.repeat(18);
|
||||
stakingBridgeContract.remove_stake(amount, vegaWalletPubKey);
|
||||
}
|
||||
|
||||
export function vegaWalletFaucetAssetsWithoutCheck(
|
||||
asset: string,
|
||||
amount: string,
|
||||
vegaWalletPublicKey: string
|
||||
) {
|
||||
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
|
||||
cy.exec(
|
||||
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
|
||||
)
|
||||
.its('stdout')
|
||||
.then((response) => {
|
||||
assert.include(
|
||||
response,
|
||||
`"success":true`,
|
||||
'Ensuring curl command was successfully undertaken'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export function switchVegaWalletPubKey() {
|
||||
cy.get('[data-testid="manage-vega-wallet"]:visible').click();
|
||||
cy.get('[data-testid="select-keypair-button"]').eq(0).click();
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
export function vegaWalletFaucetAssetsWithoutCheck(
|
||||
asset: string,
|
||||
amount: string,
|
||||
vegaWalletPublicKey: string
|
||||
) {
|
||||
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
|
||||
cy.exec(
|
||||
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
|
||||
)
|
||||
.its('stdout')
|
||||
.then((response) => {
|
||||
assert.include(
|
||||
response,
|
||||
`"success":true`,
|
||||
'Ensuring curl command was successfully undertaken'
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import { CollapsibleToggle } from './collapsible-toggle';
|
||||
|
||||
describe('CollapsibleToggle', () => {
|
||||
const testId = 'collapsible-toggle';
|
||||
|
||||
it('renders without crashing', () => {
|
||||
const mockSetToggleState = jest.fn();
|
||||
const { getByTestId, getByText } = render(
|
||||
<CollapsibleToggle
|
||||
toggleState={false}
|
||||
setToggleState={mockSetToggleState}
|
||||
dataTestId={testId}
|
||||
>
|
||||
<div>Test</div>
|
||||
</CollapsibleToggle>
|
||||
);
|
||||
|
||||
expect(getByTestId(testId)).toBeInTheDocument();
|
||||
expect(getByText('Test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('calls setToggleState with the opposite of current toggleState when clicked', () => {
|
||||
const mockSetToggleState = jest.fn();
|
||||
const { getByTestId } = render(
|
||||
<CollapsibleToggle
|
||||
toggleState={false}
|
||||
setToggleState={mockSetToggleState}
|
||||
dataTestId={testId}
|
||||
>
|
||||
<div>Test</div>
|
||||
</CollapsibleToggle>
|
||||
);
|
||||
|
||||
fireEvent.click(getByTestId(testId));
|
||||
|
||||
expect(mockSetToggleState).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('has the rotate-180 class if toggleState is true', () => {
|
||||
const mockSetToggleState = jest.fn();
|
||||
const { getByTestId } = render(
|
||||
<CollapsibleToggle
|
||||
toggleState={true}
|
||||
setToggleState={mockSetToggleState}
|
||||
dataTestId={testId}
|
||||
>
|
||||
<div>Test</div>
|
||||
</CollapsibleToggle>
|
||||
);
|
||||
|
||||
expect(getByTestId('toggle-icon-wrapper')).toHaveClass('rotate-180');
|
||||
});
|
||||
|
||||
it('does not have the rotate-180 class if toggleState is false', () => {
|
||||
const mockSetToggleState = jest.fn();
|
||||
const { getByTestId } = render(
|
||||
<CollapsibleToggle
|
||||
toggleState={false}
|
||||
setToggleState={mockSetToggleState}
|
||||
dataTestId={testId}
|
||||
>
|
||||
<div>Test</div>
|
||||
</CollapsibleToggle>
|
||||
);
|
||||
|
||||
expect(getByTestId('toggle-icon-wrapper')).not.toHaveClass('rotate-180');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import classnames from 'classnames';
|
||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import type { Dispatch, SetStateAction, ReactNode } from 'react';
|
||||
|
||||
interface CollapsibleToggleProps {
|
||||
toggleState: boolean;
|
||||
setToggleState: Dispatch<SetStateAction<boolean>>;
|
||||
children: ReactNode;
|
||||
dataTestId?: string;
|
||||
}
|
||||
|
||||
export const CollapsibleToggle = ({
|
||||
toggleState,
|
||||
setToggleState,
|
||||
dataTestId,
|
||||
children,
|
||||
}: CollapsibleToggleProps) => {
|
||||
const classes = classnames(
|
||||
'mb-4 transition-transform ease-in-out duration-300',
|
||||
{
|
||||
'rotate-180': toggleState,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<button
|
||||
onClick={() => setToggleState(!toggleState)}
|
||||
data-testid={dataTestId}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
{children}
|
||||
<div className={classes} data-testid="toggle-icon-wrapper">
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './collapsible-toggle';
|
||||
@@ -596,6 +596,8 @@
|
||||
"noPercentage": "No percentage",
|
||||
"proposalJson": "Full proposal JSON",
|
||||
"proposalDetails": "Proposal details",
|
||||
"marketSpecification": "Market specification",
|
||||
"viewMarketJson": "View market JSON",
|
||||
"proposalDescription": "Description",
|
||||
"currentlySetTo": "Currently expected to ",
|
||||
"currently": "currently",
|
||||
@@ -729,7 +731,11 @@
|
||||
"ThisWillSetValidationDeadlineTo": "This will set the validation deadline to",
|
||||
"Hours": "hours",
|
||||
"ThisWillAdd2MinutesToAllowTimeToConfirmInWallet": "Note: 2 minutes of extra time are added when you choose the minimum value. This gives you time to confirm the proposal in your wallet.",
|
||||
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "Proposal will fail if enactment is earlier than the voting deadline",
|
||||
"ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline": "The proposal will fail if enactment is earlier than the voting deadline",
|
||||
"ProposalWillFailIfEnactmentIsBelowTheMinimumDeadline": "The proposal will fail if enactment deadline is below the minimum",
|
||||
"ProposalWillFailIfVotingIsBelowTheMinimumDeadline": "The proposal will fail if voting deadline is below the minimum",
|
||||
"ProposalWillFailIfEnactmentIsAboveTheMaximumDeadline": "The proposal will fail if enactment deadline is above the maximum",
|
||||
"ProposalWillFailIfVotingIsAboveTheMaximumDeadline": "The proposal will fail if voting deadline is above the maximum",
|
||||
"SelectAMarketToChange": "Select a market to change",
|
||||
"MarketName": "Market name",
|
||||
"MarketCode": "Market code",
|
||||
@@ -780,6 +786,7 @@
|
||||
"performancePenalty": "Performance penalty",
|
||||
"overstaked": "Overstaked",
|
||||
"overstakedPenalty": "Overstaked penalty",
|
||||
"multisigPenalty": "Multisig penalty",
|
||||
"homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject. Network upgrades are proposed and approved by validators.",
|
||||
"homeProposalsButtonText": "Browse, vote, and propose",
|
||||
"homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.",
|
||||
@@ -826,5 +833,8 @@
|
||||
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
|
||||
"learnMore": "Learn more",
|
||||
"AllValidators": "All validators",
|
||||
"AllProposals": "All proposals"
|
||||
"AllProposals": "All proposals",
|
||||
"RejectedProposals": "Rejected proposals",
|
||||
"networkGovernance": "Network governance",
|
||||
"networkUpgrades": "Network upgrades"
|
||||
}
|
||||
|
||||
+8
-15
@@ -1,9 +1,9 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
|
||||
export const ProposalDescription = ({
|
||||
description,
|
||||
@@ -12,23 +12,16 @@ export const ProposalDescription = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDescription, setShowDescription] = useState(false);
|
||||
const showDescriptionIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDescription,
|
||||
});
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-description">
|
||||
<button
|
||||
onClick={() => setShowDescription(!showDescription)}
|
||||
data-testid="proposal-description-toggle"
|
||||
<CollapsibleToggle
|
||||
toggleState={showDescription}
|
||||
setToggleState={setShowDescription}
|
||||
dataTestId={'proposal-description-toggle'}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalDescription')} />
|
||||
<div className={showDescriptionIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<SubHeading title={t('proposalDescription')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDescription && (
|
||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
@@ -13,23 +13,16 @@ export const ProposalJson = ({
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-json">
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="proposal-json-toggle"
|
||||
<CollapsibleToggle
|
||||
toggleState={showDetails}
|
||||
setToggleState={setShowDetails}
|
||||
dataTestId="proposal-json-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('proposalJson')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<SubHeading title={t('proposalJson')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDetails && <SyntaxHighlighter data={proposal} />}
|
||||
</section>
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './proposal-market-data';
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
InstrumentInfoPanel,
|
||||
KeyDetailsInfoPanel,
|
||||
LiquidityMonitoringParametersInfoPanel,
|
||||
LiquidityPriceRangeInfoPanel,
|
||||
MetadataInfoPanel,
|
||||
OracleInfoPanel,
|
||||
PriceMonitoringBoundsInfoPanel,
|
||||
RiskFactorsInfoPanel,
|
||||
RiskModelInfoPanel,
|
||||
RiskParametersInfoPanel,
|
||||
SettlementAssetInfoPanel,
|
||||
} from '@vegaprotocol/markets';
|
||||
import {
|
||||
Accordion,
|
||||
AccordionItem,
|
||||
Button,
|
||||
CopyWithTooltip,
|
||||
Dialog,
|
||||
Icon,
|
||||
SyntaxHighlighter,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
||||
import { create } from 'zustand';
|
||||
|
||||
type MarketDataDialogState = {
|
||||
isOpen: boolean;
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
};
|
||||
|
||||
export const useMarketDataDialogStore = create<MarketDataDialogState>(
|
||||
(set) => ({
|
||||
isOpen: false,
|
||||
open: () => set({ isOpen: true }),
|
||||
close: () => set({ isOpen: false }),
|
||||
})
|
||||
);
|
||||
|
||||
export const ProposalMarketData = ({
|
||||
marketData,
|
||||
}: {
|
||||
marketData: MarketInfoWithData;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { isOpen, open, close } = useMarketDataDialogStore();
|
||||
const [showDetails, setShowDetails] = useState(false);
|
||||
|
||||
if (!marketData) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const settlementData = marketData.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
|
||||
const terminationData = marketData.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
|
||||
|
||||
const getSigners = (data: DataSourceDefinition) => {
|
||||
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||
const signers = data.sourceType.sourceType.signers || [];
|
||||
|
||||
return signers.map(({ signer }) => {
|
||||
return (
|
||||
(signer.__typename === 'ETHAddress' && signer.address) ||
|
||||
(signer.__typename === 'PubKey' && signer.key)
|
||||
);
|
||||
});
|
||||
}
|
||||
return [];
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="relative" data-testid="proposal-market-data">
|
||||
<CollapsibleToggle
|
||||
toggleState={showDetails}
|
||||
setToggleState={setShowDetails}
|
||||
dataTestId="proposal-market-data-toggle"
|
||||
>
|
||||
<SubHeading title={t('marketSpecification')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDetails && (
|
||||
<>
|
||||
<div className="float-right">
|
||||
<Button onClick={open} data-testid="view-market-json">
|
||||
{t('viewMarketJson')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-10">
|
||||
<Accordion>
|
||||
<AccordionItem
|
||||
itemId="key-details"
|
||||
title={t('Key details')}
|
||||
content={<KeyDetailsInfoPanel market={marketData} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="instrument"
|
||||
title={t('Instrument')}
|
||||
content={<InstrumentInfoPanel market={marketData} />}
|
||||
/>
|
||||
{isEqual(
|
||||
getSigners(settlementData),
|
||||
getSigners(terminationData)
|
||||
) ? (
|
||||
<AccordionItem
|
||||
itemId="oracles"
|
||||
title={t('Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<AccordionItem
|
||||
itemId="settlement-oracle"
|
||||
title={t('Settlement Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel
|
||||
market={marketData}
|
||||
type="settlementData"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
|
||||
<AccordionItem
|
||||
itemId="termination-oracle"
|
||||
title={t('Termination Oracle')}
|
||||
content={
|
||||
<OracleInfoPanel market={marketData} type="termination" />
|
||||
}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<AccordionItem
|
||||
itemId="settlement-asset"
|
||||
title={t('Settlement asset')}
|
||||
content={<SettlementAssetInfoPanel market={marketData} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="metadata"
|
||||
title={t('Metadata')}
|
||||
content={<MetadataInfoPanel market={marketData} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-model"
|
||||
title={t('Risk model')}
|
||||
content={<RiskModelInfoPanel market={marketData} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-parameters"
|
||||
title={t('Risk parameters')}
|
||||
content={<RiskParametersInfoPanel market={marketData} />}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="risk-factors"
|
||||
title={t('Risk factors')}
|
||||
content={<RiskFactorsInfoPanel market={marketData} />}
|
||||
/>
|
||||
{(
|
||||
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
||||
).map((_, triggerIndex) => (
|
||||
<AccordionItem
|
||||
itemId={`trigger-${triggerIndex}`}
|
||||
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
|
||||
content={
|
||||
<PriceMonitoringBoundsInfoPanel
|
||||
market={marketData}
|
||||
triggerIndex={triggerIndex}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<AccordionItem
|
||||
itemId="liqudity-monitoring-parameters"
|
||||
title={t('Liquidity monitoring parameters')}
|
||||
content={
|
||||
<LiquidityMonitoringParametersInfoPanel market={marketData} />
|
||||
}
|
||||
/>
|
||||
<AccordionItem
|
||||
itemId="liquidity-price-range"
|
||||
title={t('Liquidity price range')}
|
||||
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
|
||||
/>
|
||||
</Accordion>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Dialog
|
||||
title={marketData.tradableInstrument.instrument.code}
|
||||
open={isOpen}
|
||||
onChange={(isOpen) => (isOpen ? open() : close())}
|
||||
size="medium"
|
||||
dataTestId="market-json-dialog"
|
||||
>
|
||||
<CopyWithTooltip text={JSON.stringify(marketData)}>
|
||||
<button className="bg-vega-dark-100 rounded-sm py-2 px-3 mb-4 text-white">
|
||||
<span>
|
||||
<Icon name="duplicate" />
|
||||
</span>
|
||||
<span className="ml-2">Copy</span>
|
||||
</button>
|
||||
</CopyWithTooltip>
|
||||
<SyntaxHighlighter data={marketData} />
|
||||
</Dialog>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
+7
-16
@@ -1,4 +1,3 @@
|
||||
import classnames from 'classnames';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
@@ -6,13 +5,13 @@ import {
|
||||
KeyValueTableRow,
|
||||
Thumbs,
|
||||
RoundedWrapper,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber, formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { ProposalType } from '../proposal/proposal';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
@@ -57,23 +56,15 @@ export const ProposalVotesTable = ({
|
||||
? t('byTokenVote')
|
||||
: t('byLiquidityVote');
|
||||
|
||||
const showDetailsIconClasses = classnames('mb-4', {
|
||||
'rotate-180': showDetails,
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
data-testid="vote-breakdown-toggle"
|
||||
<CollapsibleToggle
|
||||
toggleState={showDetails}
|
||||
setToggleState={setShowDetails}
|
||||
dataTestId="vote-breakdown-toggle"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
<div className={showDetailsIconClasses}>
|
||||
<Icon name="chevron-down" size={8} />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<SubHeading title={t('voteBreakdown')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDetails && (
|
||||
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { Proposal } from './proposal';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||
@@ -29,9 +30,6 @@ jest.mock('../proposal-change-table', () => ({
|
||||
jest.mock('../proposal-json', () => ({
|
||||
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
||||
}));
|
||||
jest.mock('../proposal-terms/proposal-terms', () => ({
|
||||
ProposalTerms: () => <div data-testid="proposal-terms"></div>,
|
||||
}));
|
||||
jest.mock('../proposal-votes-table', () => ({
|
||||
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
||||
}));
|
||||
@@ -67,6 +65,17 @@ it('Renders with a link back to "all proposals"', async () => {
|
||||
expect(await screen.findByTestId('all-proposals-link')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Renders a rejected proposals with a link back to "rejected proposals"', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_REJECTED,
|
||||
});
|
||||
renderComponent(proposal);
|
||||
|
||||
expect(
|
||||
await screen.findByTestId('rejected-proposals-link')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders each section', async () => {
|
||||
const proposal = generateProposal();
|
||||
renderComponent(proposal);
|
||||
@@ -74,7 +83,6 @@ it('renders each section', async () => {
|
||||
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
||||
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalDescription } from '../proposal-description';
|
||||
import { ProposalChangeTable } from '../proposal-change-table';
|
||||
import { ProposalJson } from '../proposal-json';
|
||||
import { ProposalTerms } from '../proposal-terms';
|
||||
import { ProposalVotesTable } from '../proposal-votes-table';
|
||||
import { VoteDetails } from '../vote-details';
|
||||
import { ListAsset } from '../list-asset';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Routes from '../../../routes';
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { ProposalMarketData } from '../proposal-market-data';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
export enum ProposalType {
|
||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||
@@ -28,11 +29,16 @@ export enum ProposalType {
|
||||
}
|
||||
export interface ProposalProps {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
newMarketData?: MarketInfoWithData | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
restData: any;
|
||||
}
|
||||
|
||||
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||
export const Proposal = ({
|
||||
proposal,
|
||||
restData,
|
||||
newMarketData,
|
||||
}: ProposalProps) => {
|
||||
const { t } = useTranslation();
|
||||
const { params, loading, error } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_market_minVoterBalance,
|
||||
@@ -86,62 +92,77 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
||||
return (
|
||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
||||
<section data-testid="proposal">
|
||||
<div
|
||||
className="flex items-center gap-1"
|
||||
data-testid="all-proposals-link"
|
||||
>
|
||||
<div className="flex items-center gap-1">
|
||||
<Icon name={'chevron-left'} />
|
||||
<Link className="underline" to={Routes.PROPOSALS}>
|
||||
{t('AllProposals')}
|
||||
</Link>
|
||||
|
||||
{proposal.state === ProposalState.STATE_REJECTED ? (
|
||||
<div data-testid="rejected-proposals-link">
|
||||
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
|
||||
{t('RejectedProposals')}
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div data-testid="all-proposals-link">
|
||||
<Link className="underline" to={Routes.PROPOSALS}>
|
||||
{t('AllProposals')}
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||
|
||||
<div className="my-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
<div id="details">
|
||||
<div className="my-10">
|
||||
<ProposalChangeTable proposal={proposal} />
|
||||
</div>
|
||||
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||
proposal.id ? (
|
||||
<ListAsset
|
||||
assetId={proposal.id}
|
||||
withdrawalThreshold={proposal.terms.change.source.withdrawThreshold}
|
||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||
/>
|
||||
) : null}
|
||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||
proposal.id ? (
|
||||
<ListAsset
|
||||
assetId={proposal.id}
|
||||
withdrawalThreshold={
|
||||
proposal.terms.change.source.withdrawThreshold
|
||||
}
|
||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalDescription description={proposal.rationale.description} />
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<ProposalDescription description={proposal.rationale.description} />
|
||||
</div>
|
||||
|
||||
{proposal.terms.change.__typename !== 'NewMarket' &&
|
||||
proposal.terms.change.__typename !== 'UpdateMarket' &&
|
||||
proposal.terms.change.__typename !== 'NewFreeform' && (
|
||||
{newMarketData && (
|
||||
<div className="mb-4">
|
||||
<ProposalTerms data={proposal.terms} />
|
||||
<ProposalMarketData marketData={newMarketData} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mb-6">
|
||||
<ProposalJson proposal={restData?.data?.proposal} />
|
||||
<div className="mb-6">
|
||||
<ProposalJson proposal={restData?.data?.proposal} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-10">
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<VoteDetails
|
||||
<div id="voting">
|
||||
<div className="mb-10">
|
||||
<RoundedWrapper paddingBottom={true}>
|
||||
<VoteDetails
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={
|
||||
params?.spam_protection_voting_min_tokens
|
||||
}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalVotesTable
|
||||
proposal={proposal}
|
||||
proposalType={proposalType}
|
||||
minVoterBalance={minVoterBalance}
|
||||
spamProtectionMinTokens={
|
||||
params?.spam_protection_voting_min_tokens
|
||||
}
|
||||
/>
|
||||
</RoundedWrapper>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</AsyncRenderer>
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
import { render, fireEvent, screen } from '@testing-library/react';
|
||||
import { ProposalsListFilter } from './proposals-list-filter';
|
||||
|
||||
describe('ProposalsListFilter', () => {
|
||||
let setFilterString: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
setFilterString = jest.fn();
|
||||
render(
|
||||
<ProposalsListFilter filterString="" setFilterString={setFilterString} />
|
||||
);
|
||||
});
|
||||
|
||||
it('renders successfully', () => {
|
||||
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle the filter toggle click', () => {
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should handle input change', () => {
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'test' },
|
||||
});
|
||||
|
||||
expect(setFilterString).toHaveBeenCalledWith('test');
|
||||
});
|
||||
|
||||
// 'clear filter' tests are handled in the proposals-list.spec.tsx file
|
||||
// as it is responsible for the filter state
|
||||
});
|
||||
+25
-10
@@ -1,13 +1,16 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useState } from 'react';
|
||||
import { ButtonLink, FormGroup, Input } from '@vegaprotocol/ui-toolkit';
|
||||
import { FormGroup, Icon, Input } from '@vegaprotocol/ui-toolkit';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
interface ProposalsListFilterProps {
|
||||
filterString: string;
|
||||
setFilterString: Dispatch<SetStateAction<string>>;
|
||||
}
|
||||
|
||||
export const ProposalsListFilter = ({
|
||||
filterString,
|
||||
setFilterString,
|
||||
}: ProposalsListFilterProps) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -15,27 +18,39 @@ export const ProposalsListFilter = ({
|
||||
|
||||
return (
|
||||
<div data-testid="proposals-list-filter" className="mb-4">
|
||||
{!filterVisible && (
|
||||
<ButtonLink
|
||||
onClick={() => setFilterVisible(true)}
|
||||
data-testid="set-proposals-filter-visible"
|
||||
>
|
||||
{t('FilterProposals')}
|
||||
</ButtonLink>
|
||||
)}
|
||||
<CollapsibleToggle
|
||||
toggleState={filterVisible}
|
||||
setToggleState={setFilterVisible}
|
||||
dataTestId={'proposal-filter-toggle'}
|
||||
>
|
||||
<div className="text-xl mb-4">{t('FilterProposals')}</div>
|
||||
</CollapsibleToggle>
|
||||
|
||||
{filterVisible && (
|
||||
<div data-testid="open-proposals-list-filter">
|
||||
<div data-testid="proposals-list-filter-visible">
|
||||
<p>{t('FilterProposalsDescription')}</p>
|
||||
<FormGroup
|
||||
label="Filter text input"
|
||||
labelFor="filter-input"
|
||||
hideLabel={true}
|
||||
className="relative"
|
||||
>
|
||||
<Input
|
||||
value={filterString}
|
||||
data-testid="filter-input"
|
||||
id="filter-input"
|
||||
onChange={(e) => setFilterString(e.target.value)}
|
||||
className="pr-8"
|
||||
/>
|
||||
{filterString && filterString.length > 0 && (
|
||||
<button
|
||||
className="absolute top-2 right-2"
|
||||
onClick={() => setFilterString('')}
|
||||
data-testid="clear-filter"
|
||||
>
|
||||
<Icon name="cross" size={6} className="text-vega-light-200" />
|
||||
</button>
|
||||
)}
|
||||
</FormGroup>
|
||||
</div>
|
||||
)}
|
||||
|
||||
+110
-10
@@ -1,4 +1,7 @@
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import {
|
||||
generateProposal,
|
||||
generateProtocolUpgradeProposal,
|
||||
} from '../../test-helpers/generate-proposals';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
@@ -15,6 +18,7 @@ import {
|
||||
nextMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
const openProposalClosesNextMonth = generateProposal({
|
||||
id: 'proposal1',
|
||||
@@ -54,12 +58,22 @@ const failedProposalClosedLastMonth = generateProposal({
|
||||
},
|
||||
});
|
||||
|
||||
const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
|
||||
const closedProtocolUpgradeProposal = generateProtocolUpgradeProposal({
|
||||
upgradeBlockHeight: '1',
|
||||
});
|
||||
|
||||
const renderComponent = (
|
||||
proposals: ProposalQuery['proposal'][],
|
||||
protocolUpgradeProposals?: ProtocolUpgradeProposalFieldsFragment[]
|
||||
) => (
|
||||
<Router>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||
<ProposalsList proposals={proposals} protocolUpgradeProposals={[]} />
|
||||
<ProposalsList
|
||||
proposals={proposals}
|
||||
protocolUpgradeProposals={protocolUpgradeProposals || []}
|
||||
/>
|
||||
</VegaWalletContext.Provider>
|
||||
</AppStateProvider>
|
||||
</MockedProvider>
|
||||
@@ -143,17 +157,15 @@ describe('Proposals list', () => {
|
||||
render(
|
||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
||||
expect(
|
||||
screen.getByTestId('open-proposals-list-filter')
|
||||
).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Filters list by text - party id', () => {
|
||||
render(
|
||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'bvcx' },
|
||||
});
|
||||
@@ -166,7 +178,7 @@ describe('Proposals list', () => {
|
||||
render(
|
||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'proposal1' },
|
||||
});
|
||||
@@ -179,7 +191,7 @@ describe('Proposals list', () => {
|
||||
render(
|
||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'osal1' },
|
||||
});
|
||||
@@ -187,4 +199,92 @@ describe('Proposals list', () => {
|
||||
expect(container.querySelector('#proposal1')).toBeInTheDocument();
|
||||
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('When filter is used, clear button is visible', () => {
|
||||
render(
|
||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'test' },
|
||||
});
|
||||
expect(screen.getByTestId('clear-filter')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('When clear filter button is used, input is cleared', () => {
|
||||
render(
|
||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'test' },
|
||||
});
|
||||
fireEvent.click(screen.getByTestId('clear-filter'));
|
||||
expect((screen.getByTestId('filter-input') as HTMLInputElement).value).toBe(
|
||||
''
|
||||
);
|
||||
});
|
||||
|
||||
it('Displays a toggle for closed proposals if there are both closed governance proposals and closed upgrade proposals', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
[enactedProposalClosedLastWeek],
|
||||
[closedProtocolUpgradeProposal]
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('toggle-closed-proposals')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not display a toggle for closed proposals if there are only closed upgrade proposals', () => {
|
||||
render(renderComponent([], [closedProtocolUpgradeProposal]));
|
||||
expect(
|
||||
screen.queryByTestId('toggle-closed-proposals')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not display a toggle for closed proposals if there are only closed governance proposals', () => {
|
||||
render(renderComponent([enactedProposalClosedLastWeek]));
|
||||
expect(
|
||||
screen.queryByTestId('toggle-closed-proposals')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Does not display a toggle for closed proposals if the proposal filter is engaged', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
[enactedProposalClosedLastWeek],
|
||||
[closedProtocolUpgradeProposal]
|
||||
)
|
||||
);
|
||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||
target: { value: 'test' },
|
||||
});
|
||||
expect(
|
||||
screen.queryByTestId('toggle-closed-proposals')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Displays closed governance proposals by default due to default for the toggle', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
[enactedProposalClosedLastWeek],
|
||||
[closedProtocolUpgradeProposal]
|
||||
)
|
||||
);
|
||||
expect(
|
||||
screen.getByTestId('closed-governance-proposals')
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Displays closed upgrade proposals when the toggle is clicked', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
[enactedProposalClosedLastWeek],
|
||||
[closedProtocolUpgradeProposal]
|
||||
)
|
||||
);
|
||||
fireEvent.click(screen.getByText('Network upgrades'));
|
||||
expect(screen.getByTestId('closed-upgrade-proposals')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+106
-15
@@ -7,7 +7,12 @@ import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import Routes from '../../../routes';
|
||||
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
Button,
|
||||
Toggle,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
@@ -54,6 +59,11 @@ export const orderByUpgradeBlockHeight = (
|
||||
['desc', 'desc']
|
||||
);
|
||||
|
||||
enum ClosedProposalsViewOptions {
|
||||
NetworkGovernance = 'networkGovernance',
|
||||
NetworkUpgrades = 'networkUpgrades',
|
||||
}
|
||||
|
||||
export const ProposalsList = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
@@ -61,6 +71,10 @@ export const ProposalsList = ({
|
||||
}: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
const [closedProposalsView, setClosedProposalsView] =
|
||||
useState<ClosedProposalsViewOptions>(
|
||||
ClosedProposalsViewOptions.NetworkGovernance
|
||||
);
|
||||
|
||||
const sortedProposals: SortedProposalsProps = useMemo(() => {
|
||||
const initialSorting = proposals.reduce(
|
||||
@@ -109,7 +123,7 @@ export const ProposalsList = ({
|
||||
);
|
||||
return {
|
||||
open: orderByUpgradeBlockHeight(initialSorting.open),
|
||||
closed: orderByUpgradeBlockHeight(initialSorting.closed).reverse(),
|
||||
closed: orderByUpgradeBlockHeight(initialSorting.closed),
|
||||
};
|
||||
}, [protocolUpgradeProposals, lastBlockHeight]);
|
||||
|
||||
@@ -127,6 +141,7 @@ export const ProposalsList = ({
|
||||
marginBottom={false}
|
||||
title={t('pageTitleProposals')}
|
||||
/>
|
||||
|
||||
{DocsLinks && (
|
||||
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
||||
@@ -140,6 +155,7 @@ export const ProposalsList = ({
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="mb-8">
|
||||
{t(
|
||||
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network. Network upgrades are proposed and approved by validators.`
|
||||
@@ -152,11 +168,26 @@ export const ProposalsList = ({
|
||||
{t(`Find out more about Vega governance`)}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
|
||||
{proposals.length > 0 && (
|
||||
<ProposalsListFilter setFilterString={setFilterString} />
|
||||
<ProposalsListFilter
|
||||
filterString={filterString}
|
||||
setFilterString={(value) => {
|
||||
setFilterString(value);
|
||||
if (value.length > 0) {
|
||||
// If the filter is engaged, ensure the user is viewing governance proposals,
|
||||
// as network upgrades do not have IDs to filter by and will be excluded.
|
||||
setClosedProposalsView(
|
||||
ClosedProposalsViewOptions.NetworkGovernance
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
||||
<SubHeading title={t('openProposals')} />
|
||||
|
||||
{sortedProposals.open.length > 0 ||
|
||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||
<ul data-testid="open-proposals">
|
||||
@@ -166,6 +197,7 @@ export const ProposalsList = ({
|
||||
proposal={proposal}
|
||||
/>
|
||||
))}
|
||||
|
||||
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
||||
))}
|
||||
@@ -176,22 +208,81 @@ export const ProposalsList = ({
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
<section>
|
||||
|
||||
<section className="relative">
|
||||
<SubHeading title={t('closedProposals')} />
|
||||
{sortedProposals.closed.length > 0 ||
|
||||
sortedProtocolUpgradeProposals.closed.length > 0 ? (
|
||||
<ul data-testid="closed-proposals">
|
||||
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
||||
<ProtocolUpgradeProposalsListItem
|
||||
key={proposal.upgradeBlockHeight}
|
||||
proposal={proposal}
|
||||
/>
|
||||
))}
|
||||
<>
|
||||
{
|
||||
// We need both the closed proposals and closed protocol upgrade
|
||||
// proposals to be present for there to be a toggle. It also gets
|
||||
// hidden if the user has filtered the list, as the upgrade proposals
|
||||
// do not have the necessary fields for filtering.
|
||||
sortedProposals.closed.length > 0 &&
|
||||
sortedProtocolUpgradeProposals.closed.length > 0 &&
|
||||
filterString.length < 1 && (
|
||||
<div
|
||||
className="grid w-full justify-end xl:-mt-12 pb-6"
|
||||
data-testid="toggle-closed-proposals"
|
||||
>
|
||||
<div className="w-[440px]">
|
||||
<Toggle
|
||||
name="closed-proposals-toggle"
|
||||
toggles={[
|
||||
{
|
||||
label: t(
|
||||
ClosedProposalsViewOptions.NetworkGovernance
|
||||
),
|
||||
value: ClosedProposalsViewOptions.NetworkGovernance,
|
||||
},
|
||||
{
|
||||
label: t(
|
||||
ClosedProposalsViewOptions.NetworkUpgrades
|
||||
),
|
||||
value: ClosedProposalsViewOptions.NetworkUpgrades,
|
||||
},
|
||||
]}
|
||||
checkedValue={closedProposalsView}
|
||||
onChange={(e) =>
|
||||
setClosedProposalsView(
|
||||
e.target.value as ClosedProposalsViewOptions
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
{sortedProposals.closed.filter(filterPredicate).map((proposal) => (
|
||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
||||
))}
|
||||
</ul>
|
||||
<ul data-testid="closed-proposals">
|
||||
{closedProposalsView ===
|
||||
ClosedProposalsViewOptions.NetworkUpgrades && (
|
||||
<div data-testid="closed-upgrade-proposals">
|
||||
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
||||
<ProtocolUpgradeProposalsListItem
|
||||
key={proposal.upgradeBlockHeight}
|
||||
proposal={proposal}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{closedProposalsView ===
|
||||
ClosedProposalsViewOptions.NetworkGovernance && (
|
||||
<div data-testid="closed-governance-proposals">
|
||||
{sortedProposals.closed
|
||||
.filter(filterPredicate)
|
||||
.map((proposal) => (
|
||||
<ProposalsListItem
|
||||
key={proposal?.id}
|
||||
proposal={proposal}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
</>
|
||||
) : (
|
||||
<p className="mb-0" data-testid="no-closed-proposals">
|
||||
{t('noClosedProposals')}
|
||||
|
||||
+4
-1
@@ -23,7 +23,10 @@ export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('pageTitleRejectedProposals')} />
|
||||
<ProposalsListFilter setFilterString={setFilterString} />
|
||||
<ProposalsListFilter
|
||||
filterString={filterString}
|
||||
setFilterString={setFilterString}
|
||||
/>
|
||||
<section>
|
||||
{proposals.length > 0 ? (
|
||||
<ul data-testid="rejected-proposals">
|
||||
|
||||
+41
-1
@@ -224,7 +224,47 @@ describe('Proposal form vote, validation and enactment deadline', () => {
|
||||
expect(
|
||||
screen.getByTestId('enactment-before-voting-deadline')
|
||||
).toHaveTextContent(
|
||||
'Proposal will fail if enactment is earlier than the voting deadline'
|
||||
'The proposal will fail if enactment is earlier than the voting deadline'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the vote deadline is set earlier than the minimum allowed', () => {
|
||||
renderComponent();
|
||||
const voteDeadlineInput = screen.getByTestId('proposal-vote-deadline');
|
||||
fireEvent.change(voteDeadlineInput, { target: { value: 0.01 } });
|
||||
expect(screen.getByTestId('voting-less-than-min')).toHaveTextContent(
|
||||
'The proposal will fail if voting deadline is below the minimum'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the vote deadline is set later than the maximum allowed', () => {
|
||||
renderComponent();
|
||||
const voteDeadlineInput = screen.getByTestId('proposal-vote-deadline');
|
||||
fireEvent.change(voteDeadlineInput, { target: { value: 100000 } });
|
||||
expect(screen.getByTestId('voting-greater-than-max')).toHaveTextContent(
|
||||
'The proposal will fail if voting deadline is above the maximum'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the enactment deadline is set earlier than the minimum allowed', () => {
|
||||
renderComponent();
|
||||
const enactmentDeadlineInput = screen.getByTestId(
|
||||
'proposal-enactment-deadline'
|
||||
);
|
||||
fireEvent.change(enactmentDeadlineInput, { target: { value: 0.01 } });
|
||||
expect(screen.getByTestId('enactment-less-than-min')).toHaveTextContent(
|
||||
'The proposal will fail if enactment deadline is below the minimum'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays error text if the enactment deadline is set later than the maximum allowed', () => {
|
||||
renderComponent();
|
||||
const enactmentDeadlineInput = screen.getByTestId(
|
||||
'proposal-enactment-deadline'
|
||||
);
|
||||
fireEvent.change(enactmentDeadlineInput, { target: { value: 100000 } });
|
||||
expect(screen.getByTestId('enactment-greater-than-max')).toHaveTextContent(
|
||||
'The proposal will fail if enactment deadline is above the maximum'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+62
-22
@@ -220,21 +220,41 @@ const EnactmentForm = ({
|
||||
<span data-testid="enactment-date" className="pl-2">
|
||||
{getDateTimeFormat().format(deadlineDates.enactment)}
|
||||
</span>
|
||||
{deadlines.enactment === minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment && deadlines.enactment < deadlines.vote && (
|
||||
<span
|
||||
data-testid="enactment-before-voting-deadline"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline')}
|
||||
</span>
|
||||
{deadlines.enactment && (
|
||||
<>
|
||||
{deadlines.enactment === minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment < deadlines.vote && (
|
||||
<span
|
||||
data-testid="enactment-before-voting-deadline"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsEarlierThanVotingDeadline')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment < minEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-less-than-min"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsBelowTheMinimumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.enactment > maxEnactmentHours && (
|
||||
<span
|
||||
data-testid="enactment-greater-than-max"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfEnactmentIsAboveTheMaximumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
@@ -500,13 +520,33 @@ export function ProposalFormVoteAndEnactmentDeadline({
|
||||
<span data-testid="voting-date" className="pl-2">
|
||||
{getDateTimeFormat().format(deadlineDates.vote)}
|
||||
</span>
|
||||
{deadlines.vote === minVoteHours && (
|
||||
<span
|
||||
data-testid="voting-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
{deadlines.vote && (
|
||||
<>
|
||||
{deadlines.vote === minVoteHours && (
|
||||
<span
|
||||
data-testid="voting-2-mins-extra"
|
||||
className="block mt-4 font-light"
|
||||
>
|
||||
{t('ThisWillAdd2MinutesToAllowTimeToConfirmInWallet')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.vote < minVoteHours && (
|
||||
<span
|
||||
data-testid="voting-less-than-min"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfVotingIsBelowTheMinimumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
{deadlines.vote > maxVoteHours && (
|
||||
<span
|
||||
data-testid="voting-greater-than-max"
|
||||
className="block mt-4 text-vega-pink"
|
||||
>
|
||||
{t('ProposalWillFailIfVotingIsAboveTheMaximumDeadline')}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -7,6 +7,8 @@ import { ProposalNotFound } from '../components/proposal-not-found';
|
||||
import { useProposalQuery } from './__generated__/Proposal';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import { ENV } from '../../../config';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const params = useParams<{ proposalId: string }>();
|
||||
@@ -20,15 +22,36 @@ export const ProposalContainer = () => {
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const {
|
||||
data: newMarketData,
|
||||
loading: newMarketLoading,
|
||||
error: newMarketError,
|
||||
} = useDataProvider({
|
||||
dataProvider: marketInfoWithDataProvider,
|
||||
skipUpdates: true,
|
||||
variables: {
|
||||
marketId: data?.proposal?.id || '',
|
||||
skip: !data?.proposal?.id,
|
||||
},
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(refetch, 1000);
|
||||
const interval = setInterval(refetch, 2000);
|
||||
return () => clearInterval(interval);
|
||||
}, [refetch]);
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
||||
<AsyncRenderer
|
||||
loading={loading || newMarketLoading}
|
||||
error={error || newMarketError}
|
||||
data={newMarketData ? { newMarketData, data } : data}
|
||||
>
|
||||
{data?.proposal ? (
|
||||
<Proposal proposal={data.proposal} restData={restData} />
|
||||
<Proposal
|
||||
proposal={data.proposal}
|
||||
restData={restData}
|
||||
newMarketData={newMarketData}
|
||||
/>
|
||||
) : (
|
||||
<ProposalNotFound />
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import * as faker from 'faker';
|
||||
import isArray from 'lodash/isArray';
|
||||
@@ -6,6 +7,40 @@ import mergeWith from 'lodash/mergeWith';
|
||||
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
export function generateProtocolUpgradeProposal(
|
||||
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
|
||||
): ProtocolUpgradeProposalFieldsFragment {
|
||||
const defaultProposal: ProtocolUpgradeProposalFieldsFragment = {
|
||||
__typename: 'ProtocolUpgradeProposal',
|
||||
upgradeBlockHeight: '3917600',
|
||||
vegaReleaseTag: 'v0.71.6',
|
||||
approvers: [
|
||||
'0ac70c4ccc7f961614fe49b93e639ddf916269b7dcf8391db264cefeadf5a6b7',
|
||||
'63a1755006642bda9ab1bfa84660f944d30a113d1609590ca90c50b24aede472',
|
||||
'68ed0770fc3e67b74d09c05443243d27e29a8513dc0e8628beb98338cd509159',
|
||||
'a6e6f7daf8610f9242ab6ab46b394f6fb79cf9533d48051ca7a2f142b8b700a8',
|
||||
'aad2be546ba83cbcab4c1d57ebe22b4a942f294f54333f1a7c2c9ef0e9fe19bb',
|
||||
'acc55c7205cfcd5480e0235acab56a01487a39dc858a641fc04df6ba016870ee',
|
||||
'b7e500deb24cc19bd6ebb2311997f0904ca0d9e51541249e9650ab41fd8ac376',
|
||||
'cf295dff6d9506e8a905d168a44dfcff2f64bd0a6671783a469f8322959c62e2',
|
||||
'f4686749895bf51c6df4092ef6be4279c384a3c380c24ea7a2fd20afc602a35d',
|
||||
],
|
||||
status:
|
||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
||||
};
|
||||
|
||||
return mergeWith<
|
||||
ProtocolUpgradeProposalFieldsFragment,
|
||||
PartialDeep<ProtocolUpgradeProposalFieldsFragment>
|
||||
>(defaultProposal, override, (objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
}
|
||||
return srcValue;
|
||||
});
|
||||
}
|
||||
|
||||
export function generateProposal(
|
||||
override: PartialDeep<ProposalQuery['proposal']> = {}
|
||||
|
||||
+8
@@ -36,6 +36,7 @@ import {
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { VALIDATOR_LOGO_MAP } from './logo-map';
|
||||
import { getMultisigStatusInfo } from '../../../../lib/get-multisig-status-info';
|
||||
|
||||
interface CanonisedConsensusNodeProps {
|
||||
id: string;
|
||||
@@ -137,6 +138,10 @@ export const ConsensusValidatorsTable = ({
|
||||
[totalStake]
|
||||
);
|
||||
|
||||
const multisigStatus = previousEpochData
|
||||
? getMultisigStatusInfo(previousEpochData)
|
||||
: undefined;
|
||||
|
||||
const allNodesInPreviousEpoch = removePaginationWrapper(
|
||||
previousEpochData?.epoch.validatorsConnection?.edges
|
||||
);
|
||||
@@ -223,6 +228,8 @@ export const ConsensusValidatorsTable = ({
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare), 2)
|
||||
: undefined,
|
||||
[ValidatorFields.MULTISIG_ERROR]:
|
||||
multisigStatus?.showMultisigStatusError,
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -332,6 +339,7 @@ export const ConsensusValidatorsTable = ({
|
||||
data,
|
||||
decimals,
|
||||
hideTopThird,
|
||||
multisigStatus?.showMultisigStatusError,
|
||||
previousEpochData,
|
||||
thirdOfTotalStake,
|
||||
validatorsView,
|
||||
|
||||
@@ -39,6 +39,7 @@ export enum ValidatorFields {
|
||||
STAKED_BY_USER = 'stakedByUser',
|
||||
PENDING_USER_STAKE = 'pendingUserStake',
|
||||
USER_STAKE_SHARE = 'userStakeShare',
|
||||
MULTISIG_ERROR = 'multisigError',
|
||||
}
|
||||
|
||||
export const addUserDataToValidator = (
|
||||
@@ -326,6 +327,7 @@ interface TotalPenaltiesRendererProps {
|
||||
overstakedAmount: string;
|
||||
overstakingPenalty: string;
|
||||
totalPenalties: string;
|
||||
multisigError?: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -344,10 +346,11 @@ export const TotalPenaltiesRenderer = ({
|
||||
<div data-testid="overstaked-penalty-tooltip">
|
||||
{t('overstakedPenalty')}: {data.overstakingPenalty}
|
||||
</div>
|
||||
<div data-testid="total-penalty-tooltip">
|
||||
{t('totalPenalties')}:{' '}
|
||||
<span className="font-bold">{data.totalPenalties}</span>
|
||||
</div>
|
||||
{data.multisigError && (
|
||||
<div data-testid="multisig-error-tooltip">
|
||||
{t('multisigPenalty')}: 100%
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -14,6 +14,7 @@ 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
|
||||
|
||||
# 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
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
describe('charts', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.getByTestId('Depth').click();
|
||||
});
|
||||
|
||||
it('can see market depth chart', () => {
|
||||
// 6006-DEPC-001
|
||||
cy.getByTestId('tab-depth').should('be.visible');
|
||||
cy.get('.depth-chart-module_canvas__260De').should('be.visible');
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,8 @@
|
||||
const dialogContent = 'dialog-content';
|
||||
const nodeHealth = 'node-health';
|
||||
const statusIncidentsLink = 'footer [data-testid=external-link]';
|
||||
|
||||
describe('home', { tags: '@regression' }, () => {
|
||||
before(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
@@ -76,23 +74,14 @@ describe('home', { tags: '@regression' }, () => {
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
// 0006-NETW-002
|
||||
// 0006-NETW-003
|
||||
// 0006-NETW-011
|
||||
it('switch to fairground network and check status & incidents link', () => {
|
||||
// 0006-NETW-002
|
||||
// 0006-NETW-003
|
||||
cy.getByTestId('navigation')
|
||||
.find('[data-testid="network-switcher"]')
|
||||
.should('have.text', 'Custom')
|
||||
.click();
|
||||
cy.getByTestId('network-item').contains('Fairground testnet').click();
|
||||
cy.get('[aria-haspopup="menu"]').should('contain.text', 'Fairground');
|
||||
cy.url().should('include', 'fairground.wtf');
|
||||
cy.contains('Continue').click();
|
||||
cy.get(statusIncidentsLink)
|
||||
.children('span')
|
||||
.should('have.text', 'Mainnet status & incidents');
|
||||
cy.get(statusIncidentsLink)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://blog.vega.xyz/tagged/vega-incident-reports');
|
||||
cy.getByTestId('network-item').contains('Fairground testnet');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -182,7 +182,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
.contains('Liquidity monitoring parameters')
|
||||
.click();
|
||||
|
||||
validateMarketDataRow(0, 'Triggering Ratio', '0');
|
||||
validateMarketDataRow(0, 'Triggering Ratio', '0.7');
|
||||
validateMarketDataRow(1, 'Time Window', '3,600');
|
||||
validateMarketDataRow(2, 'Scaling Factor', '10');
|
||||
});
|
||||
|
||||
@@ -81,6 +81,7 @@ describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
||||
|
||||
cy.get(rowSelector).first().find(colFee).should('have.text', '0.09%');
|
||||
|
||||
// 5002-LIQP-013
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find(colAverageEntryValuation)
|
||||
@@ -165,7 +166,7 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
it('can see liquidity supplied', () => {
|
||||
//// 5002-LIQP-008
|
||||
// 5002-LIQP-008
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId('liquidity-supplied').within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
|
||||
@@ -237,6 +238,7 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
||||
.find(colFee)
|
||||
.should('have.text', '0.09%');
|
||||
|
||||
// 5002-LIQP-013
|
||||
cy.get(rowSelectorLiquidityActive)
|
||||
.first()
|
||||
.find(colAverageEntryValuation)
|
||||
@@ -268,7 +270,7 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
it('renders liquidity inactive table correctly', () => {
|
||||
//// 5002-LIQP-012
|
||||
// 5002-LIQP-012
|
||||
cy.getByTestId('Inactive').click();
|
||||
cy.get(rowSelectorLiquidityInactive)
|
||||
.first()
|
||||
|
||||
@@ -128,6 +128,7 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
cy.wrap(btn).click();
|
||||
});
|
||||
}
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
});
|
||||
// 7001-COLL-010
|
||||
it('sorting by asset', () => {
|
||||
@@ -146,12 +147,17 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
it('sorting by total', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
const marketsSortedDefault = [
|
||||
'1,000.00',
|
||||
'1,000.00002',
|
||||
'1,000.01',
|
||||
'1,000.00',
|
||||
'1,000.00001',
|
||||
];
|
||||
const marketsSortedAsc = [
|
||||
'1,000.00',
|
||||
'1,000.00001',
|
||||
'1,000.00002',
|
||||
'1,000.01',
|
||||
];
|
||||
const marketsSortedAsc = ['1,000.00', '1,000.00', '1,000.00', '1,000.01'];
|
||||
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
|
||||
|
||||
checkSorting(
|
||||
@@ -188,19 +194,24 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('sorting by total', () => {
|
||||
it('sorting by available', () => {
|
||||
cy.getByTestId('Collateral').click();
|
||||
const marketsSortedDefault = [
|
||||
'1,000.00',
|
||||
'1,000.01',
|
||||
'1,000.00002',
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00001',
|
||||
];
|
||||
const marketsSortedAsc = [
|
||||
'1,000.00',
|
||||
'1,000.00',
|
||||
'1,000.00001',
|
||||
'1,000.00002',
|
||||
];
|
||||
const marketsSortedAsc = ['1,000.00', '1,000.00', '1,000.00', '1,000.01'];
|
||||
const marketsSortedDesc = Array.from(marketsSortedAsc).reverse();
|
||||
|
||||
checkSorting(
|
||||
'total',
|
||||
'available',
|
||||
marketsSortedDefault,
|
||||
marketsSortedAsc,
|
||||
marketsSortedDesc
|
||||
|
||||
@@ -5,6 +5,10 @@ import {
|
||||
toggleMarket,
|
||||
} from '../support/deal-ticket';
|
||||
|
||||
const tooltipContent = 'tooltip-content';
|
||||
const reduceOnly = 'reduce-only';
|
||||
const postOnly = 'post-only';
|
||||
|
||||
describe('time in force validation', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setVegaWallet();
|
||||
@@ -122,13 +126,18 @@ describe('time in force validation', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-026
|
||||
|
||||
it(`post and reduce order market for ${tif.code}`, function () {
|
||||
// 7003-SORD-054
|
||||
// 7003-SORD-055
|
||||
// 7003-SORD-056
|
||||
// 7003-SORD-057
|
||||
|
||||
cy.getByTestId(orderTIFDropDown).select(tif.value);
|
||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
||||
'have.text',
|
||||
tif.text
|
||||
);
|
||||
cy.getByTestId('post-only').should('be.disabled');
|
||||
cy.getByTestId('reduce-only').should('be.enabled');
|
||||
cy.getByTestId(postOnly).should('be.disabled');
|
||||
cy.getByTestId(reduceOnly).should('be.enabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -144,14 +153,33 @@ describe('time in force validation', { tags: '@smoke' }, () => {
|
||||
|
||||
validTIFLimit.forEach((tif) => {
|
||||
it(`post and reduce order for limit ${tif.code}`, function () {
|
||||
// 7003-SORD-054
|
||||
// 7003-SORD-055
|
||||
// 7003-SORD-056
|
||||
// 7003-SORD-057
|
||||
cy.getByTestId(orderTIFDropDown).select(tif.value);
|
||||
cy.get(`[data-testid=${orderTIFDropDown}] option:selected`).should(
|
||||
'have.text',
|
||||
tif.text
|
||||
);
|
||||
cy.getByTestId('post-only').should('be.enabled');
|
||||
cy.getByTestId('reduce-only').should('be.disabled');
|
||||
cy.getByTestId(postOnly).should('be.enabled');
|
||||
cy.getByTestId(reduceOnly).should('be.disabled');
|
||||
});
|
||||
});
|
||||
it(`can see explanation of what post only and reduce only is/does`, function () {
|
||||
// 7003-SORD-058
|
||||
cy.get('[for="post-only"]').should('have.text', 'Post only').realHover();
|
||||
cy.getByTestId(tooltipContent).should(
|
||||
'contain.text',
|
||||
`"Post only" will ensure the order is not filled immediately but is placed on the order book as a passive order. When the order is processed it is either stopped (if it would not be filled immediately), or placed in the order book as a passive order until the price taker matches with it.`
|
||||
);
|
||||
cy.get('[for="reduce-only"]')
|
||||
.should('have.text', 'Reduce only')
|
||||
.realHover();
|
||||
cy.getByTestId(tooltipContent).should(
|
||||
'contain.text',
|
||||
`"Reduce only" can be used only with non-persistent orders, such as "Fill or Kill" or "Immediate or Cancel".`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { checkSorting, aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { marketsDataQuery } from '@vegaprotocol/mock';
|
||||
import { positionsQuery } from '@vegaprotocol/mock';
|
||||
|
||||
@@ -15,13 +14,12 @@ const toastContent = 'toast-content';
|
||||
const tooltipContent = 'tooltip-content';
|
||||
// #endregion
|
||||
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
it('renders positions on trading page', () => {
|
||||
visitAndClickPositions();
|
||||
// 7004-POSI-001
|
||||
@@ -64,7 +62,14 @@ describe('positions', { tags: '@smoke', testIsolation: true }, () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
});
|
||||
|
||||
it('rows should be displayed despite errors', () => {
|
||||
const errors = [
|
||||
{
|
||||
@@ -164,8 +169,9 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
);
|
||||
});
|
||||
|
||||
// let elementWidth: number;
|
||||
|
||||
it('Resize column', () => {
|
||||
let elementWidth: number;
|
||||
visitAndClickPositions();
|
||||
cy.get('.ag-overlay-loading-wrapper').should('not.be.visible');
|
||||
cy.get('.ag-header-container').within(() => {
|
||||
@@ -180,29 +186,33 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
cy.get(`[col-id="marketName"]`)
|
||||
.invoke('width')
|
||||
.should('be.greaterThan', 250);
|
||||
cy.get(`[col-id="marketName"]`)
|
||||
.invoke('width')
|
||||
.then((width) => {
|
||||
elementWidth = width as number;
|
||||
})
|
||||
.then(() => {
|
||||
let localStorageCopy: Record<string, string>;
|
||||
cy.window().then((win) => {
|
||||
localStorageCopy = { ...win.localStorage };
|
||||
});
|
||||
});
|
||||
|
||||
cy.reload();
|
||||
cy.window().then((win) => {
|
||||
Object.keys(localStorageCopy).forEach((key) => {
|
||||
win.localStorage.setItem(key, localStorageCopy[key]);
|
||||
});
|
||||
});
|
||||
// This test depends on the previous one
|
||||
it('Has persisted column widths', () => {
|
||||
const width = 400;
|
||||
|
||||
// 7004-POSI-012
|
||||
cy.get('[col-id="marketName"]')
|
||||
.invoke('width')
|
||||
.should('equal', elementWidth);
|
||||
});
|
||||
cy.window().then((win) => {
|
||||
win.localStorage.setItem(
|
||||
'vega_positions_store',
|
||||
JSON.stringify({
|
||||
state: {
|
||||
gridStore: {
|
||||
columnState: [{ colId: 'marketName', width }],
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
visitAndClickPositions();
|
||||
|
||||
// 7004-POSI-012
|
||||
cy.get('.ag-center-cols-container .ag-row')
|
||||
.first()
|
||||
.find('[col-id="marketName"]')
|
||||
.invoke('outerWidth')
|
||||
.should('equal', width);
|
||||
});
|
||||
|
||||
it('Scroll horizontally', () => {
|
||||
@@ -291,6 +301,7 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
cy.getByTestId(dialogContent).should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
function validatePositionsDisplayed(multiKey = false) {
|
||||
cy.getByTestId('tab-positions').should('be.visible');
|
||||
cy.getByTestId('tab-positions')
|
||||
@@ -326,6 +337,7 @@ function validatePositionsDisplayed(multiKey = false) {
|
||||
|
||||
cy.getByTestId('close-position').should('be.visible').and('have.length', 3);
|
||||
}
|
||||
|
||||
function assertPNLColor(
|
||||
pnlSelector: string,
|
||||
positiveClass: string,
|
||||
@@ -347,6 +359,7 @@ function assertPNLColor(
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function visitAndClickPositions() {
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.getByTestId(positions).click();
|
||||
|
||||
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
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_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
|
||||
NX_VEGA_ENV=DEVNET
|
||||
NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
NX_ETHEREUM_PROVIDER_URL=https://eth-mainnet.gateway.pokt.network/v1/lb/af6a2d529a11f8158bc8ca2a
|
||||
NX_ETHERSCAN_URL=https://etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
|
||||
NX_VEGA_ENV=MAINNET
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz
|
||||
|
||||
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
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_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/stagnet1/vegawallet-stagnet1.toml
|
||||
NX_VEGA_ENV=STAGNET1
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.stagnet1.vega.rocks
|
||||
|
||||
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
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_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
|
||||
@@ -2,7 +2,7 @@ NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9
|
||||
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_TRADING_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_SENTRY_DSN=https://2ffce43721964aafa78277c50654ece4@o286262.ingest.sentry.io/6300613
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/testnet2/testnet2.toml
|
||||
NX_VEGA_ENV=VALIDATOR_TESTNET
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import {
|
||||
matchFilter,
|
||||
liquidityProvisionsDataProvider,
|
||||
LiquidityTable,
|
||||
lpAggregatedDataProvider,
|
||||
useCheckLiquidityStatus,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
@@ -24,18 +22,16 @@ import {
|
||||
ExternalLink,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
|
||||
import { Header, HeaderStat, HeaderTitle } from '../../components/header';
|
||||
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
|
||||
import type { Filter } from '@vegaprotocol/liquidity';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
|
||||
import { useMarket, useStaticMarketData } from '@vegaprotocol/markets';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { LiquidityContainer } from '../../components/liquidity-container';
|
||||
|
||||
const enum LiquidityTabs {
|
||||
Active = 'active',
|
||||
@@ -49,62 +45,6 @@ export const Liquidity = () => {
|
||||
return <LiquidityViewContainer marketId={marketId} />;
|
||||
};
|
||||
|
||||
const useReloadLiquidityData = (marketId: string | undefined) => {
|
||||
const { reload } = useDataProvider({
|
||||
dataProvider: liquidityProvisionsDataProvider,
|
||||
variables: { marketId: marketId || '' },
|
||||
update: () => true,
|
||||
skip: !marketId,
|
||||
});
|
||||
useEffect(() => {
|
||||
const interval = setInterval(reload, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, [reload]);
|
||||
};
|
||||
|
||||
export const LiquidityContainer = ({
|
||||
marketId,
|
||||
filter,
|
||||
}: {
|
||||
marketId: string | undefined;
|
||||
filter?: Filter;
|
||||
}) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { data: market } = useMarket(marketId);
|
||||
|
||||
// To be removed when liquidityProvision subscriptions are working
|
||||
useReloadLiquidityData(marketId);
|
||||
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: lpAggregatedDataProvider,
|
||||
variables: { marketId: marketId || '', filter },
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const assetDecimalPlaces =
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
|
||||
const symbol =
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.market_liquidity_stakeToCcyVolume,
|
||||
]);
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<LiquidityTable
|
||||
ref={gridRef}
|
||||
rowData={data}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No data')}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const LiquidityViewHeader = memo(({ marketId }: { marketId?: string }) => {
|
||||
const { data: market } = useMarket(marketId);
|
||||
const { data: marketData } = useStaticMarketData(marketId);
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useCallback, useState, useMemo } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FixedSizeList } from 'react-window';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
@@ -184,7 +184,6 @@ const MarketList = ({
|
||||
if (error) {
|
||||
return <div>{error.message}</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
@@ -204,6 +203,29 @@ const MarketList = ({
|
||||
);
|
||||
};
|
||||
|
||||
interface ListItemData {
|
||||
data: MarketMaybeWithDataAndCandles[];
|
||||
onSelect?: (marketId: string) => void;
|
||||
currentMarketId?: string;
|
||||
}
|
||||
|
||||
const ListItem = ({
|
||||
index,
|
||||
style,
|
||||
data,
|
||||
}: {
|
||||
index: number;
|
||||
style: CSSProperties;
|
||||
data: ListItemData;
|
||||
}) => (
|
||||
<MarketSelectorItem
|
||||
market={data.data[index]}
|
||||
currentMarketId={data.currentMarketId}
|
||||
style={style}
|
||||
onSelect={data.onSelect}
|
||||
/>
|
||||
);
|
||||
|
||||
const List = ({
|
||||
data,
|
||||
loading,
|
||||
@@ -212,28 +234,20 @@ const List = ({
|
||||
onSelect,
|
||||
noItems,
|
||||
currentMarketId,
|
||||
}: {
|
||||
data: MarketMaybeWithDataAndCandles[];
|
||||
}: ListItemData & {
|
||||
loading: boolean;
|
||||
width: number;
|
||||
height: number;
|
||||
noItems: string;
|
||||
onSelect?: (marketId: string) => void;
|
||||
currentMarketId?: string;
|
||||
}) => {
|
||||
const row = ({ index, style }: { index: number; style: CSSProperties }) => {
|
||||
const market = data[index];
|
||||
|
||||
return (
|
||||
<MarketSelectorItem
|
||||
market={market}
|
||||
currentMarketId={currentMarketId}
|
||||
style={style}
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const itemKey = useCallback(
|
||||
(index: number, data: ListItemData) => data.data[index].id,
|
||||
[]
|
||||
);
|
||||
const itemData = useMemo(
|
||||
() => ({ data, onSelect, currentMarketId }),
|
||||
[data, onSelect, currentMarketId]
|
||||
);
|
||||
if (!data || loading) {
|
||||
return (
|
||||
<div style={{ width, height }}>
|
||||
@@ -259,11 +273,13 @@ const List = ({
|
||||
<FixedSizeList
|
||||
className="virtualized-list"
|
||||
itemCount={data.length}
|
||||
itemData={itemData}
|
||||
itemSize={130}
|
||||
itemKey={itemKey}
|
||||
width={width}
|
||||
height={height}
|
||||
>
|
||||
{row}
|
||||
{ListItem}
|
||||
</FixedSizeList>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -19,10 +19,7 @@ import {
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
} from '../../lib/hooks/use-market-click-handler';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { HeaderTitle } from '../../components/header';
|
||||
import {
|
||||
@@ -49,7 +46,6 @@ const MarketBottomPanel = memo(
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
return 'xxxl' === screenSize ? (
|
||||
<ResizableGrid
|
||||
@@ -69,10 +65,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Open}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketOpenOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -81,10 +73,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Closed}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketClosedOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -93,22 +81,12 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Rejected}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketRejectOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('All')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketAllOrders"
|
||||
/>
|
||||
<TradingViews.orders.component marketId={marketId} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
@@ -116,7 +94,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="marketFills"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -134,8 +111,6 @@ const MarketBottomPanel = memo(
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
noBottomPlaceholder
|
||||
storeKey="marketPositions"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -145,7 +120,6 @@ const MarketBottomPanel = memo(
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
hideButtons
|
||||
storeKey="marketCollateral"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -158,10 +132,7 @@ const MarketBottomPanel = memo(
|
||||
<Tabs storageKey="console-trade-grid-bottom">
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="marketPositions"
|
||||
/>
|
||||
<TradingViews.positions.component onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="open-orders" name={t('Open')}>
|
||||
@@ -169,10 +140,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Open}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketOpenOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -181,10 +148,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Closed}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketClosedOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -193,22 +156,12 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Rejected}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketRejectedOrders"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('All')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
enforceBottomPlaceholder
|
||||
storeKey="marketAllOrders"
|
||||
/>
|
||||
<TradingViews.orders.component marketId={marketId} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
@@ -216,7 +169,6 @@ const MarketBottomPanel = memo(
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="marketFills"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
@@ -226,7 +178,6 @@ const MarketBottomPanel = memo(
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
hideButtons
|
||||
storeKey="marketCollateral"
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
|
||||
@@ -39,7 +39,7 @@ export const TradePanels = ({
|
||||
}: TradePanelsProps) => {
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||
|
||||
const [view, setView] = useState<TradingView>('candles');
|
||||
const renderView = () => {
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DealTicketContainer } from '@vegaprotocol/deal-ticket';
|
||||
import { MarketInfoAccordionContainer } from '@vegaprotocol/markets';
|
||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||
import { OrderListContainer, Filter } from '@vegaprotocol/orders';
|
||||
import type { OrderListContainerProps } from '@vegaprotocol/orders';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||
import { TradesContainer } from '@vegaprotocol/trades';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import { OrderbookContainer } from '@vegaprotocol/market-depth';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { NO_MARKET } from './constants';
|
||||
import { LiquidityContainer } from '../liquidity/liquidity';
|
||||
import { FillsContainer } from '../../components/fills-container';
|
||||
import { PositionsContainer } from '../../components/positions-container';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import { LiquidityContainer } from '../../components/liquidity-container';
|
||||
import type { OrderContainerProps } from '../../components/orders-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
|
||||
type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
@@ -65,25 +66,25 @@ export const TradingViews = {
|
||||
positions: { label: 'Positions', component: PositionsContainer },
|
||||
activeOrders: {
|
||||
label: 'Active',
|
||||
component: (props: OrderListContainerProps) => (
|
||||
<OrderListContainer {...props} filter={Filter.Open} />
|
||||
component: (props: OrderContainerProps) => (
|
||||
<OrdersContainer {...props} filter={Filter.Open} />
|
||||
),
|
||||
},
|
||||
closedOrders: {
|
||||
label: 'Closed',
|
||||
component: (props: OrderListContainerProps) => (
|
||||
<OrderListContainer {...props} filter={Filter.Closed} />
|
||||
component: (props: OrderContainerProps) => (
|
||||
<OrdersContainer {...props} filter={Filter.Closed} />
|
||||
),
|
||||
},
|
||||
rejectedOrders: {
|
||||
label: 'Rejected',
|
||||
component: (props: OrderListContainerProps) => (
|
||||
<OrderListContainer {...props} filter={Filter.Rejected} />
|
||||
component: (props: OrderContainerProps) => (
|
||||
<OrdersContainer {...props} filter={Filter.Rejected} />
|
||||
),
|
||||
},
|
||||
orders: {
|
||||
label: 'All',
|
||||
component: OrderListContainer,
|
||||
component: OrdersContainer,
|
||||
},
|
||||
collateral: { label: 'Collateral', component: AccountsContainer },
|
||||
fills: { label: 'Fills', component: FillsContainer },
|
||||
|
||||
@@ -313,7 +313,6 @@ const ClosedMarketsDataGrid = ({
|
||||
minWidth: 100,
|
||||
}}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No markets')}
|
||||
storeKey="closedMarkets"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useRef } from 'react';
|
||||
@@ -17,17 +16,13 @@ export const DepositsContainer = () => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
const openDepositDialog = useDepositDialog((state) => state.open);
|
||||
const bottomPlaceholderProps = useBottomPlaceholder({ gridRef });
|
||||
return (
|
||||
<div className="h-full">
|
||||
<div className="h-full relative">
|
||||
<DepositsTable
|
||||
rowData={data}
|
||||
ref={gridRef}
|
||||
{...bottomPlaceholderProps}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||
/>
|
||||
</div>
|
||||
<DepositsTable
|
||||
rowData={data}
|
||||
ref={gridRef}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||
/>
|
||||
{!isReadOnly && (
|
||||
<div className="h-auto flex justify-end px-[11px] py-2 bottom-0 right-3 absolute dark:bg-black/75 bg-white/75 rounded">
|
||||
<Button
|
||||
|
||||
@@ -1,29 +1,39 @@
|
||||
import { useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { LayoutPriority } from 'allotment';
|
||||
import { titlefy } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
||||
import { usePaneLayout } from '@vegaprotocol/react-helpers';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { DepositsContainer } from './deposits-container';
|
||||
import { LayoutPriority } from 'allotment';
|
||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { usePageTitleStore } from '../../stores';
|
||||
import { LedgerContainer } from '@vegaprotocol/ledger';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import { DepositsContainer } from './deposits-container';
|
||||
import { FillsContainer } from '../../components/fills-container';
|
||||
import { PositionsContainer } from '../../components/positions-container';
|
||||
import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { LedgerContainer } from '../../components/ledger-container';
|
||||
import { AccountHistoryContainer } from './account-history-container';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
} from '../../lib/hooks/use-market-click-handler';
|
||||
import {
|
||||
ResizableGrid,
|
||||
ResizableGridPanel,
|
||||
} from '../../components/resizable-grid';
|
||||
|
||||
const WithdrawalsIndicator = () => {
|
||||
const { ready } = useIncompleteWithdrawals();
|
||||
if (!ready || ready.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<span className="bg-vega-blue-450 text-white text-[10px] rounded p-[3px] pb-[2px] leading-none">
|
||||
{ready.length}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export const Portfolio = () => {
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
updateTitle: store.updateTitle,
|
||||
@@ -34,7 +44,6 @@ export const Portfolio = () => {
|
||||
}, [updateTitle]);
|
||||
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||
return (
|
||||
@@ -50,29 +59,17 @@ export const Portfolio = () => {
|
||||
</Tab>
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<VegaWalletContainer>
|
||||
<PositionsContainer
|
||||
onMarketClick={onMarketClick}
|
||||
noBottomPlaceholder
|
||||
storeKey="portfolioPositions"
|
||||
allKeys
|
||||
/>
|
||||
<PositionsContainer onMarketClick={onMarketClick} allKeys />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<VegaWalletContainer>
|
||||
<OrderListContainer
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
storeKey="portfolioOrders"
|
||||
/>
|
||||
<OrdersContainer />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<VegaWalletContainer>
|
||||
<FillsContainer
|
||||
onMarketClick={onMarketClick}
|
||||
storeKey="portfolioFills"
|
||||
/>
|
||||
<FillsContainer onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||
@@ -92,10 +89,7 @@ export const Portfolio = () => {
|
||||
<Tabs storageKey="console-portfolio-bottom">
|
||||
<Tab id="collateral" name={t('Collateral')}>
|
||||
<VegaWalletContainer>
|
||||
<AccountsContainer
|
||||
storeKey="portfolioCollateral"
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
<AccountsContainer />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="deposits" name={t('Deposits')}>
|
||||
@@ -103,7 +97,11 @@ export const Portfolio = () => {
|
||||
<DepositsContainer />
|
||||
</VegaWalletContainer>
|
||||
</Tab>
|
||||
<Tab id="withdrawals" name={t('Withdrawals')}>
|
||||
<Tab
|
||||
id="withdrawals"
|
||||
name={t('Withdrawals')}
|
||||
indicator={<WithdrawalsIndicator />}
|
||||
>
|
||||
<WithdrawalsContainer />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
withdrawalProvider,
|
||||
useWithdrawalDialog,
|
||||
WithdrawalsTable,
|
||||
useIncompleteWithdrawals,
|
||||
} from '@vegaprotocol/withdraws';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
@@ -17,6 +18,7 @@ export const WithdrawalsContainer = () => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
const openWithdrawDialog = useWithdrawalDialog((state) => state.open);
|
||||
const { ready, delayed } = useIncompleteWithdrawals();
|
||||
|
||||
return (
|
||||
<VegaWalletContainer>
|
||||
@@ -25,6 +27,8 @@ export const WithdrawalsContainer = () => {
|
||||
data-testid="withdrawals-history"
|
||||
rowData={data}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No withdrawals')}
|
||||
ready={ready}
|
||||
delayed={delayed}
|
||||
/>
|
||||
</div>
|
||||
{!isReadOnly && (
|
||||
|
||||
@@ -8,16 +8,19 @@ import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import { AccountManager, useTransferDialog } from '@vegaprotocol/accounts';
|
||||
import { useDepositDialog } from '@vegaprotocol/deposits';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
|
||||
export const AccountsContainer = ({
|
||||
pinnedAsset,
|
||||
hideButtons,
|
||||
storeKey,
|
||||
onMarketClick,
|
||||
}: {
|
||||
pinnedAsset?: PinnedAsset;
|
||||
hideButtons?: boolean;
|
||||
storeKey?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
}) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
@@ -26,6 +29,12 @@ export const AccountsContainer = ({
|
||||
const openDepositDialog = useDepositDialog((store) => store.open);
|
||||
const openTransferDialog = useTransferDialog((store) => store.open);
|
||||
|
||||
const gridStore = useAccountStore((store) => store.gridStore);
|
||||
const updateGridStore = useAccountStore((store) => store.updateGridStore);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||
updateGridStore(colState);
|
||||
});
|
||||
|
||||
const onClickAsset = useCallback(
|
||||
(assetId?: string) => {
|
||||
assetId && openAssetDetailsDialog(assetId);
|
||||
@@ -51,7 +60,7 @@ export const AccountsContainer = ({
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
pinnedAsset={pinnedAsset}
|
||||
storeKey={storeKey}
|
||||
gridProps={gridStoreCallbacks}
|
||||
/>
|
||||
{!isReadOnly && !hideButtons && (
|
||||
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
|
||||
@@ -75,3 +84,9 @@ export const AccountsContainer = ({
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useAccountStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_accounts_store',
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { DefaultWeb3ProviderContextShape } from '@vegaprotocol/web3';
|
||||
import {
|
||||
useEthereumConfig,
|
||||
createConnectors,
|
||||
Web3Provider as Web3ProviderInternal,
|
||||
useWeb3ConnectStore,
|
||||
createDefaultProvider,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
|
||||
export const Web3Provider = ({ children }: { children: ReactNode }) => {
|
||||
@@ -17,10 +20,13 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
|
||||
|
||||
const connectors = useWeb3ConnectStore((store) => store.connectors);
|
||||
const initializeConnectors = useWeb3ConnectStore((store) => store.initialize);
|
||||
const [defaultProvider, setDefaultProvider] = useState<
|
||||
DefaultWeb3ProviderContextShape['provider'] | undefined
|
||||
>(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
if (config?.chain_id) {
|
||||
return initializeConnectors(
|
||||
initializeConnectors(
|
||||
createConnectors(
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
Number(config?.chain_id),
|
||||
@@ -29,6 +35,11 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
|
||||
),
|
||||
Number(config.chain_id)
|
||||
);
|
||||
const defaultProvider = createDefaultProvider(
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
Number(config?.chain_id)
|
||||
);
|
||||
setDefaultProvider(defaultProvider);
|
||||
}
|
||||
}, [
|
||||
config?.chain_id,
|
||||
@@ -49,7 +60,10 @@ export const Web3Provider = ({ children }: { children: ReactNode }) => {
|
||||
}}
|
||||
noDataMessage={t('Could not fetch Ethereum configuration')}
|
||||
>
|
||||
<Web3ProviderInternal connectors={connectors}>
|
||||
<Web3ProviderInternal
|
||||
connectors={connectors}
|
||||
defaultProvider={defaultProvider}
|
||||
>
|
||||
<>{children}</>
|
||||
</Web3ProviderInternal>
|
||||
</AsyncRenderer>
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { FillsManager } from '@vegaprotocol/fills';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
|
||||
export const FillsContainer = ({
|
||||
marketId,
|
||||
onMarketClick,
|
||||
}: {
|
||||
marketId?: string;
|
||||
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
|
||||
}) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const gridStore = useFillsStore((store) => store.gridStore);
|
||||
const updateGridStore = useFillsStore((store) => store.updateGridStore);
|
||||
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||
updateGridStore(colState);
|
||||
});
|
||||
|
||||
if (!pubKey) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('Please connect Vega wallet')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<FillsManager
|
||||
partyId={pubKey}
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
gridProps={gridStoreCallbacks}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const useFillsStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_fills_store',
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './fills-container';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './ledger-container';
|
||||
@@ -0,0 +1,36 @@
|
||||
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { LedgerManager } from '@vegaprotocol/ledger';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
export const LedgerContainer = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const gridStore = useLedgerStore((store) => store.gridStore);
|
||||
const updateGridStore = useLedgerStore((store) => store.updateGridStore);
|
||||
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||
updateGridStore(colState);
|
||||
});
|
||||
|
||||
if (!pubKey) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('Please connect Vega wallet')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return <LedgerManager partyId={pubKey} gridProps={gridStoreCallbacks} />;
|
||||
};
|
||||
|
||||
const useLedgerStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_ledger_store',
|
||||
})
|
||||
);
|
||||
@@ -0,0 +1 @@
|
||||
export * from './liquidity-container';
|
||||
@@ -0,0 +1,93 @@
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
lpAggregatedDataProvider,
|
||||
type Filter,
|
||||
LiquidityTable,
|
||||
liquidityProvisionsDataProvider,
|
||||
} from '@vegaprotocol/liquidity';
|
||||
import { useMarket } from '@vegaprotocol/markets';
|
||||
import {
|
||||
NetworkParams,
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
export const LiquidityContainer = ({
|
||||
marketId,
|
||||
filter,
|
||||
}: {
|
||||
marketId: string | undefined;
|
||||
filter?: Filter;
|
||||
}) => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
|
||||
const gridStore = useLiquidityStore((store) => store.gridStore);
|
||||
const updateGridStore = useLiquidityStore((store) => store.updateGridStore);
|
||||
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||
updateGridStore(colState);
|
||||
});
|
||||
const { data: market } = useMarket(marketId);
|
||||
|
||||
// To be removed when liquidityProvision subscriptions are working
|
||||
useReloadLiquidityData(marketId);
|
||||
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: lpAggregatedDataProvider,
|
||||
variables: { marketId: marketId || '', filter },
|
||||
skip: !marketId,
|
||||
});
|
||||
|
||||
const assetDecimalPlaces =
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.decimals || 0;
|
||||
const quantum =
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.quantum || 0;
|
||||
const symbol =
|
||||
market?.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.market_liquidity_stakeToCcyVolume,
|
||||
]);
|
||||
const stakeToCcyVolume = params.market_liquidity_stakeToCcyVolume;
|
||||
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<LiquidityTable
|
||||
ref={gridRef}
|
||||
rowData={data}
|
||||
symbol={symbol}
|
||||
assetDecimalPlaces={assetDecimalPlaces}
|
||||
quantum={quantum}
|
||||
stakeToCcyVolume={stakeToCcyVolume}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No data')}
|
||||
{...gridStoreCallbacks}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const useReloadLiquidityData = (marketId: string | undefined) => {
|
||||
const { reload } = useDataProvider({
|
||||
dataProvider: liquidityProvisionsDataProvider,
|
||||
variables: { marketId: marketId || '' },
|
||||
update: () => true,
|
||||
skip: !marketId,
|
||||
});
|
||||
useEffect(() => {
|
||||
const interval = setInterval(reload, 30000);
|
||||
return () => clearInterval(interval);
|
||||
}, [reload]);
|
||||
};
|
||||
|
||||
const useLiquidityStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_ledger_store',
|
||||
})
|
||||
);
|
||||
@@ -125,14 +125,19 @@ export const MarketLiquiditySupplied = ({
|
||||
</KeyValueTableRow>
|
||||
</KeyValueTable>
|
||||
<br />
|
||||
<Link href={`/#/liquidity/${marketId}`} data-testid="view-liquidity-link">
|
||||
{t('View liquidity provision table')}
|
||||
</Link>
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.LIQUIDITY} className="mt-2">
|
||||
{t('Learn about providing liquidity')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
<div className="flex flex-col gap-2">
|
||||
<Link
|
||||
href={`/#/liquidity/${marketId}`}
|
||||
data-testid="view-liquidity-link"
|
||||
>
|
||||
{t('View liquidity provision table')}
|
||||
</Link>
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.LIQUIDITY} className="mt-2">
|
||||
{t('Learn about providing liquidity')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</div>
|
||||
{showMessage && (
|
||||
<p className="mt-4">
|
||||
{t(
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './orders-container';
|
||||
@@ -0,0 +1,106 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import {
|
||||
FilterStatusValue,
|
||||
STORAGE_KEY,
|
||||
useOrderListGridState,
|
||||
} from './orders-container';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { OrderType } from '@vegaprotocol/types';
|
||||
|
||||
describe('useOrderListGridState', () => {
|
||||
afterAll(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
|
||||
const setup = (filter: Filter | undefined) => {
|
||||
return renderHook(() => useOrderListGridState(filter));
|
||||
};
|
||||
|
||||
it.each(Object.values(Filter))(
|
||||
'providers correct AgGrid filter for %s',
|
||||
(filter) => {
|
||||
const { result } = setup(filter);
|
||||
expect(typeof result.current.updateGridState).toBe('function');
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState: undefined,
|
||||
filterModel: {
|
||||
status: {
|
||||
value: FilterStatusValue[filter],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('provides correct AgGrid filter for all', () => {
|
||||
const { result } = setup(undefined);
|
||||
expect(typeof result.current.updateGridState).toBe('function');
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState: undefined,
|
||||
filterModel: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(Object.values(Filter))(
|
||||
'sets and stores column state and filters for %s',
|
||||
(filter) => {
|
||||
const filterModel = {
|
||||
type: {
|
||||
value: [OrderType.TYPE_LIMIT],
|
||||
},
|
||||
};
|
||||
const { result } = setup(filter);
|
||||
|
||||
act(() => {
|
||||
result.current.updateGridState(filter, {
|
||||
filterModel,
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState: undefined,
|
||||
filterModel: {
|
||||
...filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[filter],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const columnState = [{ colId: 'status', width: 200 }];
|
||||
|
||||
act(() => {
|
||||
result.current.updateGridState(filter, {
|
||||
columnState,
|
||||
});
|
||||
});
|
||||
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState,
|
||||
filterModel: {
|
||||
...filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[filter],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const storeKeyMap = {
|
||||
[Filter.Open]: 'open',
|
||||
[Filter.Rejected]: 'rejected',
|
||||
[Filter.Closed]: 'closed',
|
||||
};
|
||||
|
||||
expect(JSON.parse(localStorage.getItem(STORAGE_KEY) || '')).toMatchObject(
|
||||
{
|
||||
state: {
|
||||
[storeKeyMap[filter]]: {
|
||||
columnState,
|
||||
filterModel, // no need to check that status is set, hook will return status
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,166 @@
|
||||
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { OrderListManager } from '@vegaprotocol/orders';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
} from '../../lib/hooks/use-market-click-handler';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import type { DataGridStore } from '../../stores/datagrid-store-slice';
|
||||
import { OrderStatus } from '@vegaprotocol/types';
|
||||
|
||||
export const FilterStatusValue = {
|
||||
[Filter.Open]: [OrderStatus.STATUS_ACTIVE, OrderStatus.STATUS_PARKED],
|
||||
[Filter.Closed]: [
|
||||
OrderStatus.STATUS_CANCELLED,
|
||||
OrderStatus.STATUS_EXPIRED,
|
||||
OrderStatus.STATUS_FILLED,
|
||||
OrderStatus.STATUS_PARTIALLY_FILLED,
|
||||
OrderStatus.STATUS_STOPPED,
|
||||
],
|
||||
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
|
||||
};
|
||||
|
||||
export interface OrderContainerProps {
|
||||
marketId?: string;
|
||||
filter?: Filter;
|
||||
}
|
||||
|
||||
export const OrdersContainer = ({ marketId, filter }: OrderContainerProps) => {
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||
const { gridState, updateGridState } = useOrderListGridState(filter);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridState, (newState) => {
|
||||
updateGridState(filter, newState);
|
||||
});
|
||||
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
|
||||
return (
|
||||
<OrderListManager
|
||||
partyId={pubKey}
|
||||
marketId={marketId}
|
||||
filter={filter}
|
||||
onMarketClick={onMarketClick}
|
||||
onOrderTypeClick={onOrderTypeClick}
|
||||
isReadOnly={isReadOnly}
|
||||
gridProps={gridStoreCallbacks}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const STORAGE_KEY = 'vega_order_list_store';
|
||||
const useOrderListStore = create<{
|
||||
open: DataGridStore;
|
||||
closed: DataGridStore;
|
||||
rejected: DataGridStore;
|
||||
all: DataGridStore;
|
||||
update: (filter: Filter | undefined, gridStore: DataGridStore) => void;
|
||||
}>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
open: {},
|
||||
closed: {},
|
||||
rejected: {},
|
||||
all: {},
|
||||
update: (filter, newStore) => {
|
||||
switch (filter) {
|
||||
case Filter.Open: {
|
||||
set((curr) => ({
|
||||
open: {
|
||||
...curr.open,
|
||||
...newStore,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
case Filter.Closed: {
|
||||
set((curr) => ({
|
||||
closed: {
|
||||
...curr.closed,
|
||||
...newStore,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
case Filter.Rejected: {
|
||||
set((curr) => ({
|
||||
rejected: {
|
||||
...curr.rejected,
|
||||
...newStore,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
case undefined: {
|
||||
set((curr) => ({
|
||||
all: {
|
||||
...curr.all,
|
||||
...newStore,
|
||||
},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: STORAGE_KEY,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const useOrderListGridState = (filter: Filter | undefined) => {
|
||||
const updateGridState = useOrderListStore((store) => store.update);
|
||||
const gridState = useOrderListStore((store) => {
|
||||
// Return the column/filter state for the given filter but ensuring that
|
||||
// each filter controlled by the tab is always applied
|
||||
switch (filter) {
|
||||
case Filter.Open: {
|
||||
return {
|
||||
columnState: store.open.columnState,
|
||||
filterModel: {
|
||||
...store.open.filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Open],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case Filter.Closed: {
|
||||
return {
|
||||
columnState: store.closed.columnState,
|
||||
filterModel: {
|
||||
...store.closed.filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Closed],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
case Filter.Rejected: {
|
||||
return {
|
||||
columnState: store.rejected.columnState,
|
||||
filterModel: {
|
||||
...store.rejected.filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Rejected],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
default: {
|
||||
return store.all;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { gridState, updateGridState };
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './positions-container';
|
||||
+19
-7
@@ -1,21 +1,28 @@
|
||||
import { useDataGridEvents } from '@vegaprotocol/datagrid';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { PositionsManager } from '@vegaprotocol/positions';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { PositionsManager } from './positions-manager';
|
||||
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
|
||||
export const PositionsContainer = ({
|
||||
onMarketClick,
|
||||
noBottomPlaceholder,
|
||||
storeKey,
|
||||
allKeys,
|
||||
}: {
|
||||
onMarketClick?: (marketId: string) => void;
|
||||
noBottomPlaceholder?: boolean;
|
||||
storeKey?: string;
|
||||
allKeys?: boolean;
|
||||
}) => {
|
||||
const { pubKey, pubKeys, isReadOnly } = useVegaWallet();
|
||||
|
||||
const gridStore = usePositionsStore((store) => store.gridStore);
|
||||
const updateGridStore = usePositionsStore((store) => store.updateGridStore);
|
||||
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
|
||||
updateGridStore(colState);
|
||||
});
|
||||
|
||||
if (!pubKey) {
|
||||
return (
|
||||
<Splash>
|
||||
@@ -38,8 +45,13 @@ export const PositionsContainer = ({
|
||||
partyIds={partyIds}
|
||||
onMarketClick={onMarketClick}
|
||||
isReadOnly={isReadOnly}
|
||||
noBottomPlaceholder={noBottomPlaceholder}
|
||||
storeKey={storeKey}
|
||||
gridProps={gridStoreCallbacks}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const usePositionsStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_positions_store',
|
||||
})
|
||||
);
|
||||
@@ -1,17 +1,25 @@
|
||||
import { render, screen, act } from '@testing-library/react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { TelemetryApproval } from './telemetry-approval';
|
||||
|
||||
jest.mock('@vegaprotocol/logger', () => ({
|
||||
SentryInit: () => undefined,
|
||||
SentryClose: () => undefined,
|
||||
}));
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
|
||||
}));
|
||||
|
||||
describe('TelemetryApproval', () => {
|
||||
it('click on checkbox should be properly handled', () => {
|
||||
it('click on checkbox should be properly handled', async () => {
|
||||
const helpText = 'My help text';
|
||||
render(<TelemetryApproval helpText={helpText} />);
|
||||
expect(screen.getByRole('checkbox')).toHaveAttribute(
|
||||
'data-state',
|
||||
'unchecked'
|
||||
);
|
||||
act(() => {
|
||||
screen.getByRole('checkbox').click();
|
||||
});
|
||||
await userEvent.click(screen.getByRole('checkbox'));
|
||||
expect(screen.getByRole('checkbox')).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
|
||||
@@ -11,6 +11,7 @@ import { WelcomeNoticeDialog } from './welcome-notice-dialog';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { Networks } from '@vegaprotocol/environment';
|
||||
import { isTestEnv } from '@vegaprotocol/utils';
|
||||
|
||||
export const WelcomeDialog = () => {
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
@@ -31,9 +32,7 @@ export const WelcomeDialog = () => {
|
||||
);
|
||||
|
||||
const isRiskDialogNeeded =
|
||||
riskAccepted !== 'true' &&
|
||||
VEGA_ENV !== Networks.MAINNET &&
|
||||
!('Cypress' in window);
|
||||
riskAccepted !== 'true' && VEGA_ENV !== Networks.MAINNET && !isTestEnv();
|
||||
|
||||
const isWelcomeDialogNeeded = pathname === '/' || shouldDisplayWelcomeDialog;
|
||||
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
const windowOrDefault = (key: string, defaultValue?: string) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
if (window._env_ && window._env_[key]) {
|
||||
return window._env_[key];
|
||||
}
|
||||
}
|
||||
return defaultValue || '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Need to have default value as next in-lines environment variables. Cannot figure out dynamic keys.
|
||||
* So must provide the default with the key so that next can figure it out.
|
||||
*/
|
||||
export const ENV = {
|
||||
envName: windowOrDefault('NX_VEGA_ENV', process.env['NX_VEGA_ENV']),
|
||||
dsn: windowOrDefault(
|
||||
'NX_TRADING_SENTRY_DSN',
|
||||
process.env['NX_TRADING_SENTRY_DSN']
|
||||
),
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export * from './env';
|
||||
@@ -20,20 +20,8 @@ export const useMarketClickHandler = (replace = false) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const useMarketLiquidityClickHandler = (replace = false) => {
|
||||
const navigate = useNavigate();
|
||||
const { marketId } = useParams();
|
||||
const { pathname } = useLocation();
|
||||
const isLiquidityPage = pathname.match(/^\/liquidity\/(.+)/);
|
||||
return useCallback(
|
||||
(selectedId: string, metaKey?: boolean) => {
|
||||
const link = Links[Routes.LIQUIDITY](selectedId);
|
||||
if (metaKey) {
|
||||
window.open(`/#${link}`, '_blank');
|
||||
} else if (selectedId !== marketId || !isLiquidityPage) {
|
||||
navigate(link, { replace });
|
||||
}
|
||||
},
|
||||
[navigate, marketId, replace, isLiquidityPage]
|
||||
);
|
||||
export const useMarketLiquidityClickHandler = () => {
|
||||
return useCallback((selectedId: string, metaKey?: boolean) => {
|
||||
window.open(`/#/liquidity/${selectedId}`, metaKey ? '_blank' : '_self');
|
||||
}, []);
|
||||
};
|
||||
|
||||
@@ -12,22 +12,26 @@ jest.mock('@vegaprotocol/react-helpers', () => ({
|
||||
.fn()
|
||||
.mockImplementation(() => [false, mockSetValue, mockRemoveValue]),
|
||||
}));
|
||||
jest.mock('@vegaprotocol/environment', () => ({
|
||||
useEnvironment: () => ({ VEGA_ENV: 'test', SENTRY_DSN: 'sentry-dsn' }),
|
||||
}));
|
||||
|
||||
describe('useTelemetryApproval', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('hook should return proper array', () => {
|
||||
const res = renderHook(() => useTelemetryApproval());
|
||||
expect(res.result.current[0]).toEqual(false);
|
||||
expect(res.result.current[1]).toEqual(expect.any(Function));
|
||||
const { result } = renderHook(() => useTelemetryApproval());
|
||||
expect(result.current[0]).toEqual(false);
|
||||
expect(result.current[1]).toEqual(expect.any(Function));
|
||||
expect(useLocalStorage).toHaveBeenCalledWith(STORAGE_KEY);
|
||||
});
|
||||
|
||||
it('hook should init stuff properly', async () => {
|
||||
const res = renderHook(() => useTelemetryApproval());
|
||||
const { result } = renderHook(() => useTelemetryApproval());
|
||||
await act(() => {
|
||||
res.result.current[1](true);
|
||||
result.current[1](true);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(SentryInit).toHaveBeenCalled();
|
||||
@@ -36,9 +40,9 @@ describe('useTelemetryApproval', () => {
|
||||
});
|
||||
|
||||
it('hook should close stuff properly', async () => {
|
||||
const res = renderHook(() => useTelemetryApproval());
|
||||
const { result } = renderHook(() => useTelemetryApproval());
|
||||
await act(() => {
|
||||
res.result.current[1](false);
|
||||
result.current[1](false);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(SentryClose).toHaveBeenCalled();
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import { useLocalStorage } from '@vegaprotocol/react-helpers';
|
||||
import { useCallback } from 'react';
|
||||
import { SentryInit, SentryClose } from '@vegaprotocol/logger';
|
||||
import { ENV } from '../config';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
export const STORAGE_KEY = 'vega_telemetry_approval';
|
||||
|
||||
export const useTelemetryApproval = (): [
|
||||
value: boolean,
|
||||
setValue: (value: boolean) => void
|
||||
] => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
const [value, setValue, removeValue] = useLocalStorage(STORAGE_KEY);
|
||||
const setApprove = useCallback(
|
||||
(value: boolean) => {
|
||||
if (value) {
|
||||
SentryInit(ENV.dsn, ENV.envName);
|
||||
if (value && SENTRY_DSN) {
|
||||
SentryInit(SENTRY_DSN, VEGA_ENV);
|
||||
return setValue('1');
|
||||
}
|
||||
SentryClose();
|
||||
removeValue();
|
||||
},
|
||||
[setValue, removeValue]
|
||||
[setValue, removeValue, SENTRY_DSN, VEGA_ENV]
|
||||
);
|
||||
return [Boolean(value), setApprove];
|
||||
};
|
||||
|
||||
@@ -34,7 +34,6 @@ import { ViewingBanner } from '../components/viewing-banner';
|
||||
import { AnnouncementBanner, UpgradeBanner } from '../components/banner';
|
||||
import { AppLoader, DynamicLoader } from '../components/app-loader';
|
||||
import { Navbar } from '../components/navbar';
|
||||
import { ENV } from '../lib/config';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { activeOrdersProvider } from '@vegaprotocol/orders';
|
||||
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
|
||||
@@ -155,10 +154,11 @@ const PartyData = () => {
|
||||
};
|
||||
|
||||
const MaybeConnectEagerly = () => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
useVegaEagerConnect(Connectors);
|
||||
const [isTelemetryApproved] = useTelemetryApproval();
|
||||
useEthereumEagerConnect(
|
||||
isTelemetryApproved ? { dsn: ENV.dsn, env: ENV.envName } : {}
|
||||
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {}
|
||||
);
|
||||
|
||||
const { pubKey, connect } = useVegaWallet();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user