Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c543a0d507 | ||
|
|
6a07127185 | ||
|
|
45f0ba7c0e | ||
|
|
6f4ea68778 | ||
|
|
90dda20891 | ||
|
|
278eb01c2f | ||
|
|
c3157de146 | ||
|
|
d28db485c5 | ||
|
|
caa0076396 | ||
|
|
451da40ed2 | ||
|
|
47694e7b72 | ||
|
|
e01fb7f9ae | ||
|
|
ffcdfb6a6a | ||
|
|
86f0d013bb | ||
|
|
c12ac45890 | ||
|
|
47ce68455a | ||
|
|
c1675e4b49 | ||
|
|
f8c4d39b93 | ||
|
|
e55cf5ecc6 | ||
|
|
406cf22810 | ||
|
|
8ff46bab5b | ||
|
|
5f177785b6 | ||
|
|
06779f36ca | ||
|
|
29991015b5 | ||
|
|
071a6ff8bb | ||
|
|
5b8df4c414 | ||
|
|
ce6873fe54 | ||
|
|
d0e64364e3 | ||
|
|
737ffb4c35 | ||
|
|
53048ac8ef | ||
|
|
05ca1a09b9 | ||
|
|
94e398dd1c | ||
|
|
7c0a4f61e9 | ||
|
|
056d3215e8 | ||
|
|
a68b2093e4 | ||
|
|
f440f57be2 |
@@ -81,6 +81,22 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
main-branch-name: develop
|
main-branch-name: develop
|
||||||
|
|
||||||
|
# See affected apps
|
||||||
|
- name: See affected apps
|
||||||
|
run: |
|
||||||
|
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
|
||||||
|
python3 tools/ci/check-affected.py --github-ref="${{ github.ref }}" --branch-slug="$branch_slug" --event-name="${{ github.event_name }}"
|
||||||
|
|
||||||
|
- name: Verify script result
|
||||||
|
run: |
|
||||||
|
echo "Check outputs from script"
|
||||||
|
echo "projects: ${{ env.PROJECTS }}"
|
||||||
|
echo "projects-e2e: ${{ env.PROJECTS_E2E }}"
|
||||||
|
echo "preview_governance: ${{ env.PREVIEW_GOVERNANCE }}"
|
||||||
|
echo "preview_trading: ${{ env.PREVIEW_TRADING }}"
|
||||||
|
echo "preview_explorer: ${{ env.PREVIEW_EXPLORER }}"
|
||||||
|
echo "preview_tools: ${{ env.PREVIEW_TOOLS }}"
|
||||||
|
|
||||||
- name: Check formatting
|
- name: Check formatting
|
||||||
run: yarn nx format:check
|
run: yarn nx format:check
|
||||||
|
|
||||||
@@ -96,126 +112,6 @@ jobs:
|
|||||||
- name: Build affected
|
- name: Build affected
|
||||||
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
|
run: yarn nx affected:build || (yarn install && yarn nx affected:build)
|
||||||
|
|
||||||
# See affected apps
|
|
||||||
- name: See affected apps
|
|
||||||
run: |
|
|
||||||
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
|
|
||||||
branch_slug="$(echo '${{ github.head_ref || github.ref_name }}' | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | cut -c 1-50 )"
|
|
||||||
|
|
||||||
echo ">>>> debug"
|
|
||||||
echo "NX_BASE: ${{ env.NX_BASE }}"
|
|
||||||
echo "NX_HEAD: ${{ env.NX_HEAD }}"
|
|
||||||
echo "Affected: ${affected}"
|
|
||||||
echo "Branch slug: ${branch_slug}"
|
|
||||||
echo "Current ref: ${{ github.ref }}"
|
|
||||||
echo ">>>> eof debug"
|
|
||||||
|
|
||||||
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_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_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_array+=("explorer")
|
|
||||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
|
||||||
fi
|
|
||||||
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
|
|
||||||
|
|
||||||
# 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"
|
|
||||||
echo "Deploying tools on preview"
|
|
||||||
preview_tools=$(printf "https://%s.%s.vega.rocks" "tools" "$branch_slug")
|
|
||||||
|
|
||||||
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"
|
|
||||||
echo "Deploying tools on s3"
|
|
||||||
|
|
||||||
projects_array+=("multisig-signer")
|
|
||||||
fi
|
|
||||||
if echo "$affected" | grep -q static; then
|
|
||||||
echo "static is affected"
|
|
||||||
echo "Deploying static on s3"
|
|
||||||
|
|
||||||
projects_array+=("static")
|
|
||||||
fi
|
|
||||||
if echo "$affected" | grep -q ui-toolkit; then
|
|
||||||
echo "ui-toolkit is affected"
|
|
||||||
echo "Deploying ui-toolkit on s3"
|
|
||||||
|
|
||||||
projects_array+=("ui-toolkit")
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# if branch starts with release/ and ends with trading / governance or explorer - overwrite the array of affected projects with fixed single application
|
|
||||||
if [[ "${{ github.ref }}" == *release* ]]; then
|
|
||||||
echo ">> This is a relase branch"
|
|
||||||
case "${{ github.ref }}" in
|
|
||||||
*trading)
|
|
||||||
echo ">> Only trading will be deployed"
|
|
||||||
projects_array=(trading)
|
|
||||||
projects_e2e_array=(trading)
|
|
||||||
;;
|
|
||||||
*governance)
|
|
||||||
echo ">> Only governance will be deployed"
|
|
||||||
projects_array=(governance)
|
|
||||||
projects_e2e_array=(governance)
|
|
||||||
;;
|
|
||||||
*explorer)
|
|
||||||
echo ">> Only explorer will be deployed"
|
|
||||||
projects_array=(explorer)
|
|
||||||
projects_e2e_array=(explorer)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo ">> All apps will be deployed"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
|
|
||||||
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
|
|
||||||
echo PREVIEW_TOOLS=$preview_tools >> $GITHUB_ENV
|
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
projects: ${{ env.PROJECTS }}
|
projects: ${{ env.PROJECTS }}
|
||||||
projects-e2e: ${{ env.PROJECTS_E2E }}
|
projects-e2e: ${{ env.PROJECTS_E2E }}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ jobs:
|
|||||||
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
|
echo IS_MAINNET_RELEASE=false >> $GITHUB_ENV
|
||||||
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
|
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
|
||||||
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
|
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
|
||||||
|
echo IS_S3_RELEASE=false >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Is PR
|
- name: Is PR
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
@@ -49,6 +50,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
|
echo IS_IPFS_RELEASE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Is S3 Release
|
||||||
|
if: ${{ env.IS_IPFS_RELEASE == 'false' && github.event_name == 'push' }}
|
||||||
|
run: |
|
||||||
|
echo IS_S3_RELEASE=true >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
id: quemu
|
id: quemu
|
||||||
uses: docker/setup-qemu-action@v2
|
uses: docker/setup-qemu-action@v2
|
||||||
@@ -91,52 +97,13 @@ jobs:
|
|||||||
- name: Define dist variables
|
- name: Define dist variables
|
||||||
if: ${{ github.event_name == 'push' }}
|
if: ${{ github.event_name == 'push' }}
|
||||||
run: |
|
run: |
|
||||||
envName=''
|
python3 tools/ci/define-dist-variables.py --github-ref="${{ github.ref }}" --app="${{ matrix.app }}"
|
||||||
domain="vega.rocks"
|
|
||||||
bucketName=''
|
|
||||||
|
|
||||||
if [[ "${{ github.ref }}" =~ .*release/.* ]]; then
|
- name: Verify script result
|
||||||
# remove prefixing release/ and take the first string limited by - which is supposed to be name of the environment for releasing (format: release/testnet-trading)
|
if: ${{ github.event_name == 'push' }}
|
||||||
envName="$(echo ${{ github.ref }} | sed -e "s|refs/heads/release/||" | cut -d '-' -f 1 )"
|
run: |
|
||||||
elif [[ "${{ github.ref }}" =~ .*develop$ ]]; then
|
echo "BUCKET_NAME=${{ env.BUCKET_NAME }}"
|
||||||
envName="stagnet1"
|
echo "ENV_NAME=${{ env.ENV_NAME }}"
|
||||||
if [[ "${{ matrix.app }}" = "multisig-signer" ]]; then
|
|
||||||
envName="mainnet"
|
|
||||||
bucketName="tools.vega.xyz"
|
|
||||||
fi
|
|
||||||
if [[ "${{ matrix.app }}" = "static" ]]; then
|
|
||||||
envName="mainnet"
|
|
||||||
bucketName="static.vega.xyz"
|
|
||||||
fi
|
|
||||||
if [[ "${{ matrix.app }}" = "ui-toolkit" ]]; then
|
|
||||||
envName="mainnet"
|
|
||||||
bucketName="ui.vega.rocks"
|
|
||||||
fi
|
|
||||||
elif [[ "${{ github.ref }}" =~ .*mainnet$ ]]; then
|
|
||||||
envName="mainnet"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "${envName}" = "mainnet" ]]; then
|
|
||||||
domain="vega.xyz"
|
|
||||||
if [[ -z "${bucketName}" ]]; then
|
|
||||||
bucketName="${{ matrix.app }}.${domain}"
|
|
||||||
fi
|
|
||||||
elif [[ "${envName}" = "testnet" ]]; then
|
|
||||||
domain="fairground.wtf"
|
|
||||||
if [[ -z "${bucketName}" ]]; then
|
|
||||||
bucketName="${{ matrix.app }}.${domain}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${bucketName}" ]]; then
|
|
||||||
bucketName="${{ matrix.app }}.${envName}.${domain}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "bucket name: ${bucketName}"
|
|
||||||
echo "env name: ${envName}"
|
|
||||||
|
|
||||||
echo BUCKET_NAME=${bucketName} >> $GITHUB_ENV
|
|
||||||
echo ENV_NAME=${envName} >> $GITHUB_ENV
|
|
||||||
|
|
||||||
- name: Build local dist
|
- name: Build local dist
|
||||||
run: |
|
run: |
|
||||||
@@ -151,8 +118,12 @@ jobs:
|
|||||||
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
|
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
|
||||||
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
|
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
|
||||||
DIST_LOCATION=dist/storybook/ui-toolkit
|
DIST_LOCATION=dist/storybook/ui-toolkit
|
||||||
|
elif [ "${{ matrix.app }}" = "static" ]; then
|
||||||
|
yarn nx build static || (yarn install && yarn nx build static)
|
||||||
else
|
else
|
||||||
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
|
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
|
||||||
|
fi
|
||||||
|
if [[ -z "$DIST_LOCATION" ]]; then
|
||||||
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
||||||
fi
|
fi
|
||||||
mv $DIST_LOCATION dist-result
|
mv $DIST_LOCATION dist-result
|
||||||
@@ -245,7 +216,7 @@ jobs:
|
|||||||
- name: Publish dist to s3
|
- name: Publish dist to s3
|
||||||
uses: jakejarvis/s3-sync-action@master
|
uses: jakejarvis/s3-sync-action@master
|
||||||
# s3 releases are not happening for trading on mainnet - it's IPFS
|
# s3 releases are not happening for trading on mainnet - it's IPFS
|
||||||
if: ${{ env.IS_IPFS_RELEASE == 'false' }}
|
if: ${{ env.IS_S3_RELEASE == 'true' }}
|
||||||
with:
|
with:
|
||||||
args: --acl private --follow-symlinks --delete
|
args: --acl private --follow-symlinks --delete
|
||||||
env:
|
env:
|
||||||
@@ -256,11 +227,11 @@ jobs:
|
|||||||
SOURCE_DIR: 'dist-result'
|
SOURCE_DIR: 'dist-result'
|
||||||
|
|
||||||
- name: Install aws CLI
|
- name: Install aws CLI
|
||||||
if: ${{ env.IS_IPFS_RELEASE == 'false' }}
|
if: ${{ env.IS_S3_RELEASE == 'true' }}
|
||||||
uses: unfor19/install-aws-cli-action@master
|
uses: unfor19/install-aws-cli-action@master
|
||||||
|
|
||||||
- name: Perform cache invalidation
|
- name: Perform cache invalidation
|
||||||
if: ${{ env.IS_IPFS_RELEASE == 'false' }}
|
if: ${{ env.IS_S3_RELEASE == 'true' }}
|
||||||
env:
|
env:
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|||||||
@@ -16,5 +16,10 @@ NX_EXPLORER_NETWORK_PARAMETERS=1
|
|||||||
NX_EXPLORER_PARTIES=1
|
NX_EXPLORER_PARTIES=1
|
||||||
NX_EXPLORER_VALIDATORS=1
|
NX_EXPLORER_VALIDATORS=1
|
||||||
|
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
|
||||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||||
|
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||||
|
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||||
|
CYPRESS_FAUCET_URL=http://localhost:1790/api/v1/mint
|
||||||
|
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||||
|
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||||
|
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||||
|
|||||||
@@ -1,19 +1,130 @@
|
|||||||
//Tests set to skip until market bug for capsule checkpoint is fixed
|
context('Market page', { tags: '@regression' }, function () {
|
||||||
context.skip('Market page', { tags: '@regression' }, function () {
|
|
||||||
describe('Verify elements on page', function () {
|
describe('Verify elements on page', function () {
|
||||||
const marketHeaders = '[data-testid="markets-header"]';
|
const marketHeaders = 'markets-heading';
|
||||||
const marketNavigation = 'a[href="/markets"]';
|
const createdMarketId =
|
||||||
|
'2eab0e66545a789047561bc5a2e5cbc3b19eb708da41104e3cac2474ee36c4d4';
|
||||||
|
|
||||||
it('Markets page is displayed', function () {
|
before('Create market', function () {
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.get(marketNavigation).click();
|
cy.createMarket();
|
||||||
cy.common_validate_blocks_data_displayed(marketHeaders);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Markets page displayed on mobile', function () {
|
beforeEach('Get market id', function () {
|
||||||
|
cy.navigate_to('markets');
|
||||||
|
cy.get('[col-id="id"]').eq(1).invoke('text').as('createdMarketId');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Market displayed on market page', function () {
|
||||||
|
cy.navigate_to('markets');
|
||||||
|
cy.getByTestId(marketHeaders).should('be.visible');
|
||||||
|
cy.get(`[row-id="${this.createdMarketId}"]`)
|
||||||
|
.should('be.visible')
|
||||||
|
.within(() => {
|
||||||
|
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
|
||||||
|
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
|
||||||
|
cy.get_element_by_col_id('state').should('have.text', 'Pending');
|
||||||
|
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
|
||||||
|
cy.get_element_by_col_id('id').should(
|
||||||
|
'have.text',
|
||||||
|
this.createdMarketId
|
||||||
|
);
|
||||||
|
cy.get_element_by_col_id('actions')
|
||||||
|
.find('a')
|
||||||
|
.should('have.attr', 'href', `/markets/${this.createdMarketId}`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Able to go to market details page', function () {
|
||||||
|
cy.navigate_to('markets');
|
||||||
|
cy.get_element_by_col_id('actions').eq(1).click();
|
||||||
|
cy.getByTestId(marketHeaders).should('have.text', 'Test market 1');
|
||||||
|
cy.validate_element_from_table('Name', 'Test market 1');
|
||||||
|
cy.validate_element_from_table('Market ID', this.createdMarketId);
|
||||||
|
cy.validate_element_from_table('Trading Mode', 'Opening auction');
|
||||||
|
cy.validate_element_from_table('Market Decimal Places', '5');
|
||||||
|
cy.validate_element_from_table('Position Decimal Places', '5');
|
||||||
|
cy.validate_element_from_table('Settlement Asset Decimal Places', '5');
|
||||||
|
// Instrument
|
||||||
|
cy.validate_element_from_table('Market Name', 'Test market 1');
|
||||||
|
cy.validate_element_from_table('Code', 'TEST.24h');
|
||||||
|
cy.validate_element_from_table('Product Type', 'Future');
|
||||||
|
cy.validate_element_from_table('Quote Name', 'fUSDC');
|
||||||
|
// Settlement Asset
|
||||||
|
cy.validate_element_from_table('Quote Name', 'fUSDC');
|
||||||
|
cy.validate_element_from_table('Symbol', 'fUSDC');
|
||||||
|
cy.validate_element_from_table('Decimals', '5');
|
||||||
|
cy.validate_element_from_table('Quantum', '0.00');
|
||||||
|
cy.validate_element_from_table('Status', 'Enabled');
|
||||||
|
cy.validate_element_from_table('Max faucet amount', '10,000,000.00');
|
||||||
|
cy.validate_element_from_table(
|
||||||
|
'Infrastructure fee account balance',
|
||||||
|
'0.00'
|
||||||
|
);
|
||||||
|
cy.validate_element_from_table(
|
||||||
|
'Global reward pool account balance',
|
||||||
|
'0.00'
|
||||||
|
);
|
||||||
|
// Metadata
|
||||||
|
cy.validate_element_from_table('Sector', 'tech');
|
||||||
|
cy.validate_element_from_table('Source', 'docs.vega.xyz');
|
||||||
|
// Risk model
|
||||||
|
cy.validate_element_from_table('Tau', '0.0001140771161');
|
||||||
|
cy.validate_element_from_table('Risk Aversion Parameter', '0.01');
|
||||||
|
// Risk parameters
|
||||||
|
// cy.validate_element_from_table('R', '0.016')
|
||||||
|
cy.validate_element_from_table('Sigma', '0.5');
|
||||||
|
// Risk factors
|
||||||
|
cy.validate_element_from_table('Short', '0.0143218738374871');
|
||||||
|
cy.validate_element_from_table('Long', '0.0141450471498822');
|
||||||
|
// Price monitoring settings 1
|
||||||
|
cy.validate_element_from_table('Horizon Secs', '43,200');
|
||||||
|
cy.validate_element_from_table('Probability', '1');
|
||||||
|
cy.validate_element_from_table('Auction Extension Secs', '600');
|
||||||
|
// Liquidity Monitoring
|
||||||
|
cy.validate_element_from_table('Triggering Ratio', '0.7');
|
||||||
|
cy.validate_element_from_table('Time Window', '3,600');
|
||||||
|
cy.validate_element_from_table('Scaling Factor', '10');
|
||||||
|
// Liquidity
|
||||||
|
cy.validate_element_from_table('Target Stake', '0.00 fUSDC');
|
||||||
|
cy.validate_element_from_table('Supplied Stake', '0.00 fUSDC');
|
||||||
|
cy.validate_element_from_table('Market Value Proxy', '0.00 fUSDC');
|
||||||
|
// Liquidity price range
|
||||||
|
cy.validate_element_from_table(
|
||||||
|
'Liquidity Price Range',
|
||||||
|
'1,000.00% of mid price'
|
||||||
|
);
|
||||||
|
cy.validate_element_from_table('Lowest Price', '0.00 fUSDC');
|
||||||
|
cy.validate_element_from_table('Highest Price', '0.00 fUSDC');
|
||||||
|
|
||||||
|
cy.getByTestId('oracle-spec-links')
|
||||||
|
.should('have.attr', 'href')
|
||||||
|
.and(
|
||||||
|
'contain',
|
||||||
|
'/oracles/bf242aa5c9f64fcbb77808aa8582e73711519f4b35264eb797a80f1803590a24'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Able to view Json
|
||||||
|
cy.contains('View JSON').click();
|
||||||
|
cy.get('.language-json').should('exist');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Skipping due to resize observer loop limit error
|
||||||
|
it.skip('Markets page displayed on mobile', function () {
|
||||||
cy.common_switch_to_mobile_and_click_toggle();
|
cy.common_switch_to_mobile_and_click_toggle();
|
||||||
cy.get(marketNavigation).click();
|
cy.navigate_to('markets', true);
|
||||||
cy.common_validate_blocks_data_displayed(marketHeaders);
|
cy.getByTestId(marketHeaders).should('be.visible');
|
||||||
|
cy.get(`[row-id="${createdMarketId}"]`)
|
||||||
|
.should('be.visible')
|
||||||
|
.within(() => {
|
||||||
|
cy.get_element_by_col_id('code').should('have.text', 'TEST.24h');
|
||||||
|
cy.get_element_by_col_id('name').should('have.text', 'Test market 1');
|
||||||
|
cy.get_element_by_col_id('state').should('have.text', 'Pending');
|
||||||
|
cy.get_element_by_col_id('asset').should('have.text', 'fUSDC');
|
||||||
|
cy.get_element_by_col_id('id').should('have.text', createdMarketId);
|
||||||
|
cy.get_element_by_col_id('actions')
|
||||||
|
.find('a')
|
||||||
|
.should('have.attr', 'href', `/markets/${createdMarketId}`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ context.skip('Transactions page', function () {
|
|||||||
.first()
|
.first()
|
||||||
.click({ force: true });
|
.click({ force: true });
|
||||||
} else {
|
} else {
|
||||||
cy.slack('Unable to find any transactions on page');
|
cy.log('Unable to find any transactions on page');
|
||||||
cy.screenshot();
|
cy.screenshot();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,7 +78,7 @@ context.skip('Transactions page', function () {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
cy.slack('Unable to find any transactions on page');
|
cy.log('Unable to find any transactions on page');
|
||||||
cy.screenshot();
|
cy.screenshot();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ context('Validator page', { tags: '@smoke' }, function () {
|
|||||||
it('should be able to see validator tiles', function () {
|
it('should be able to see validator tiles', function () {
|
||||||
cy.getNodes().then((nodes) => {
|
cy.getNodes().then((nodes) => {
|
||||||
nodes.forEach((node) => {
|
nodes.forEach((node) => {
|
||||||
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
|
if (node.rankingScore.performanceScore > 0) {
|
||||||
|
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
|
||||||
|
} else {
|
||||||
|
cy.get(`[validator-id="${node.id}"]`).should('not.exist');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Cypress.Commands.add('switchToMobile', () => {
|
|||||||
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
|
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
|
||||||
cy.viewport('iphone-x');
|
cy.viewport('iphone-x');
|
||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.get('[data-testid="open-menu"]').click();
|
cy.get('[data-testid="button-menu-drawer"]').click();
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add('monitor_clipboard', () => {
|
Cypress.Commands.add('monitor_clipboard', () => {
|
||||||
@@ -88,3 +88,42 @@ Cypress.Commands.add('convert_number_to_max_four_decimal', (number) => {
|
|||||||
maximumFractionDigits: 4,
|
maximumFractionDigits: 4,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('navigate_to', (page, mobileView = false) => {
|
||||||
|
const navigation = {
|
||||||
|
transactions: '/txs',
|
||||||
|
blocks: '/blocks',
|
||||||
|
oracles: '/oracles',
|
||||||
|
validators: '/validators',
|
||||||
|
parties: '/parties',
|
||||||
|
assets: '/assets',
|
||||||
|
markets: '/markets',
|
||||||
|
governanceProposals: '/governance',
|
||||||
|
networkParameters: '/network-parameters',
|
||||||
|
genesisParameters: '/genesis',
|
||||||
|
};
|
||||||
|
|
||||||
|
const topLevelRoutes = ['transactions', 'blocks', 'oracles', 'validators'];
|
||||||
|
|
||||||
|
cy.getByTestId('navigation').within(() => {
|
||||||
|
if (!topLevelRoutes.includes(page) && mobileView === false) {
|
||||||
|
cy.getByTestId('other').first().click();
|
||||||
|
}
|
||||||
|
cy.get(`[href="${navigation[page]}"]`).first().click();
|
||||||
|
});
|
||||||
|
cy.url().should('include', navigation[page]);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add('get_element_by_col_id', (elementName) => {
|
||||||
|
cy.get(`[col-id="${elementName}"]`);
|
||||||
|
});
|
||||||
|
|
||||||
|
Cypress.Commands.add(
|
||||||
|
'validate_element_from_table',
|
||||||
|
(tableRowName, tableRowValue) => {
|
||||||
|
cy.contains(tableRowName)
|
||||||
|
.parentsUntil("[data-testid='key-value-table-row']")
|
||||||
|
.siblings()
|
||||||
|
.should('have.text', tableRowValue);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
|||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
||||||
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
||||||
NX_VEGA_ENV=MAINNET-MIRROR
|
NX_VEGA_ENV=MAINNET_MIRROR
|
||||||
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
|
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.rocks/rest
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
NX_VEGA_GOVERNANCE_URL=https://governance.mainnet-mirror.vega.rocks
|
||||||
|
|||||||
@@ -95,7 +95,10 @@ export const Header = () => {
|
|||||||
{mainItems.map(routeToNavigationItem)}
|
{mainItems.map(routeToNavigationItem)}
|
||||||
{groupedItems && groupedItems.length > 0 && (
|
{groupedItems && groupedItems.length > 0 && (
|
||||||
<NavigationItem>
|
<NavigationItem>
|
||||||
<NavigationTrigger isActive={Boolean(isOnOther)}>
|
<NavigationTrigger
|
||||||
|
isActive={Boolean(isOnOther)}
|
||||||
|
data-testid="other"
|
||||||
|
>
|
||||||
{t('Other')}
|
{t('Other')}
|
||||||
</NavigationTrigger>
|
</NavigationTrigger>
|
||||||
<NavigationContent>
|
<NavigationContent>
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
|||||||
hide: window.innerWidth <= BREAKPOINT_MD,
|
hide: window.innerWidth <= BREAKPOINT_MD,
|
||||||
headerName: t('Enactment date'),
|
headerName: t('Enactment date'),
|
||||||
field: 'terms.enactmentDatetime',
|
field: 'terms.enactmentDatetime',
|
||||||
valueFormatte: ({
|
valueFormatter: ({
|
||||||
value,
|
value,
|
||||||
}: VegaValueFormatterParams<
|
}: VegaValueFormatterParams<
|
||||||
ProposalListFieldsFragment,
|
ProposalListFieldsFragment,
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { t } from '@vegaprotocol/i18n';
|
||||||
|
import type { components } from '../../../../../types/explorer';
|
||||||
|
import { Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
|
export interface TxDetailsOrderIcebergDetailsProps {
|
||||||
|
iceberg: components['schemas']['v1IcebergOpts'];
|
||||||
|
size: components['schemas']['v1OrderSubmission']['size'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
|
.iceberg .sub {
|
||||||
|
vertical-align: bottom;
|
||||||
|
color: #954F01;
|
||||||
|
}
|
||||||
|
|
||||||
|
.iceberg .sup {
|
||||||
|
vertical-align: top;
|
||||||
|
color: #515E1E;
|
||||||
|
}
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visualises the iceberg details of an order, showing the minimum visible size,
|
||||||
|
* the full size and the peak size.
|
||||||
|
*/
|
||||||
|
export const TxOrderIcebergDetails = ({
|
||||||
|
iceberg,
|
||||||
|
size,
|
||||||
|
}: TxDetailsOrderIcebergDetailsProps) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="inline-block iceberg bg-vega-blue-300 border-vega-blue-350 border-[1px] text-[70%] font-mono py-[2px] pl-2 pr-3 height-[1em] leading-[1em] cursor-pointer"
|
||||||
|
style={{ clipPath: 'polygon(10% 0%, 100% 0%, 90% 100%, 0% 100%)' }}
|
||||||
|
>
|
||||||
|
<Tooltip description={t('Iceberg: Minimum visible size')}>
|
||||||
|
<span className="align-bottom text-vega-orange-650">
|
||||||
|
{iceberg.minimumVisibleSize || '-'}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip description={t('Iceberg: Total size')}>
|
||||||
|
<span className="text-sm text-vega-blue-600 mx-3">{size}</span>
|
||||||
|
</Tooltip>
|
||||||
|
<Tooltip description={t('Iceberg: Visible peak')}>
|
||||||
|
<span className="align-top text-vega-yellow-600">
|
||||||
|
{iceberg.peakSize || '-'}
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -8,6 +8,7 @@ import { txSignatureToDeterministicId } from '../lib/deterministic-ids';
|
|||||||
import DeterministicOrderDetails from '../../order-details/deterministic-order-details';
|
import DeterministicOrderDetails from '../../order-details/deterministic-order-details';
|
||||||
import Hash from '../../links/hash';
|
import Hash from '../../links/hash';
|
||||||
import { TxOrderPeggedReferenceRow } from './order/tx-order-peg';
|
import { TxOrderPeggedReferenceRow } from './order/tx-order-peg';
|
||||||
|
import { TxOrderIcebergDetails } from './order/tx-order-iceberg';
|
||||||
|
|
||||||
interface TxDetailsOrderProps {
|
interface TxDetailsOrderProps {
|
||||||
txData: BlockExplorerTransactionResult | undefined;
|
txData: BlockExplorerTransactionResult | undefined;
|
||||||
@@ -32,6 +33,8 @@ export const TxDetailsOrder = ({
|
|||||||
const marketId = txData.command.orderSubmission.marketId || '-';
|
const marketId = txData.command.orderSubmission.marketId || '-';
|
||||||
const reference = txData.command.orderSubmission.peggedOrder;
|
const reference = txData.command.orderSubmission.peggedOrder;
|
||||||
const side = txData.command.orderSubmission.side;
|
const side = txData.command.orderSubmission.side;
|
||||||
|
const iceberg = txData.command.orderSubmission.icebergOpts;
|
||||||
|
const size = txData.command.orderSubmission.size;
|
||||||
|
|
||||||
let deterministicId = '';
|
let deterministicId = '';
|
||||||
|
|
||||||
@@ -74,6 +77,14 @@ export const TxDetailsOrder = ({
|
|||||||
marketId={marketId}
|
marketId={marketId}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{iceberg ? (
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableCell>{t('Iceberg details')}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<TxOrderIcebergDetails iceberg={iceberg} size={size} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : null}
|
||||||
</TableWithTbody>
|
</TableWithTbody>
|
||||||
|
|
||||||
{deterministicId.length > 0 ? (
|
{deterministicId.length > 0 ? (
|
||||||
|
|||||||
@@ -166,6 +166,7 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
|
'text-white dark:text-white bg-vega-dark-150 dark:bg-vega-dark-250';
|
||||||
|
|
||||||
// This will get unwieldy and should probably produce a different colour of tag
|
// This will get unwieldy and should probably produce a different colour of tag
|
||||||
|
// Note that colours are currently arbitrary
|
||||||
if (type === 'Chain Event' && !!command?.chainEvent) {
|
if (type === 'Chain Event' && !!command?.chainEvent) {
|
||||||
type = getLabelForChainEvent(command.chainEvent);
|
type = getLabelForChainEvent(command.chainEvent);
|
||||||
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
colours = 'text-white dark-text-white bg-vega-pink dark:bg-vega-pink';
|
||||||
@@ -177,6 +178,9 @@ export const TxOrderType = ({ orderType, command }: TxOrderTypeProps) => {
|
|||||||
type = getLabelForProposal(command.proposalSubmission);
|
type = getLabelForProposal(command.proposalSubmission);
|
||||||
}
|
}
|
||||||
colours = 'text-black bg-vega-yellow';
|
colours = 'text-black bg-vega-yellow';
|
||||||
|
} else if (type === 'Order' && command) {
|
||||||
|
type = getLabelForOrderType(orderType, command);
|
||||||
|
colours = 'text-white dark-text-white bg-vega-blue dark:bg-vega-blue';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'Vote on Proposal') {
|
if (type === 'Vote on Proposal') {
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ const windowOrDefault = (key: string) => {
|
|||||||
return (process.env[key] as string) || '';
|
return (process.env[key] as string) || '';
|
||||||
};
|
};
|
||||||
|
|
||||||
const truthy = ['1', 'true'];
|
|
||||||
|
|
||||||
export const ENV = {
|
export const ENV = {
|
||||||
// Data sources
|
// Data sources
|
||||||
// Environment
|
// Environment
|
||||||
@@ -20,19 +18,6 @@ export const ENV = {
|
|||||||
governanceUrl: windowOrDefault('NX_VEGA_GOVERNANCE_URL'),
|
governanceUrl: windowOrDefault('NX_VEGA_GOVERNANCE_URL'),
|
||||||
vegaRepoUrl: windowOrDefault('NX_VEGA_REPO_URL'),
|
vegaRepoUrl: windowOrDefault('NX_VEGA_REPO_URL'),
|
||||||
},
|
},
|
||||||
flags: {
|
|
||||||
assets: truthy.includes(windowOrDefault('NX_EXPLORER_ASSETS')),
|
|
||||||
genesis: truthy.includes(windowOrDefault('NX_EXPLORER_GENESIS')),
|
|
||||||
governance: truthy.includes(windowOrDefault('NX_EXPLORER_GOVERNANCE')),
|
|
||||||
markets: truthy.includes(windowOrDefault('NX_EXPLORER_MARKETS')),
|
|
||||||
oracles: truthy.includes(windowOrDefault('NX_EXPLORER_ORACLES')),
|
|
||||||
txsList: truthy.includes(windowOrDefault('NX_EXPLORER_TXS_LIST')),
|
|
||||||
networkParameters: truthy.includes(
|
|
||||||
windowOrDefault('NX_EXPLORER_NETWORK_PARAMETERS')
|
|
||||||
),
|
|
||||||
parties: truthy.includes(windowOrDefault('NX_EXPLORER_PARTIES')),
|
|
||||||
validators: truthy.includes(windowOrDefault('NX_EXPLORER_VALIDATORS')),
|
|
||||||
},
|
|
||||||
addresses: {
|
addresses: {
|
||||||
feedback: windowOrDefault('NX_GITHUB_FEEDBACK_URL'),
|
feedback: windowOrDefault('NX_GITHUB_FEEDBACK_URL'),
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { ENV } from './env';
|
|
||||||
|
|
||||||
export default {
|
|
||||||
...ENV.flags,
|
|
||||||
};
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
import { Link, useParams } from 'react-router-dom';
|
import { Link, useParams } from 'react-router-dom';
|
||||||
import { DATA_SOURCES } from '../../../config';
|
|
||||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||||
import type { TendermintBlocksResponse } from '../tendermint-blocks-response';
|
|
||||||
import { RouteTitle } from '../../../components/route-title';
|
import { RouteTitle } from '../../../components/route-title';
|
||||||
import { TimeAgo } from '../../../components/time-ago';
|
import { TimeAgo } from '../../../components/time-ago';
|
||||||
import {
|
import {
|
||||||
@@ -14,7 +12,7 @@ import { TxsPerBlock } from '../../../components/txs/txs-per-block';
|
|||||||
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Routes } from '../../route-names';
|
import { Routes } from '../../route-names';
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
import { useBlockInfo } from '@vegaprotocol/tendermint';
|
||||||
import { NodeLink } from '../../../components/links';
|
import { NodeLink } from '../../../components/links';
|
||||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||||
import EmptyList from '../../../components/empty-list/empty-list';
|
import EmptyList from '../../../components/empty-list/empty-list';
|
||||||
@@ -24,10 +22,7 @@ const Block = () => {
|
|||||||
useDocumentTitle(['Blocks', `Block #${block}`]);
|
useDocumentTitle(['Blocks', `Block #${block}`]);
|
||||||
const {
|
const {
|
||||||
state: { data: blockData, loading, error },
|
state: { data: blockData, loading, error },
|
||||||
} = useFetch<TendermintBlocksResponse>(
|
} = useBlockInfo(Number(block));
|
||||||
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`,
|
|
||||||
{ cache: 'force-cache' }
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export const PartyBlockStake = ({
|
|||||||
{p?.stakingSummary.currentStakeAvailable ? (
|
{p?.stakingSummary.currentStakeAvailable ? (
|
||||||
<KeyValueTable>
|
<KeyValueTable>
|
||||||
<KeyValueTableRow noBorder={true}>
|
<KeyValueTableRow noBorder={true}>
|
||||||
<div>{t('Available stake')}</div>
|
<div>{t('Associated to key')}</div>
|
||||||
<div>
|
<div>
|
||||||
<GovernanceAssetBalance
|
<GovernanceAssetBalance
|
||||||
price={p.stakingSummary.currentStakeAvailable}
|
price={p.stakingSummary.currentStakeAvailable}
|
||||||
@@ -62,7 +62,7 @@ export const PartyBlockStake = ({
|
|||||||
</div>
|
</div>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
<KeyValueTableRow noBorder={true}>
|
<KeyValueTableRow noBorder={true}>
|
||||||
<div>{t('Active stake')}</div>
|
<div>{t('Staked to validator')}</div>
|
||||||
<div>
|
<div>
|
||||||
<GovernanceAssetBalance price={linkedStake || '0'} />
|
<GovernanceAssetBalance price={linkedStake || '0'} />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const Party = () => {
|
|||||||
text: t('Go back'),
|
text: t('Go back'),
|
||||||
action: () => navigate(-1),
|
action: () => navigate(-1),
|
||||||
className: 'py-1',
|
className: 'py-1',
|
||||||
size: 'sm',
|
size: 'small',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import { Block } from './blocks/id';
|
|||||||
import { Blocks } from './blocks/home';
|
import { Blocks } from './blocks/home';
|
||||||
import { Tx } from './txs/id';
|
import { Tx } from './txs/id';
|
||||||
import { TxsList } from './txs/home';
|
import { TxsList } from './txs/home';
|
||||||
import flags from '../config/flags';
|
|
||||||
import { t } from '@vegaprotocol/i18n';
|
import { t } from '@vegaprotocol/i18n';
|
||||||
import { Routes } from './route-names';
|
import { Routes } from './route-names';
|
||||||
import { NetworkParameters } from './network-parameters';
|
import { NetworkParameters } from './network-parameters';
|
||||||
@@ -30,6 +29,7 @@ import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
|||||||
import { remove0x } from '@vegaprotocol/utils';
|
import { remove0x } from '@vegaprotocol/utils';
|
||||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||||
import { Disclaimer } from './pages/disclaimer';
|
import { Disclaimer } from './pages/disclaimer';
|
||||||
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
|
||||||
export type Navigable = {
|
export type Navigable = {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -59,7 +59,7 @@ type Route = RouteItem & {
|
|||||||
children?: RouteItem[];
|
children?: RouteItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const partiesRoutes: Route[] = flags.parties
|
const partiesRoutes: Route[] = FLAGS.EXPLORER_PARTIES
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.PARTIES,
|
path: Routes.PARTIES,
|
||||||
@@ -119,7 +119,7 @@ const partiesRoutes: Route[] = flags.parties
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const assetsRoutes: Route[] = flags.assets
|
const assetsRoutes: Route[] = FLAGS.EXPLORER_ASSETS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.ASSETS,
|
path: Routes.ASSETS,
|
||||||
@@ -147,7 +147,7 @@ const assetsRoutes: Route[] = flags.assets
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const genesisRoutes: Route[] = flags.genesis
|
const genesisRoutes: Route[] = FLAGS.EXPLORER_GENESIS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.GENESIS,
|
path: Routes.GENESIS,
|
||||||
@@ -163,7 +163,7 @@ const genesisRoutes: Route[] = flags.genesis
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const governanceRoutes: Route[] = flags.governance
|
const governanceRoutes: Route[] = FLAGS.EXPLORER_GOVERNANCE
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.GOVERNANCE,
|
path: Routes.GOVERNANCE,
|
||||||
@@ -179,7 +179,7 @@ const governanceRoutes: Route[] = flags.governance
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const marketsRoutes: Route[] = flags.markets
|
const marketsRoutes: Route[] = FLAGS.EXPLORER_MARKETS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.MARKETS,
|
path: Routes.MARKETS,
|
||||||
@@ -207,7 +207,7 @@ const marketsRoutes: Route[] = flags.markets
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const networkParametersRoutes: Route[] = flags.networkParameters
|
const networkParametersRoutes: Route[] = FLAGS.EXPLORER_NETWORK_PARAMETERS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.NETWORK_PARAMETERS,
|
path: Routes.NETWORK_PARAMETERS,
|
||||||
@@ -225,7 +225,7 @@ const networkParametersRoutes: Route[] = flags.networkParameters
|
|||||||
]
|
]
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const validators: Route[] = flags.validators
|
const validators: Route[] = FLAGS.EXPLORER_VALIDATORS
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
path: Routes.VALIDATORS,
|
path: Routes.VALIDATORS,
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ query ExplorerNodes {
|
|||||||
stakedByDelegates
|
stakedByDelegates
|
||||||
stakedTotal
|
stakedTotal
|
||||||
pendingStake
|
pendingStake
|
||||||
|
rankingScore {
|
||||||
|
performanceScore
|
||||||
|
}
|
||||||
epochData {
|
epochData {
|
||||||
total
|
total
|
||||||
offline
|
offline
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
|
|||||||
export type ExplorerNodesQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
export type ExplorerNodesQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
|
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, rankingScore: { __typename?: 'RankingScore', performanceScore: string }, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
|
||||||
|
|
||||||
|
|
||||||
export const ExplorerNodesDocument = gql`
|
export const ExplorerNodesDocument = gql`
|
||||||
@@ -27,6 +27,9 @@ export const ExplorerNodesDocument = gql`
|
|||||||
stakedByDelegates
|
stakedByDelegates
|
||||||
stakedTotal
|
stakedTotal
|
||||||
pendingStake
|
pendingStake
|
||||||
|
rankingScore {
|
||||||
|
performanceScore
|
||||||
|
}
|
||||||
epochData {
|
epochData {
|
||||||
total
|
total
|
||||||
offline
|
offline
|
||||||
|
|||||||
@@ -91,7 +91,15 @@ export const ValidatorsPage = () => {
|
|||||||
const { data: tmData } = useTendermintValidators(5000);
|
const { data: tmData } = useTendermintValidators(5000);
|
||||||
const { data, loading, error, refetch } = useExplorerNodesQuery();
|
const { data, loading, error, refetch } = useExplorerNodesQuery();
|
||||||
|
|
||||||
const validators = compact(data?.nodesConnection.edges?.map((e) => e?.node));
|
const validators = compact(
|
||||||
|
data?.nodesConnection.edges
|
||||||
|
?.map((e) => e?.node)
|
||||||
|
.filter(
|
||||||
|
(node) =>
|
||||||
|
node?.rankingScore?.performanceScore &&
|
||||||
|
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
// voting power
|
// voting power
|
||||||
const powers = compact(tmData?.result.validators).map(
|
const powers = compact(tmData?.result.validators).map(
|
||||||
|
|||||||
@@ -144,7 +144,7 @@ context(
|
|||||||
closeStakingDialog();
|
closeStakingDialog();
|
||||||
navigateTo(navigation.validators);
|
navigateTo(navigation.validators);
|
||||||
cy.getByTestId(viewStakedByMeToggle).click();
|
cy.getByTestId(viewStakedByMeToggle).click();
|
||||||
cy.getByTestId(userStakeBtn).should('have.length', 2);
|
cy.getByTestId(userStakeBtn, epochTimeout).should('have.length', 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Able to stake against a validator - using vega from vesting contract', function () {
|
it('Able to stake against a validator - using vega from vesting contract', function () {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
|||||||
cy.getByTestId('home-proposals').within(() => {
|
cy.getByTestId('home-proposals').within(() => {
|
||||||
cy.get('[href="/proposals"]')
|
cy.get('[href="/proposals"]')
|
||||||
.should('exist')
|
.should('exist')
|
||||||
.and('have.text', 'Browse, vote, and propose');
|
.and('have.text', 'See all proposals');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ context(
|
|||||||
);
|
);
|
||||||
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
cy.getByTestId('protocol-upgrade-proposal-status').should(
|
||||||
'have.text',
|
'have.text',
|
||||||
'Approved by validators '
|
'Approved by validators'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -227,5 +227,42 @@ context(
|
|||||||
cy.getByTestId(networkUpgradeProposalListItem).should('not.exist');
|
cy.getByTestId(networkUpgradeProposalListItem).should('not.exist');
|
||||||
cy.getByTestId(closedProposalToggle).should('not.exist');
|
cy.getByTestId(closedProposalToggle).should('not.exist');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should display network upgrade banner with estimate', function () {
|
||||||
|
mockNetworkUpgradeProposal();
|
||||||
|
cy.visit('/');
|
||||||
|
cy.getByTestId('banners').within(() => {
|
||||||
|
cy.get('div')
|
||||||
|
.should('contain.text', 'The network will upgrade to v1 in ')
|
||||||
|
.and(
|
||||||
|
'contain.text',
|
||||||
|
'Trading activity will be interrupted, manage your risk appropriately.'
|
||||||
|
);
|
||||||
|
cy.getByTestId('external-link')
|
||||||
|
.should('have.attr', 'href')
|
||||||
|
.and('contain', '/proposals/protocol-upgrade/v1');
|
||||||
|
});
|
||||||
|
|
||||||
|
// estimate does not display possibly due to mocks or Cypress unless the proposal is clicked on several times
|
||||||
|
// By default the application waits for 10 blocks until showing estimate - roughly 10 seconds
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
// eslint-disable-next-line cypress/no-unnecessary-waiting
|
||||||
|
cy.wait(3000);
|
||||||
|
navigateTo(navigation.proposals);
|
||||||
|
cy.getByTestId(networkUpgradeProposalListItem)
|
||||||
|
.first()
|
||||||
|
.find('[data-testid="view-proposal-btn"]')
|
||||||
|
.click();
|
||||||
|
}
|
||||||
|
cy.getByTestId('upgrade-proposal-estimate')
|
||||||
|
.invoke('text')
|
||||||
|
.as('displayedEstimate');
|
||||||
|
cy.get('@displayedEstimate').then((estimateText) => {
|
||||||
|
// Estimated time should automatically update every second
|
||||||
|
cy.getByTestId('protocol-upgrade-time')
|
||||||
|
.invoke('text')
|
||||||
|
.should('not.eq', estimateText);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -174,7 +174,8 @@ export function clickOnValidatorFromList(
|
|||||||
cy.get(`[row-id="${validatorNumber}"]`)
|
cy.get(`[row-id="${validatorNumber}"]`)
|
||||||
.should('be.visible')
|
.should('be.visible')
|
||||||
.find(stakeValidatorListName)
|
.find(stakeValidatorListName)
|
||||||
.click();
|
.as('validatorOnList');
|
||||||
|
cy.get('@validatorOnList').click();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,12 @@ NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/annou
|
|||||||
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
LC_ALL="en_US.UTF-8"
|
LC_ALL="en_US.UTF-8"
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=true
|
||||||
@@ -19,6 +19,11 @@ NX_DELEGATIONS_PAGINATION=50
|
|||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
NX_VEGA_REST_URL=http://localhost:3008/api/v2/
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=http://localhost:26617
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||||
|
|
||||||
#Test configuration variables
|
#Test configuration variables
|
||||||
CYPRESS_FAIRGROUND=false
|
CYPRESS_FAIRGROUND=false
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=false
|
||||||
@@ -13,3 +13,9 @@ NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
|
|||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.devnet1.vega.xyz/api/v2/
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://tm.be.devnet1.vega.xyz/
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.devnet1.vega.xyz/websocket
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=true
|
||||||
@@ -14,3 +14,9 @@ NX_DELEGATIONS_PAGINATION=50
|
|||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
NX_VEGA_REST_URL=https://api.vega.community/api/v2/
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://be.vega.community
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=false
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
# App configuration variables
|
# App configuration variables
|
||||||
NX_VEGA_ENV=MAINNET-MIRROR
|
NX_VEGA_ENV=MAINNET_MIRROR
|
||||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/mainnet-mirror/vegawallet-mainnet-mirror.toml
|
||||||
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
NX_VEGA_URL=https://api.mainnet-mirror.vega.rocks/graphql
|
||||||
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz","MAINNET-MIRROR":"https://governance.mainnet-mirror.vega.rocks","STAGNET1":"https://trading.stagnet1.vega.rocks"}'
|
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.governance.vega.xyz","TESTNET":"https://governance.fairground.wtf","MAINNET":"https://governance.vega.xyz","MAINNET_MIRROR":"https://governance.mainnet-mirror.vega.rocks","STAGNET1":"https://trading.stagnet1.vega.rocks"}'
|
||||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||||
@@ -13,3 +13,9 @@ NX_DELEGATIONS_PAGINATION=50
|
|||||||
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-mirror-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
NX_VEGA_REST_URL=https://api.mainnet-mirror.vega.rocks/api/v2/
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://be.mainnet-mirror.vega.rocks
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=false
|
||||||
@@ -9,3 +9,9 @@ NX_DELEGATIONS_PAGINATION=50
|
|||||||
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
|
||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n00.stagnet1.vega.xyz/api/v2/
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.rocks
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=true
|
||||||
@@ -14,3 +14,9 @@ NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
|
|||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
NX_VEGA_REST_URL=https://api.n07.testnet.vega.xyz/api/v2/
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=true
|
||||||
@@ -11,3 +11,9 @@ NX_VEGA_EXPLORER_URL=https://explorer.validators-testnet.vega.rocks/
|
|||||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||||
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
NX_VEGA_REST_URL=https://api-validators-testnet.vega.rocks/api/v2/
|
||||||
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40@o286262.ingest.sentry.io/5882996
|
||||||
|
|
||||||
|
NX_TENDERMINT_URL=https://tm.be.validators-testnet.vega.rocks
|
||||||
|
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||||
|
|
||||||
|
# Cosmic elevator flags
|
||||||
|
NX_SUCCESSOR_MARKETS=false
|
||||||
@@ -2,14 +2,13 @@ import * as Sentry from '@sentry/react';
|
|||||||
import { toBigNum } from '@vegaprotocol/utils';
|
import { toBigNum } from '@vegaprotocol/utils';
|
||||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||||
import { useEnvironment } from '@vegaprotocol/environment';
|
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import { useWeb3React } from '@web3-react/core';
|
import { useWeb3React } from '@web3-react/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
import { SplashError } from './components/splash-error';
|
import { SplashError } from './components/splash-error';
|
||||||
import { SplashLoader } from './components/splash-loader';
|
import { SplashLoader } from './components/splash-loader';
|
||||||
import { Flags } from './config';
|
|
||||||
import {
|
import {
|
||||||
AppStateActionType,
|
AppStateActionType,
|
||||||
useAppState,
|
useAppState,
|
||||||
@@ -80,7 +79,7 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!Flags.NETWORK_DOWN) {
|
if (!FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
}, [token, appDispatch, staking, vesting]);
|
}, [token, appDispatch, staking, vesting]);
|
||||||
@@ -148,7 +147,7 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Only begin polling if network limits flag is set, as this is a new API not yet on mainnet 7/3/22
|
// Only begin polling if network limits flag is set, as this is a new API not yet on mainnet 7/3/22
|
||||||
if (Flags.NETWORK_LIMITS) {
|
if (FLAGS.GOVERNANCE_NETWORK_LIMITS) {
|
||||||
getNetworkLimits();
|
getNetworkLimits();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +156,7 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
|||||||
};
|
};
|
||||||
}, [appDispatch, VEGA_URL, t]);
|
}, [appDispatch, VEGA_URL, t]);
|
||||||
|
|
||||||
if (Flags.NETWORK_DOWN) {
|
if (FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||||
return (
|
return (
|
||||||
<Splash>
|
<Splash>
|
||||||
<SplashError />
|
<SplashError />
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './json-diff';
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { render, screen, cleanup } from '@testing-library/react';
|
||||||
|
import { JsonDiff } from './json-diff';
|
||||||
|
|
||||||
|
describe('JsonDiff', () => {
|
||||||
|
afterEach(cleanup);
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
render(<JsonDiff left={{}} right={{}} />);
|
||||||
|
expect(screen.getByTestId('json-diff')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows the correct message when both objects are identical', () => {
|
||||||
|
render(<JsonDiff left={{}} right={{}} />);
|
||||||
|
expect(screen.getByText('Data is identical')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not show the "identical" message when both objects are not identical', () => {
|
||||||
|
render(
|
||||||
|
<JsonDiff
|
||||||
|
left={{
|
||||||
|
name: 'test',
|
||||||
|
}}
|
||||||
|
right={{
|
||||||
|
name: 'test2',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(screen.queryByText('Data is identical')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { useState, useEffect } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { formatters, create } from 'jsondiffpatch';
|
||||||
|
import 'jsondiffpatch/dist/formatters-styles/html.css';
|
||||||
|
import 'jsondiffpatch/dist/formatters-styles/annotated.css';
|
||||||
|
|
||||||
|
export type JsonValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| JsonValue[]
|
||||||
|
| { [key: string]: JsonValue };
|
||||||
|
|
||||||
|
interface JsonDiffProps {
|
||||||
|
left: JsonValue;
|
||||||
|
right: JsonValue;
|
||||||
|
objectHash?: (obj: unknown) => string | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JsonDiff = ({ right, left, objectHash }: JsonDiffProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [html, setHtml] = useState<string | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const delta = create({
|
||||||
|
objectHash,
|
||||||
|
}).diff(left, right);
|
||||||
|
|
||||||
|
if (delta) {
|
||||||
|
const deltaHtml = formatters.html.format(delta, left);
|
||||||
|
|
||||||
|
formatters.html.hideUnchanged();
|
||||||
|
|
||||||
|
setHtml(deltaHtml);
|
||||||
|
} else {
|
||||||
|
setHtml(undefined);
|
||||||
|
}
|
||||||
|
}, [right, left, objectHash]);
|
||||||
|
|
||||||
|
return html ? (
|
||||||
|
<div data-testid="json-diff" dangerouslySetInnerHTML={{ __html: html }} />
|
||||||
|
) : (
|
||||||
|
<p data-testid="json-diff">{t('dataIsIdentical')}</p>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -18,6 +18,10 @@ import { VegaWallet } from '../vega-wallet';
|
|||||||
import { useLocation, useMatch } from 'react-router-dom';
|
import { useLocation, useMatch } from 'react-router-dom';
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
|
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
|
||||||
|
import {
|
||||||
|
ProtocolUpgradeCountdown,
|
||||||
|
ProtocolUpgradeCountdownMode,
|
||||||
|
} from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
export const SettingsLink = () => {
|
export const SettingsLink = () => {
|
||||||
const { open, isOpen, close } = useTelemetryDialog();
|
const { open, isOpen, close } = useTelemetryDialog();
|
||||||
@@ -61,7 +65,14 @@ export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
|
|||||||
appName="Governance"
|
appName="Governance"
|
||||||
theme={theme}
|
theme={theme}
|
||||||
breakpoints={[458, 959]}
|
breakpoints={[458, 959]}
|
||||||
actions={<SettingsLink />}
|
actions={
|
||||||
|
<>
|
||||||
|
<SettingsLink />
|
||||||
|
<ProtocolUpgradeCountdown
|
||||||
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<NavigationList
|
<NavigationList
|
||||||
className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2"
|
className="[.drawer-content_&]:border-b [.drawer-content_&]:border-b-vega-light-200 dark:[.drawer-content_&]:border-b-vega-dark-200 [.drawer-content_&]:pb-8 [.drawer-content_&]:mb-2"
|
||||||
|
|||||||
@@ -4,7 +4,11 @@ import type { ReactNode } from 'react';
|
|||||||
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
import { AnnouncementBanner } from '@vegaprotocol/announcements';
|
||||||
import { Nav } from '../nav';
|
import { Nav } from '../nav';
|
||||||
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
import { Networks, useEnvironment } from '@vegaprotocol/environment';
|
||||||
import React from 'react';
|
import {
|
||||||
|
ProtocolUpgradeCountdownMode,
|
||||||
|
ProtocolUpgradeProposalNotification,
|
||||||
|
} from '@vegaprotocol/proposals';
|
||||||
|
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
interface AppLayoutProps {
|
interface AppLayoutProps {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -31,8 +35,23 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
|
||||||
|
<NotificationsContainer />
|
||||||
</div>
|
</div>
|
||||||
<div className={AppLayoutClasses}>{children}</div>
|
<div className={AppLayoutClasses}>{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const NotificationsContainer = () => {
|
||||||
|
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
||||||
|
return (
|
||||||
|
<div data-testid="banners">
|
||||||
|
<ProtocolUpgradeProposalNotification
|
||||||
|
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||||
|
/>
|
||||||
|
{isReadOnly ? (
|
||||||
|
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
export interface TemplateSidebarProps {
|
export interface TemplateSidebarProps {
|
||||||
@@ -8,24 +6,18 @@ export interface TemplateSidebarProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
|
||||||
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
|
||||||
{isReadOnly ? (
|
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
|
||||||
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
|
{children}
|
||||||
) : null}
|
</main>
|
||||||
<div className="w-full border-b border-neutral-700 lg:grid lg:grid-rows-[1fr] lg:grid-cols-[1fr_450px]">
|
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
||||||
<main className="max-w-[100vw] col-start-1 p-4 overflow-auto">
|
{sidebar.map((Component, i) => (
|
||||||
{children}
|
<section className="mb-4 last:mb-0" key={i}>
|
||||||
</main>
|
{Component}
|
||||||
<aside className="col-start-2 row-start-1 row-span-2 hidden lg:block p-4 bg-banner bg-contain border-l border-neutral-700">
|
</section>
|
||||||
{sidebar.map((Component, i) => (
|
))}
|
||||||
<section className="mb-4 last:mb-0" key={i}>
|
</aside>
|
||||||
{Component}
|
</div>
|
||||||
</section>
|
|
||||||
))}
|
|
||||||
</aside>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export const ContractAddresses: {
|
|||||||
claimAddress: customClaimAddress ?? '0x0',
|
claimAddress: customClaimAddress ?? '0x0',
|
||||||
lockedAddress: customLockedAddress ?? '0x0',
|
lockedAddress: customLockedAddress ?? '0x0',
|
||||||
},
|
},
|
||||||
|
MAINNET_MIRROR: {
|
||||||
|
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
|
||||||
|
lockedAddress: '0x0', // TODO not deployed to this env
|
||||||
|
},
|
||||||
DEVNET: {
|
DEVNET: {
|
||||||
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
|
claimAddress: '0x8Cef746ab7C83B61F6461cC92882bD61AB65a994', // TODO not deployed to this env, but random address so app doesn't error
|
||||||
lockedAddress: '0x0', // TODO not deployed to this env
|
lockedAddress: '0x0', // TODO not deployed to this env
|
||||||
@@ -63,12 +67,6 @@ export const ENV = {
|
|||||||
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
localProviderUrl: windowOrDefault('NX_LOCAL_PROVIDER_URL'),
|
||||||
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
|
delegationsPagination: windowOrDefault('NX_DELEGATIONS_PAGINATION'),
|
||||||
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
rest: windowOrDefault('NX_VEGA_REST_URL'),
|
||||||
flags: {
|
|
||||||
NETWORK_DOWN: TRUTHY.includes(windowOrDefault('NX_NETWORK_DOWN')),
|
|
||||||
MOCK: TRUTHY.includes(windowOrDefault('NX_MOCKED')),
|
|
||||||
FAIRGROUND: TRUTHY.includes(windowOrDefault('NX_FAIRGROUND')),
|
|
||||||
NETWORK_LIMITS: TRUTHY.includes(windowOrDefault('NX_NETWORK_LIMITS')),
|
|
||||||
},
|
|
||||||
addresses:
|
addresses:
|
||||||
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
ContractAddresses[(envName === 'local' ? 'CUSTOM' : envName) as Networks],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
import { ENV } from './env';
|
|
||||||
|
|
||||||
export const Flags = {
|
|
||||||
...ENV.flags,
|
|
||||||
};
|
|
||||||
@@ -1,2 +1 @@
|
|||||||
export * from './flags';
|
|
||||||
export * from './env';
|
export * from './env';
|
||||||
|
|||||||
@@ -166,6 +166,7 @@
|
|||||||
"proposedEnactment": "Proposed enactment",
|
"proposedEnactment": "Proposed enactment",
|
||||||
"Enacted": "Enacted",
|
"Enacted": "Enacted",
|
||||||
"enactedOn": "Enacted on",
|
"enactedOn": "Enacted on",
|
||||||
|
"enactedOn{{date}}": "Enacted on {{enactmentDate}}",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"state": "State",
|
"state": "State",
|
||||||
"shouldPass": "Should pass",
|
"shouldPass": "Should pass",
|
||||||
@@ -185,6 +186,7 @@
|
|||||||
"proposedOn": "Proposed on",
|
"proposedOn": "Proposed on",
|
||||||
"proposedBy": "Proposed by",
|
"proposedBy": "Proposed by",
|
||||||
"toEnactOn": "Enacts on",
|
"toEnactOn": "Enacts on",
|
||||||
|
"enactsOn{{date}}": "Enacts on {{enactmentDate}}",
|
||||||
"closesOn": "Closes on",
|
"closesOn": "Closes on",
|
||||||
"closedOn": "Closed on",
|
"closedOn": "Closed on",
|
||||||
"errorDetails": "Error details",
|
"errorDetails": "Error details",
|
||||||
@@ -217,6 +219,7 @@
|
|||||||
"votingThresholdInfo": "If the token vote passes the participation threshold it will be the deciding vote. If not, the outcome will be determined by liquidity providers on this market.",
|
"votingThresholdInfo": "If the token vote passes the participation threshold it will be the deciding vote. If not, the outcome will be determined by liquidity providers on this market.",
|
||||||
"noGovernanceTokens": "You need some VEGA tokens to participate in governance",
|
"noGovernanceTokens": "You need some VEGA tokens to participate in governance",
|
||||||
"youVoted": "You voted",
|
"youVoted": "You voted",
|
||||||
|
"voted": "Voted",
|
||||||
"changeVote": "Change vote",
|
"changeVote": "Change vote",
|
||||||
"txRequested": "Confirm transaction in wallet",
|
"txRequested": "Confirm transaction in wallet",
|
||||||
"votePending": "Casting vote",
|
"votePending": "Casting vote",
|
||||||
@@ -628,7 +631,6 @@
|
|||||||
"pendingDescriptionLinkText": "set up and run a node on Vega",
|
"pendingDescriptionLinkText": "set up and run a node on Vega",
|
||||||
"pendingDescription2": ". A node can move from being a candidate into standby based on how much nomination it attracts, assuming it has proven reliability by sending heartbeats to the network.",
|
"pendingDescription2": ". A node can move from being a candidate into standby based on how much nomination it attracts, assuming it has proven reliability by sending heartbeats to the network.",
|
||||||
"n/a": "N/A",
|
"n/a": "N/A",
|
||||||
"Set to": "Set to",
|
|
||||||
"pass": "pass",
|
"pass": "pass",
|
||||||
"fail": "fail",
|
"fail": "fail",
|
||||||
"New asset": "New asset",
|
"New asset": "New asset",
|
||||||
@@ -788,7 +790,7 @@
|
|||||||
"overstakedPenalty": "Overstaked penalty",
|
"overstakedPenalty": "Overstaked penalty",
|
||||||
"multisigPenalty": "Multisig 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.",
|
"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",
|
"homeProposalsButtonText": "See all proposals",
|
||||||
"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.",
|
"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.",
|
||||||
"homeValidatorsButtonText": "Browse, and stake",
|
"homeValidatorsButtonText": "Browse, and stake",
|
||||||
"homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.",
|
"homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.",
|
||||||
@@ -837,5 +839,21 @@
|
|||||||
"RejectedProposals": "Rejected proposals",
|
"RejectedProposals": "Rejected proposals",
|
||||||
"networkGovernance": "Network governance",
|
"networkGovernance": "Network governance",
|
||||||
"networkUpgrades": "Network upgrades",
|
"networkUpgrades": "Network upgrades",
|
||||||
"assetSpecification": "Asset specification"
|
"assetSpecification": "Asset specification",
|
||||||
|
"viewDetails": "View details",
|
||||||
|
"vegaGovernance": "Vega Governance",
|
||||||
|
"latestProposals": "Latest proposals",
|
||||||
|
"vegaToken": "VEGA Token",
|
||||||
|
"majorityVotedForProposal": "majority voted for this proposal",
|
||||||
|
"majorityNotVotedForProposal": "majority not voted for this proposal",
|
||||||
|
"requiredMajorityVotedForProposal": "Required majority voted for this proposal",
|
||||||
|
"requiredMajorityNotVotedForProposal": "Required majority not voted for this proposal",
|
||||||
|
"minParticipationReached": "Min. participation reached",
|
||||||
|
"minParticipationNotReached": "Min. participation not reached",
|
||||||
|
"consensusNodes": "consensus nodes",
|
||||||
|
"activeNodes": "active nodes",
|
||||||
|
"Estimated time to upgrade": "Estimated time to upgrade",
|
||||||
|
"Upgraded at": "Upgraded at",
|
||||||
|
"dataIsIdentical": "Data is identical",
|
||||||
|
"updatesToMarket": "Updates to market"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
getNotRejectedProposals,
|
getNotRejectedProposals,
|
||||||
getNotRejectedProtocolUpgradeProposals,
|
getNotRejectedProtocolUpgradeProposals,
|
||||||
} from '../proposals/proposals/proposals-container';
|
} from '../proposals/proposals/proposals-container';
|
||||||
import { Heading } from '../../components/heading';
|
import { Heading, SubHeading } from '../../components/heading';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
import type { RouteChildProps } from '..';
|
import type { RouteChildProps } from '..';
|
||||||
import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals';
|
||||||
@@ -31,6 +31,11 @@ import {
|
|||||||
orderByDate,
|
orderByDate,
|
||||||
orderByUpgradeBlockHeight,
|
orderByUpgradeBlockHeight,
|
||||||
} from '../proposals/components/proposals-list/proposals-list';
|
} from '../proposals/components/proposals-list/proposals-list';
|
||||||
|
import {
|
||||||
|
NetworkParams,
|
||||||
|
useNetworkParams,
|
||||||
|
} from '@vegaprotocol/network-parameters';
|
||||||
|
import { BigNumber } from '../../lib/bignumber';
|
||||||
|
|
||||||
const nodesToShow = 6;
|
const nodesToShow = 6;
|
||||||
|
|
||||||
@@ -42,33 +47,57 @@ const HomeProposals = ({
|
|||||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
params: networkParams,
|
||||||
|
loading: networkParamsLoading,
|
||||||
|
error: networkParamsError,
|
||||||
|
} = useNetworkParams([
|
||||||
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajorityLP,
|
||||||
|
NetworkParams.governance_proposal_asset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="mb-16" data-testid="home-proposals">
|
<AsyncRenderer
|
||||||
<Heading title={t('Proposals')} />
|
loading={networkParamsLoading}
|
||||||
<h3 className="mb-6">{t('homeProposalsIntro')}</h3>
|
error={networkParamsError}
|
||||||
<div className="flex items-center mb-8 gap-8">
|
data={networkParams}
|
||||||
<Link to={`${Routes.PROPOSALS}`}>
|
>
|
||||||
<Button size="md">{t('homeProposalsButtonText')}</Button>
|
<section className="mb-16" data-testid="home-proposals">
|
||||||
</Link>
|
<Heading title={t('vegaGovernance')} />
|
||||||
|
<h3 className="mb-6">{t('homeProposalsIntro')}</h3>
|
||||||
|
<div className="mb-8">
|
||||||
|
<ExternalLink href={ExternalLinks.GOVERNANCE_PAGE}>
|
||||||
|
{t(`readMoreGovernance`)}
|
||||||
|
</ExternalLink>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ExternalLink href={ExternalLinks.GOVERNANCE_PAGE}>
|
<SubHeading title={t('latestProposals')} />
|
||||||
{t(`readMoreGovernance`)}
|
<ul data-testid="home-proposal-list" className="grid gap-6">
|
||||||
</ExternalLink>
|
{protocolUpgradeProposals.map((proposal, index) => (
|
||||||
</div>
|
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
||||||
<ul
|
))}
|
||||||
data-testid="home-proposal-list"
|
|
||||||
className="grid md:grid-cols-2 lg:grid-cols-1 xl:grid-cols-2 gap-6"
|
|
||||||
>
|
|
||||||
{protocolUpgradeProposals.map((proposal, index) => (
|
|
||||||
<ProtocolUpgradeProposalsListItem key={index} proposal={proposal} />
|
|
||||||
))}
|
|
||||||
|
|
||||||
{proposals.map((proposal) => (
|
{proposals.map((proposal) => (
|
||||||
<ProposalsListItem key={proposal.id} proposal={proposal} />
|
<ProposalsListItem
|
||||||
))}
|
key={proposal.id}
|
||||||
</ul>
|
proposal={proposal}
|
||||||
</section>
|
networkParams={networkParams}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<Link to={`${Routes.PROPOSALS}`}>
|
||||||
|
<Button size="md">{t('homeProposalsButtonText')}</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -86,8 +115,8 @@ const HomeNodes = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const highlightedNodeData = [
|
const highlightedNodeData = [
|
||||||
{ title: t('active nodes'), length: activeNodes.length },
|
{ title: t('activeNodes'), length: activeNodes.length },
|
||||||
{ title: t('consensus nodes'), length: consensusNodes.length },
|
{ title: t('consensusNodes'), length: consensusNodes.length },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -233,7 +262,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
|||||||
[protocolUpgradeProposals]
|
[protocolUpgradeProposals]
|
||||||
);
|
);
|
||||||
|
|
||||||
const totalProposalsDesired = 4;
|
const totalProposalsDesired = 3;
|
||||||
const protocolUpgradeProposalsToShow = sortedProtocolUpgradeProposals.slice(
|
const protocolUpgradeProposalsToShow = sortedProtocolUpgradeProposals.slice(
|
||||||
0,
|
0,
|
||||||
totalProposalsDesired
|
totalProposalsDesired
|
||||||
@@ -248,6 +277,8 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
|||||||
|
|
||||||
const activeNodes = removePaginationWrapper(
|
const activeNodes = removePaginationWrapper(
|
||||||
validatorsData?.nodesConnection.edges
|
validatorsData?.nodesConnection.edges
|
||||||
|
).filter((node) =>
|
||||||
|
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
|
||||||
);
|
);
|
||||||
|
|
||||||
const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow);
|
const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow);
|
||||||
@@ -287,7 +318,7 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-testid="home-vega-token">
|
<div data-testid="home-vega-token">
|
||||||
<Heading title={t('VEGA Token')} marginTop={false} />
|
<Heading title={t('vegaToken')} marginTop={false} />
|
||||||
<h3 className="mb-6">{t('homeVegaTokenIntro')}</h3>
|
<h3 className="mb-6">{t('homeVegaTokenIntro')}</h3>
|
||||||
<div className="flex items-center mb-8 gap-4">
|
<div className="flex items-center mb-8 gap-4">
|
||||||
<Link to={Routes.WITHDRAWALS}>
|
<Link to={Routes.WITHDRAWALS}>
|
||||||
|
|||||||
+10
-32
@@ -1,5 +1,4 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
@@ -18,53 +17,32 @@ export const CurrentProposalState = ({
|
|||||||
|
|
||||||
switch (proposal?.state) {
|
switch (proposal?.state) {
|
||||||
case ProposalState.STATE_ENACTED: {
|
case ProposalState.STATE_ENACTED: {
|
||||||
proposalStatus = (
|
proposalStatus = t('voteState_Enacted');
|
||||||
<>
|
|
||||||
<span className="mr-2">{t('voteState_Enacted')}</span>
|
|
||||||
<Icon name={'tick'} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_PASSED: {
|
case ProposalState.STATE_PASSED: {
|
||||||
proposalStatus = (
|
proposalStatus = t('voteState_Passed');
|
||||||
<>
|
|
||||||
<span className="mr-2">{t('voteState_Passed')}</span>
|
|
||||||
<Icon name={'tick'} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
||||||
proposalStatus = (
|
proposalStatus = t('voteState_WaitingForNodeVote');
|
||||||
<>
|
|
||||||
<span className="mr-2">{t('voteState_WaitingForNodeVote')}</span>
|
|
||||||
<Icon name={'time'} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_OPEN: {
|
case ProposalState.STATE_OPEN: {
|
||||||
variant = 'primary' as ProposalInfoLabelVariant;
|
variant = 'primary' as ProposalInfoLabelVariant;
|
||||||
proposalStatus = <>{t('voteState_Open')}</>;
|
proposalStatus = t('voteState_Open');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_DECLINED: {
|
case ProposalState.STATE_DECLINED: {
|
||||||
proposalStatus = (
|
proposalStatus = t('voteState_Declined');
|
||||||
<>
|
|
||||||
<span className="mr-2">{t('voteState_Declined')}</span>
|
|
||||||
<Icon name={'cross'} />
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_REJECTED: {
|
case ProposalState.STATE_REJECTED: {
|
||||||
proposalStatus = (
|
proposalStatus = t('voteState_Rejected');
|
||||||
<>
|
break;
|
||||||
<span className="mr-2">{t('voteState_Rejected')}</span>
|
}
|
||||||
<Icon name={'warning-sign'} />
|
case ProposalState.STATE_FAILED: {
|
||||||
</>
|
proposalStatus = t('voteState_Failed');
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+80
-20
@@ -1,21 +1,66 @@
|
|||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import {
|
||||||
|
ProposalRejectionReason,
|
||||||
|
ProposalState,
|
||||||
|
VoteValue,
|
||||||
|
} from '@vegaprotocol/types';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import {
|
import {
|
||||||
generateNoVotes,
|
generateNoVotes,
|
||||||
generateProposal,
|
generateProposal,
|
||||||
generateYesVotes,
|
generateYesVotes,
|
||||||
} from '../../test-helpers/generate-proposals';
|
} from '../../test-helpers/generate-proposals';
|
||||||
import { ProposalHeader } from './proposal-header';
|
import { ProposalHeader } from './proposal-header';
|
||||||
|
import {
|
||||||
|
lastWeek,
|
||||||
|
nextWeek,
|
||||||
|
mockNetworkParams,
|
||||||
|
mockWalletContext,
|
||||||
|
createUserVoteQueryMock,
|
||||||
|
} from '../../test-helpers/mocks';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import { lastWeek, nextWeek } from '../../test-helpers/mocks';
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/proposals', () => ({
|
||||||
|
...jest.requireActual('@vegaprotocol/proposals'),
|
||||||
|
useSuccessorMarketProposalDetails: () => ({
|
||||||
|
code: 'PARENT_CODE',
|
||||||
|
parentMarketId: 'PARENT_ID',
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
const renderComponent = (
|
const renderComponent = (
|
||||||
proposal: ProposalQuery['proposal'],
|
proposal: ProposalQuery['proposal'],
|
||||||
isListItem = true
|
isListItem = true,
|
||||||
) => render(<ProposalHeader proposal={proposal} isListItem={isListItem} />);
|
mocks: MockedResponse[] = []
|
||||||
|
) =>
|
||||||
|
render(
|
||||||
|
<AppStateProvider>
|
||||||
|
<BrowserRouter>
|
||||||
|
<MockedProvider mocks={mocks}>
|
||||||
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<ProposalHeader
|
||||||
|
proposal={proposal}
|
||||||
|
isListItem={isListItem}
|
||||||
|
networkParams={mockNetworkParams}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</BrowserRouter>
|
||||||
|
</AppStateProvider>
|
||||||
|
);
|
||||||
|
|
||||||
describe('Proposal header', () => {
|
describe('Proposal header', () => {
|
||||||
|
afterAll(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
it('Renders New market proposal', () => {
|
it('Renders New market proposal', () => {
|
||||||
|
const mockedFlags = jest.mocked(FLAGS);
|
||||||
|
mockedFlags.SUCCESSOR_MARKETS = true;
|
||||||
renderComponent(
|
renderComponent(
|
||||||
generateProposal({
|
generateProposal({
|
||||||
rationale: {
|
rationale: {
|
||||||
@@ -48,6 +93,9 @@ describe('Proposal header', () => {
|
|||||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||||
'tGBP settled future.'
|
'tGBP settled future.'
|
||||||
);
|
);
|
||||||
|
expect(screen.getByTestId('proposal-successor-info')).toHaveTextContent(
|
||||||
|
'PARENT_CODE'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders Update market proposal', () => {
|
it('Renders Update market proposal', () => {
|
||||||
@@ -317,22 +365,6 @@ describe('Proposal header', () => {
|
|||||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders proposal state: Declined - majority not reached', () => {
|
|
||||||
renderComponent(
|
|
||||||
generateProposal({
|
|
||||||
state: ProposalState.STATE_DECLINED,
|
|
||||||
terms: {
|
|
||||||
enactmentDatetime: lastWeek.toString(),
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
no: generateNoVotes(1, 1000000000000000000),
|
|
||||||
yes: generateYesVotes(1, 1000000000000000000),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Declined');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Renders proposal state: Rejected', () => {
|
it('Renders proposal state: Rejected', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
generateProposal({
|
generateProposal({
|
||||||
@@ -346,4 +378,32 @@ describe('Proposal header', () => {
|
|||||||
);
|
);
|
||||||
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Rejected');
|
expect(screen.getByTestId('proposal-status')).toHaveTextContent('Rejected');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Renders proposal state: Open - user voted against', async () => {
|
||||||
|
const proposal = generateProposal({
|
||||||
|
state: ProposalState.STATE_OPEN,
|
||||||
|
terms: {
|
||||||
|
closingDatetime: nextWeek.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
renderComponent(proposal, true, [
|
||||||
|
// @ts-ignore generateProposal always creates an id
|
||||||
|
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_NO),
|
||||||
|
]);
|
||||||
|
expect(await screen.findByTestId('user-voted-no')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders proposal state: Open - user voted for', async () => {
|
||||||
|
const proposal = generateProposal({
|
||||||
|
state: ProposalState.STATE_OPEN,
|
||||||
|
terms: {
|
||||||
|
closingDatetime: nextWeek.toString(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
renderComponent(proposal, true, [
|
||||||
|
// @ts-ignore generateProposal always creates an id
|
||||||
|
createUserVoteQueryMock(proposal.id, VoteValue.VALUE_YES),
|
||||||
|
]);
|
||||||
|
expect(await screen.findByTestId('user-voted-yes')).toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+66
-14
@@ -1,5 +1,5 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
import { Lozenge, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||||
import { shorten } from '@vegaprotocol/utils';
|
import { shorten } from '@vegaprotocol/utils';
|
||||||
import { Heading, SubHeading } from '../../../../components/heading';
|
import { Heading, SubHeading } from '../../../../components/heading';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
@@ -8,15 +8,25 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|||||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||||
import { CurrentProposalState } from '../current-proposal-state';
|
import { CurrentProposalState } from '../current-proposal-state';
|
||||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||||
|
import { useUserVote } from '../vote-details/use-user-vote';
|
||||||
|
import { ProposalVotingStatus } from '../proposal-voting-status';
|
||||||
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
|
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||||
|
import { FLAGS } from '@vegaprotocol/environment';
|
||||||
|
import Routes from '../../../routes';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
export const ProposalHeader = ({
|
export const ProposalHeader = ({
|
||||||
proposal,
|
proposal,
|
||||||
|
networkParams,
|
||||||
isListItem = true,
|
isListItem = true,
|
||||||
}: {
|
}: {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
|
networkParams: Partial<NetworkParamsResult>;
|
||||||
isListItem?: boolean;
|
isListItem?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const { voteState } = useUserVote(proposal?.id);
|
||||||
const change = proposal?.terms.change;
|
const change = proposal?.terms.change;
|
||||||
|
|
||||||
let details: ReactNode;
|
let details: ReactNode;
|
||||||
@@ -33,6 +43,9 @@ export const ProposalHeader = ({
|
|||||||
fallbackTitle = t('NewMarketProposal');
|
fallbackTitle = t('NewMarketProposal');
|
||||||
details = (
|
details = (
|
||||||
<>
|
<>
|
||||||
|
{FLAGS.SUCCESSOR_MARKETS && (
|
||||||
|
<SuccessorCode proposalId={proposal?.id} />
|
||||||
|
)}
|
||||||
<span>
|
<span>
|
||||||
{t('Code')}: {change.instrument.code}.
|
{t('Code')}: {change.instrument.code}.
|
||||||
</span>{' '}
|
</span>{' '}
|
||||||
@@ -119,6 +132,35 @@ export const ProposalHeader = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<div className="flex items-center justify-between gap-4 mb-6 text-sm">
|
||||||
|
<div data-testid="proposal-type">
|
||||||
|
<ProposalInfoLabel variant="secondary">
|
||||||
|
{t(`${proposalType}`)}
|
||||||
|
</ProposalInfoLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-6">
|
||||||
|
{(voteState === 'Yes' || voteState === 'No') && (
|
||||||
|
<div
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
data-testid={`user-voted-${voteState.toLowerCase()}`}
|
||||||
|
>
|
||||||
|
<div className="text-vega-green" data-testid="you-voted-icon">
|
||||||
|
<VegaIcon name={VegaIconNames.VOTE} size={24} />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
{t('voted')}{' '}
|
||||||
|
<span className="uppercase">{t(`voteState_${voteState}`)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div data-testid="proposal-status">
|
||||||
|
<CurrentProposalState proposal={proposal} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div data-testid="proposal-title">
|
<div data-testid="proposal-title">
|
||||||
{isListItem ? (
|
{isListItem ? (
|
||||||
<header>
|
<header>
|
||||||
@@ -133,23 +175,33 @@ export const ProposalHeader = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-2 mb-4">
|
|
||||||
<div data-testid="proposal-type">
|
|
||||||
<ProposalInfoLabel variant="secondary">
|
|
||||||
{t(`${proposalType}`)}
|
|
||||||
</ProposalInfoLabel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-testid="proposal-status">
|
|
||||||
<CurrentProposalState proposal={proposal} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{details && (
|
{details && (
|
||||||
<div data-testid="proposal-details" className="break-words my-10">
|
<div
|
||||||
|
data-testid="proposal-details"
|
||||||
|
className="break-words mb-6 text-vega-light-200"
|
||||||
|
>
|
||||||
{details}
|
{details}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<ProposalVotingStatus proposal={proposal} networkParams={networkParams} />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const SuccessorCode = ({ proposalId }: { proposalId?: string | null }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const successor = useSuccessorMarketProposalDetails(proposalId);
|
||||||
|
|
||||||
|
return successor.parentMarketId || successor.code ? (
|
||||||
|
<span className="block" data-testid="proposal-successor-info">
|
||||||
|
{t('Successor market to')}:{' '}
|
||||||
|
<Link
|
||||||
|
to={`${Routes.PROPOSALS}/${successor.parentMarketId}`}
|
||||||
|
className="hover:underline"
|
||||||
|
>
|
||||||
|
{successor.code || successor.parentMarketId}
|
||||||
|
</Link>
|
||||||
|
</span>
|
||||||
|
) : null;
|
||||||
|
};
|
||||||
|
|||||||
+4
-4
@@ -7,10 +7,10 @@ export type ProposalInfoLabelVariant =
|
|||||||
| 'tertiary'
|
| 'tertiary'
|
||||||
| 'highlight';
|
| 'highlight';
|
||||||
|
|
||||||
const base = 'rounded-md px-2 py-1 font-alpha';
|
const base = 'rounded-full px-3 py-1 font-alpha';
|
||||||
const primary = 'bg-vega-light-150 text-black';
|
const primary = 'bg-vega-green text-black';
|
||||||
const secondary = 'bg-vega-dark-200 text-white';
|
const secondary = 'bg-vega-dark-200 text-vega-light-200';
|
||||||
const tertiary = 'bg-vega-dark-150 text-white';
|
const tertiary = 'bg-vega-dark-150 text-vega-light-200';
|
||||||
const highlight = 'bg-vega-yellow text-black';
|
const highlight = 'bg-vega-yellow text-black';
|
||||||
|
|
||||||
const getClassname = (variant: ProposalInfoLabelVariant) => {
|
const getClassname = (variant: ProposalInfoLabelVariant) => {
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-market-changes';
|
||||||
+91
@@ -0,0 +1,91 @@
|
|||||||
|
import { render, fireEvent } from '@testing-library/react';
|
||||||
|
import {
|
||||||
|
ProposalMarketChanges,
|
||||||
|
applyImmutableKeysFromEarlierVersion,
|
||||||
|
} from './proposal-market-changes';
|
||||||
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
|
|
||||||
|
describe('applyImmutableKeysFromEarlierVersion', () => {
|
||||||
|
it('returns an empty object if any argument is not an object or null', () => {
|
||||||
|
const earlierVersion: JsonValue = null;
|
||||||
|
const updatedVersion: JsonValue = null;
|
||||||
|
expect(
|
||||||
|
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
|
||||||
|
).toEqual({});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('overrides updatedVersion with values from earlierVersion for immutable keys', () => {
|
||||||
|
const earlierVersion: JsonValue = {
|
||||||
|
decimalPlaces: 2,
|
||||||
|
positionDecimalPlaces: 3,
|
||||||
|
instrument: {
|
||||||
|
name: 'Instrument1',
|
||||||
|
future: {
|
||||||
|
settlementAsset: 'Asset1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const updatedVersion: JsonValue = {
|
||||||
|
decimalPlaces: 3, // should be overridden by 2
|
||||||
|
positionDecimalPlaces: 4, // should be overridden by 3
|
||||||
|
instrument: {
|
||||||
|
name: 'Instrument2', // should be overridden by 'Instrument1'
|
||||||
|
future: {
|
||||||
|
settlementAsset: 'Asset2', // should be overridden by 'Asset1'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const expected: JsonValue = {
|
||||||
|
decimalPlaces: 2,
|
||||||
|
positionDecimalPlaces: 3,
|
||||||
|
instrument: {
|
||||||
|
name: 'Instrument1',
|
||||||
|
future: {
|
||||||
|
settlementAsset: 'Asset1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(
|
||||||
|
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
|
||||||
|
).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('ProposalMarketChanges', () => {
|
||||||
|
it('renders correctly', () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<ProposalMarketChanges
|
||||||
|
originalProposal={{}}
|
||||||
|
latestEnactedProposal={{}}
|
||||||
|
updatedProposal={{}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('JsonDiff is not visible when showChanges is false', () => {
|
||||||
|
const { queryByTestId } = render(
|
||||||
|
<ProposalMarketChanges
|
||||||
|
originalProposal={{}}
|
||||||
|
latestEnactedProposal={{}}
|
||||||
|
updatedProposal={{}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('JsonDiff is visible when showChanges is true', async () => {
|
||||||
|
const { getByTestId } = render(
|
||||||
|
<ProposalMarketChanges
|
||||||
|
originalProposal={{}}
|
||||||
|
latestEnactedProposal={{}}
|
||||||
|
updatedProposal={{}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
||||||
|
expect(getByTestId('json-diff')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
import cloneDeep from 'lodash/cloneDeep';
|
||||||
|
import set from 'lodash/set';
|
||||||
|
import get from 'lodash/get';
|
||||||
|
import { JsonDiff } from '../../../../components/json-diff';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||||
|
import { SubHeading } from '../../../../components/heading';
|
||||||
|
import type { JsonValue } from '../../../../components/json-diff';
|
||||||
|
|
||||||
|
const immutableKeys = [
|
||||||
|
'decimalPlaces',
|
||||||
|
'positionDecimalPlaces',
|
||||||
|
'instrument.name',
|
||||||
|
'instrument.future.settlementAsset',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const applyImmutableKeysFromEarlierVersion = (
|
||||||
|
earlierVersion: JsonValue,
|
||||||
|
updatedVersion: JsonValue
|
||||||
|
) => {
|
||||||
|
if (
|
||||||
|
typeof earlierVersion !== 'object' ||
|
||||||
|
earlierVersion === null ||
|
||||||
|
typeof updatedVersion !== 'object' ||
|
||||||
|
updatedVersion === null
|
||||||
|
) {
|
||||||
|
// If either version is not an object or is null, return null or throw an error
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedVersionCopy = cloneDeep(updatedVersion);
|
||||||
|
|
||||||
|
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
||||||
|
immutableKeys.forEach((key) => {
|
||||||
|
set(updatedVersionCopy, key, get(earlierVersion, key));
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedVersionCopy;
|
||||||
|
};
|
||||||
|
|
||||||
|
interface ProposalMarketChangesProps {
|
||||||
|
originalProposal: JsonValue;
|
||||||
|
latestEnactedProposal: JsonValue | undefined;
|
||||||
|
updatedProposal: JsonValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProposalMarketChanges = ({
|
||||||
|
originalProposal,
|
||||||
|
latestEnactedProposal,
|
||||||
|
updatedProposal,
|
||||||
|
}: ProposalMarketChangesProps) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [showChanges, setShowChanges] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section data-testid="proposal-market-changes">
|
||||||
|
<CollapsibleToggle
|
||||||
|
toggleState={showChanges}
|
||||||
|
setToggleState={setShowChanges}
|
||||||
|
dataTestId={'proposal-market-changes-toggle'}
|
||||||
|
>
|
||||||
|
<SubHeading title={t('updatesToMarket')} />
|
||||||
|
</CollapsibleToggle>
|
||||||
|
|
||||||
|
{showChanges && (
|
||||||
|
<div className="mb-6">
|
||||||
|
<JsonDiff
|
||||||
|
left={latestEnactedProposal || originalProposal}
|
||||||
|
right={
|
||||||
|
latestEnactedProposal
|
||||||
|
? applyImmutableKeysFromEarlierVersion(
|
||||||
|
latestEnactedProposal,
|
||||||
|
updatedProposal
|
||||||
|
)
|
||||||
|
: applyImmutableKeysFromEarlierVersion(
|
||||||
|
originalProposal,
|
||||||
|
updatedProposal
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-voting-status';
|
||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { BrowserRouter as Router } from 'react-router-dom';
|
||||||
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||||
|
import {
|
||||||
|
lastWeek,
|
||||||
|
mockWalletContext,
|
||||||
|
networkParamsQueryMock,
|
||||||
|
nextWeek,
|
||||||
|
mockNetworkParams,
|
||||||
|
} from '../../test-helpers/mocks';
|
||||||
|
import { ProposalVotingStatus } from './proposal-voting-status';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
|
import {
|
||||||
|
generateNoVotes,
|
||||||
|
generateProposal,
|
||||||
|
generateYesVotes,
|
||||||
|
} from '../../test-helpers/generate-proposals';
|
||||||
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
import { BigNumber } from '../../../../lib/bignumber';
|
||||||
|
import type { AppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
|
|
||||||
|
const mockTotalSupply = new BigNumber(100);
|
||||||
|
// Note - giving a fixedTokenValue of 1 means a ratio of 1:1 votes to tokens, making sums easier :)
|
||||||
|
const fixedTokenValue = 1000000000000000000;
|
||||||
|
|
||||||
|
const mockAppState: AppState = {
|
||||||
|
totalAssociated: new BigNumber('50063005'),
|
||||||
|
decimals: 18,
|
||||||
|
totalSupply: mockTotalSupply,
|
||||||
|
vegaWalletManageOverlay: false,
|
||||||
|
transactionOverlay: false,
|
||||||
|
bannerMessage: '',
|
||||||
|
disconnectNotice: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
jest.mock('../../../../contexts/app-state/app-state-context', () => ({
|
||||||
|
useAppState: () => ({
|
||||||
|
appState: mockAppState,
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const renderComponent = (
|
||||||
|
proposal: ProposalQuery['proposal'],
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||||
|
) =>
|
||||||
|
render(
|
||||||
|
<Router>
|
||||||
|
<MockedProvider mocks={mocks}>
|
||||||
|
<VegaWalletContext.Provider value={mockWalletContext}>
|
||||||
|
<ProposalVotingStatus
|
||||||
|
proposal={proposal}
|
||||||
|
networkParams={mockNetworkParams}
|
||||||
|
/>
|
||||||
|
</VegaWalletContext.Provider>
|
||||||
|
</MockedProvider>
|
||||||
|
</Router>
|
||||||
|
);
|
||||||
|
|
||||||
|
describe('ProposalVotingStatus', () => {
|
||||||
|
beforeAll(() => {
|
||||||
|
jest.useFakeTimers();
|
||||||
|
jest.setSystemTime(0);
|
||||||
|
});
|
||||||
|
afterAll(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders majority reached', () => {
|
||||||
|
const yesVotes = 100;
|
||||||
|
const noVotes = 0;
|
||||||
|
|
||||||
|
renderComponent(
|
||||||
|
generateProposal({
|
||||||
|
state: ProposalState.STATE_PASSED,
|
||||||
|
terms: {
|
||||||
|
closingDatetime: lastWeek.toString(),
|
||||||
|
enactmentDatetime: nextWeek.toString(),
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
__typename: 'ProposalVotes',
|
||||||
|
yes: generateYesVotes(yesVotes, fixedTokenValue),
|
||||||
|
no: generateNoVotes(noVotes, fixedTokenValue),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('majority-reached')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders majority not reached', () => {
|
||||||
|
const yesVotes = 20;
|
||||||
|
const noVotes = 80;
|
||||||
|
|
||||||
|
renderComponent(
|
||||||
|
generateProposal({
|
||||||
|
state: ProposalState.STATE_PASSED,
|
||||||
|
terms: {
|
||||||
|
closingDatetime: lastWeek.toString(),
|
||||||
|
enactmentDatetime: nextWeek.toString(),
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
__typename: 'ProposalVotes',
|
||||||
|
yes: generateYesVotes(yesVotes, fixedTokenValue),
|
||||||
|
no: generateNoVotes(noVotes, fixedTokenValue),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('majority-not-reached')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders participation reached', () => {
|
||||||
|
const yesVotes = 1000;
|
||||||
|
const noVotes = 0;
|
||||||
|
|
||||||
|
renderComponent(
|
||||||
|
generateProposal({
|
||||||
|
state: ProposalState.STATE_PASSED,
|
||||||
|
terms: {
|
||||||
|
closingDatetime: lastWeek.toString(),
|
||||||
|
enactmentDatetime: nextWeek.toString(),
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
__typename: 'ProposalVotes',
|
||||||
|
yes: generateYesVotes(yesVotes, fixedTokenValue),
|
||||||
|
no: generateNoVotes(noVotes, fixedTokenValue),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('participation-reached')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Renders participation not reached', () => {
|
||||||
|
const yesVotes = 0;
|
||||||
|
const noVotes = 0;
|
||||||
|
|
||||||
|
renderComponent(
|
||||||
|
generateProposal({
|
||||||
|
terms: {
|
||||||
|
closingDatetime: lastWeek.toString(),
|
||||||
|
enactmentDatetime: nextWeek.toString(),
|
||||||
|
},
|
||||||
|
votes: {
|
||||||
|
__typename: 'ProposalVotes',
|
||||||
|
yes: generateYesVotes(yesVotes, fixedTokenValue),
|
||||||
|
no: generateNoVotes(noVotes, fixedTokenValue),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('participation-not-reached')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
+174
@@ -0,0 +1,174 @@
|
|||||||
|
import classNames from 'classnames';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Icon } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { useVoteInformation } from '../../hooks';
|
||||||
|
import { BigNumber } from '../../../../lib/bignumber';
|
||||||
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
|
||||||
|
const statusClasses = (reached: boolean) =>
|
||||||
|
classNames('flex items-center gap-2 px-4 py-2 rounded-md', {
|
||||||
|
'bg-vega-green-700': reached,
|
||||||
|
'bg-vega-red-700': !reached,
|
||||||
|
});
|
||||||
|
|
||||||
|
const MajorityStatus = ({
|
||||||
|
reached,
|
||||||
|
requiredMajority,
|
||||||
|
}: {
|
||||||
|
reached: boolean;
|
||||||
|
requiredMajority: string | null | undefined;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={statusClasses(reached)}
|
||||||
|
data-testid="proposal-majority-status"
|
||||||
|
>
|
||||||
|
{reached ? <Icon name="tick" /> : <Icon name="cross" />}
|
||||||
|
{reached ? (
|
||||||
|
<div data-testid="majority-reached">
|
||||||
|
{requiredMajority ? (
|
||||||
|
<>
|
||||||
|
{new BigNumber(requiredMajority).times(100).toString()}%{' '}
|
||||||
|
{t('majorityVotedForProposal')}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('requiredMajorityVotedForProposal')
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div data-testid="majority-not-reached">
|
||||||
|
{requiredMajority ? (
|
||||||
|
<>
|
||||||
|
{new BigNumber(requiredMajority).times(100).toString()}%{' '}
|
||||||
|
{t('majorityNotVotedForProposal')}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
t('requiredMajorityNotVotedForProposal')
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ParticipationStatus = ({ reached }: { reached: boolean }) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={statusClasses(reached)}>
|
||||||
|
{reached ? (
|
||||||
|
<>
|
||||||
|
<Icon name="tick" />
|
||||||
|
<div data-testid="participation-reached">
|
||||||
|
{t('minParticipationReached')}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Icon name="cross" />
|
||||||
|
<div data-testid="participation-not-reached">
|
||||||
|
{t('minParticipationNotReached')}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ProposalVotingStatus = ({
|
||||||
|
proposal,
|
||||||
|
networkParams,
|
||||||
|
}: {
|
||||||
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
|
networkParams: Partial<NetworkParamsResult>;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { majorityMet, majorityLPMet, participationMet, participationLPMet } =
|
||||||
|
useVoteInformation({
|
||||||
|
proposal,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!proposal) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isUpdateMarket = proposal?.terms.change.__typename === 'UpdateMarket';
|
||||||
|
|
||||||
|
let requiredVotingMajority = null;
|
||||||
|
let requiredVotingMajorityLP = null;
|
||||||
|
|
||||||
|
if (networkParams) {
|
||||||
|
switch (proposal.terms.change.__typename) {
|
||||||
|
case 'NewMarket':
|
||||||
|
requiredVotingMajority =
|
||||||
|
networkParams.governance_proposal_market_requiredMajority;
|
||||||
|
break;
|
||||||
|
case 'UpdateMarket':
|
||||||
|
requiredVotingMajority =
|
||||||
|
networkParams.governance_proposal_updateMarket_requiredMajority;
|
||||||
|
requiredVotingMajorityLP =
|
||||||
|
networkParams.governance_proposal_updateMarket_requiredMajorityLP;
|
||||||
|
break;
|
||||||
|
case 'NewAsset':
|
||||||
|
requiredVotingMajority =
|
||||||
|
networkParams.governance_proposal_asset_requiredMajority;
|
||||||
|
break;
|
||||||
|
case 'UpdateAsset':
|
||||||
|
requiredVotingMajority =
|
||||||
|
networkParams.governance_proposal_updateAsset_requiredMajority;
|
||||||
|
break;
|
||||||
|
case 'UpdateNetworkParameter':
|
||||||
|
requiredVotingMajority =
|
||||||
|
networkParams.governance_proposal_updateNetParam_requiredMajority;
|
||||||
|
break;
|
||||||
|
case 'NewFreeform':
|
||||||
|
requiredVotingMajority =
|
||||||
|
networkParams.governance_proposal_freeform_requiredMajority;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isUpdateMarket) {
|
||||||
|
return (
|
||||||
|
<div className="mb-6">
|
||||||
|
<p>{t('Token vote')}</p>
|
||||||
|
<div
|
||||||
|
className="grid grid-cols-2 gap-4 items-center"
|
||||||
|
data-testid="token-vote-statuses"
|
||||||
|
>
|
||||||
|
<MajorityStatus
|
||||||
|
reached={majorityMet}
|
||||||
|
requiredMajority={requiredVotingMajority}
|
||||||
|
/>{' '}
|
||||||
|
<ParticipationStatus reached={participationMet} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="mt-4">{t('Liquidity provider vote')}</p>
|
||||||
|
<div
|
||||||
|
className="grid grid-cols-2 gap-4 items-center"
|
||||||
|
data-testid="lp-vote-statuses"
|
||||||
|
>
|
||||||
|
<MajorityStatus
|
||||||
|
reached={majorityLPMet}
|
||||||
|
requiredMajority={requiredVotingMajorityLP}
|
||||||
|
/>{' '}
|
||||||
|
<ParticipationStatus reached={participationLPMet} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-2 gap-4 items-center mb-6">
|
||||||
|
<MajorityStatus
|
||||||
|
reached={majorityMet}
|
||||||
|
requiredMajority={requiredVotingMajority}
|
||||||
|
/>{' '}
|
||||||
|
<ParticipationStatus reached={participationMet} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ import { generateProposal } from '../../test-helpers/generate-proposals';
|
|||||||
import { Proposal } from './proposal';
|
import { Proposal } from './proposal';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
import { mockNetworkParams } from '../../test-helpers/mocks';
|
||||||
|
|
||||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||||
@@ -46,6 +47,7 @@ const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
|||||||
<Proposal
|
<Proposal
|
||||||
restData={{}}
|
restData={{}}
|
||||||
proposal={proposal as ProposalQuery['proposal']}
|
proposal={proposal as ProposalQuery['proposal']}
|
||||||
|
networkParams={mockNetworkParams}
|
||||||
/>
|
/>
|
||||||
</MemoryRouter>
|
</MemoryRouter>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,6 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||||
NetworkParams,
|
|
||||||
useNetworkParams,
|
|
||||||
} from '@vegaprotocol/network-parameters';
|
|
||||||
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||||
import { ProposalDescription } from '../proposal-description';
|
import { ProposalDescription } from '../proposal-description';
|
||||||
import { ProposalChangeTable } from '../proposal-change-table';
|
import { ProposalChangeTable } from '../proposal-change-table';
|
||||||
@@ -21,6 +17,8 @@ import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
|||||||
import type { AssetQuery } from '@vegaprotocol/assets';
|
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
|
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||||
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
export enum ProposalType {
|
export enum ProposalType {
|
||||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||||
@@ -32,28 +30,27 @@ export enum ProposalType {
|
|||||||
}
|
}
|
||||||
export interface ProposalProps {
|
export interface ProposalProps {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
|
networkParams: Partial<NetworkParamsResult>;
|
||||||
newMarketData?: MarketInfoWithData | null;
|
newMarketData?: MarketInfoWithData | null;
|
||||||
assetData?: AssetQuery | null;
|
assetData?: AssetQuery | null;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
restData: any;
|
restData: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
originalMarketProposalRestData?: any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
mostRecentlyEnactedAssociatedMarketProposal?: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Proposal = ({
|
export const Proposal = ({
|
||||||
proposal,
|
proposal,
|
||||||
|
networkParams,
|
||||||
restData,
|
restData,
|
||||||
newMarketData,
|
newMarketData,
|
||||||
assetData,
|
assetData,
|
||||||
|
originalMarketProposalRestData,
|
||||||
|
mostRecentlyEnactedAssociatedMarketProposal,
|
||||||
}: ProposalProps) => {
|
}: ProposalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { params, loading, error } = useNetworkParams([
|
|
||||||
NetworkParams.governance_proposal_market_minVoterBalance,
|
|
||||||
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
|
|
||||||
NetworkParams.governance_proposal_asset_minVoterBalance,
|
|
||||||
NetworkParams.governance_proposal_updateAsset_minVoterBalance,
|
|
||||||
NetworkParams.governance_proposal_updateNetParam_minVoterBalance,
|
|
||||||
NetworkParams.governance_proposal_freeform_minVoterBalance,
|
|
||||||
NetworkParams.spam_protection_voting_min_tokens,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!proposal) {
|
if (!proposal) {
|
||||||
return null;
|
return null;
|
||||||
@@ -79,122 +76,140 @@ export const Proposal = ({
|
|||||||
let minVoterBalance = null;
|
let minVoterBalance = null;
|
||||||
let proposalType = null;
|
let proposalType = null;
|
||||||
|
|
||||||
if (params) {
|
if (networkParams) {
|
||||||
switch (proposal.terms.change.__typename) {
|
switch (proposal.terms.change.__typename) {
|
||||||
case 'NewMarket':
|
case 'NewMarket':
|
||||||
minVoterBalance = params.governance_proposal_market_minVoterBalance;
|
minVoterBalance =
|
||||||
|
networkParams.governance_proposal_market_minVoterBalance;
|
||||||
proposalType = ProposalType.PROPOSAL_NEW_MARKET;
|
proposalType = ProposalType.PROPOSAL_NEW_MARKET;
|
||||||
break;
|
break;
|
||||||
case 'UpdateMarket':
|
case 'UpdateMarket':
|
||||||
minVoterBalance =
|
minVoterBalance =
|
||||||
params.governance_proposal_updateMarket_minVoterBalance;
|
networkParams.governance_proposal_updateMarket_minVoterBalance;
|
||||||
proposalType = ProposalType.PROPOSAL_UPDATE_MARKET;
|
proposalType = ProposalType.PROPOSAL_UPDATE_MARKET;
|
||||||
break;
|
break;
|
||||||
case 'NewAsset':
|
case 'NewAsset':
|
||||||
minVoterBalance = params.governance_proposal_asset_minVoterBalance;
|
minVoterBalance =
|
||||||
|
networkParams.governance_proposal_asset_minVoterBalance;
|
||||||
proposalType = ProposalType.PROPOSAL_NEW_ASSET;
|
proposalType = ProposalType.PROPOSAL_NEW_ASSET;
|
||||||
break;
|
break;
|
||||||
case 'UpdateAsset':
|
case 'UpdateAsset':
|
||||||
minVoterBalance =
|
minVoterBalance =
|
||||||
params.governance_proposal_updateAsset_minVoterBalance;
|
networkParams.governance_proposal_updateAsset_minVoterBalance;
|
||||||
proposalType = ProposalType.PROPOSAL_UPDATE_ASSET;
|
proposalType = ProposalType.PROPOSAL_UPDATE_ASSET;
|
||||||
break;
|
break;
|
||||||
case 'UpdateNetworkParameter':
|
case 'UpdateNetworkParameter':
|
||||||
minVoterBalance =
|
minVoterBalance =
|
||||||
params.governance_proposal_updateNetParam_minVoterBalance;
|
networkParams.governance_proposal_updateNetParam_minVoterBalance;
|
||||||
proposalType = ProposalType.PROPOSAL_NETWORK_PARAMETER;
|
proposalType = ProposalType.PROPOSAL_NETWORK_PARAMETER;
|
||||||
break;
|
break;
|
||||||
case 'NewFreeform':
|
case 'NewFreeform':
|
||||||
minVoterBalance = params.governance_proposal_freeform_minVoterBalance;
|
minVoterBalance =
|
||||||
|
networkParams.governance_proposal_freeform_minVoterBalance;
|
||||||
proposalType = ProposalType.PROPOSAL_FREEFORM;
|
proposalType = ProposalType.PROPOSAL_FREEFORM;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer data={params} loading={loading} error={error}>
|
<section data-testid="proposal">
|
||||||
<section data-testid="proposal">
|
<div className="flex items-center gap-1 mb-6">
|
||||||
<div className="flex items-center gap-1">
|
<Icon name={'chevron-left'} />
|
||||||
<Icon name={'chevron-left'} />
|
|
||||||
|
|
||||||
{proposal.state === ProposalState.STATE_REJECTED ? (
|
{proposal.state === ProposalState.STATE_REJECTED ? (
|
||||||
<div data-testid="rejected-proposals-link">
|
<div data-testid="rejected-proposals-link">
|
||||||
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
|
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
|
||||||
{t('RejectedProposals')}
|
{t('RejectedProposals')}
|
||||||
</Link>
|
</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 id="details">
|
|
||||||
<div className="my-10">
|
|
||||||
<ProposalChangeTable proposal={proposal} />
|
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<div data-testid="all-proposals-link">
|
||||||
|
<Link className="underline" to={Routes.PROPOSALS}>
|
||||||
|
{t('AllProposals')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ProposalHeader
|
||||||
|
proposal={proposal}
|
||||||
|
isListItem={false}
|
||||||
|
networkParams={networkParams}
|
||||||
|
/>
|
||||||
|
|
||||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
<div id="details">
|
||||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
<div className="my-10">
|
||||||
proposal.id ? (
|
<ProposalChangeTable proposal={proposal} />
|
||||||
<ListAsset
|
</div>
|
||||||
assetId={proposal.id}
|
|
||||||
withdrawalThreshold={
|
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||||
proposal.terms.change.source.withdrawThreshold
|
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>
|
||||||
|
|
||||||
|
{newMarketData && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalMarketData marketData={newMarketData} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{proposal.terms.change.__typename === 'UpdateMarket' && (
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalMarketChanges
|
||||||
|
originalProposal={
|
||||||
|
originalMarketProposalRestData?.data?.proposal?.terms?.newMarket
|
||||||
|
?.changes || {}
|
||||||
|
}
|
||||||
|
latestEnactedProposal={
|
||||||
|
mostRecentlyEnactedAssociatedMarketProposal?.node?.proposal
|
||||||
|
?.terms?.updateMarket?.changes || {}
|
||||||
|
}
|
||||||
|
updatedProposal={
|
||||||
|
restData?.data?.proposal?.terms?.updateMarket?.changes || {}
|
||||||
}
|
}
|
||||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<ProposalDescription description={proposal.rationale.description} />
|
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{newMarketData && (
|
{(proposal.terms.change.__typename === 'NewAsset' ||
|
||||||
|
proposal.terms.change.__typename === 'UpdateAsset') &&
|
||||||
|
asset && (
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<ProposalMarketData marketData={newMarketData} />
|
<ProposalAssetDetails asset={asset} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{(proposal.terms.change.__typename === 'NewAsset' ||
|
<div className="mb-6">
|
||||||
proposal.terms.change.__typename === 'UpdateAsset') &&
|
<ProposalJson proposal={restData?.data?.proposal} />
|
||||||
asset && (
|
|
||||||
<div className="mb-4">
|
|
||||||
<ProposalAssetDetails asset={asset} />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="mb-6">
|
|
||||||
<ProposalJson proposal={restData?.data?.proposal} />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="voting">
|
<div id="voting">
|
||||||
<div className="mb-10">
|
<div className="mb-10">
|
||||||
<RoundedWrapper paddingBottom={true}>
|
<RoundedWrapper paddingBottom={true}>
|
||||||
<VoteDetails
|
<VoteDetails
|
||||||
proposal={proposal}
|
|
||||||
proposalType={proposalType}
|
|
||||||
minVoterBalance={minVoterBalance}
|
|
||||||
spamProtectionMinTokens={
|
|
||||||
params?.spam_protection_voting_min_tokens
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</RoundedWrapper>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<ProposalVotesTable
|
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
proposalType={proposalType}
|
proposalType={proposalType}
|
||||||
|
minVoterBalance={minVoterBalance}
|
||||||
|
spamProtectionMinTokens={
|
||||||
|
networkParams?.spam_protection_voting_min_tokens
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</RoundedWrapper>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
|
||||||
</AsyncRenderer>
|
<div className="mb-4">
|
||||||
|
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+11
-127
@@ -5,11 +5,7 @@ import type { MockedResponse } from '@apollo/client/testing';
|
|||||||
import { MockedProvider } from '@apollo/client/testing';
|
import { MockedProvider } from '@apollo/client/testing';
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import {
|
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||||
ProposalRejectionReason,
|
|
||||||
ProposalState,
|
|
||||||
VoteValue,
|
|
||||||
} from '@vegaprotocol/types';
|
|
||||||
import {
|
import {
|
||||||
generateNoVotes,
|
generateNoVotes,
|
||||||
generateProposal,
|
generateProposal,
|
||||||
@@ -18,7 +14,6 @@ import {
|
|||||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||||
import {
|
import {
|
||||||
mockPubkey,
|
|
||||||
mockWalletContext,
|
mockWalletContext,
|
||||||
networkParamsQueryMock,
|
networkParamsQueryMock,
|
||||||
fiveMinutes,
|
fiveMinutes,
|
||||||
@@ -28,39 +23,6 @@ import {
|
|||||||
nextWeek,
|
nextWeek,
|
||||||
} from '../../test-helpers/mocks';
|
} from '../../test-helpers/mocks';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
import { UserVoteDocument } from '../vote-details/__generated__/Vote';
|
|
||||||
import faker from 'faker';
|
|
||||||
|
|
||||||
const createUserVoteQueryMock = (
|
|
||||||
proposalId: string | undefined | null,
|
|
||||||
value: VoteValue
|
|
||||||
) => ({
|
|
||||||
request: {
|
|
||||||
query: UserVoteDocument,
|
|
||||||
variables: {
|
|
||||||
partyId: mockPubkey.publicKey,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
data: {
|
|
||||||
party: {
|
|
||||||
votesConnection: {
|
|
||||||
edges: [
|
|
||||||
{
|
|
||||||
node: {
|
|
||||||
proposalId,
|
|
||||||
vote: {
|
|
||||||
value,
|
|
||||||
datetime: faker.date.past().toISOString(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const renderComponent = (
|
const renderComponent = (
|
||||||
proposal: ProposalQuery['proposal'],
|
proposal: ProposalQuery['proposal'],
|
||||||
@@ -131,7 +93,7 @@ describe('Proposals list item details', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders proposal state: Update market proposal - set to pass by LP vote', () => {
|
it('Renders proposal state: Update market proposal - Currently expected to pass by LP vote', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_OPEN,
|
state: ProposalState.STATE_OPEN,
|
||||||
@@ -153,11 +115,11 @@ describe('Proposals list item details', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||||
'Set to pass by LP vote'
|
'Currently expected to pass by LP vote'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders proposal state: Update market proposal - set to pass by token vote', () => {
|
it('Renders proposal state: Update market proposal - Currently expected to pass by token vote', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_OPEN,
|
state: ProposalState.STATE_OPEN,
|
||||||
@@ -179,11 +141,11 @@ describe('Proposals list item details', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||||
'Set to pass by token vote'
|
'Currently expected to pass by token vote'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders proposal state: Update market proposal - set to fail', () => {
|
it('Renders proposal state: Update market proposal - Currently expected to fail', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
generateProposal({
|
generateProposal({
|
||||||
state: ProposalState.STATE_OPEN,
|
state: ProposalState.STATE_OPEN,
|
||||||
@@ -204,7 +166,9 @@ describe('Proposals list item details', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent('Set to fail');
|
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||||
|
'Currently expected to fail'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders proposal state: Open - 5 minutes left to vote', () => {
|
it('Renders proposal state: Open - 5 minutes left to vote', () => {
|
||||||
@@ -249,52 +213,6 @@ describe('Proposals list item details', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Renders proposal state: Open - user voted for', async () => {
|
|
||||||
const proposal = generateProposal({
|
|
||||||
state: ProposalState.STATE_OPEN,
|
|
||||||
terms: {
|
|
||||||
closingDatetime: nextWeek.toString(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
renderComponent(proposal, [
|
|
||||||
networkParamsQueryMock,
|
|
||||||
createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_YES),
|
|
||||||
]);
|
|
||||||
expect(await screen.findByText('You voted For')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Renders proposal state: Open - user voted against', async () => {
|
|
||||||
const proposal = generateProposal({
|
|
||||||
state: ProposalState.STATE_OPEN,
|
|
||||||
terms: {
|
|
||||||
closingDatetime: nextWeek.toString(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
renderComponent(proposal, [
|
|
||||||
networkParamsQueryMock,
|
|
||||||
createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_NO),
|
|
||||||
]);
|
|
||||||
expect(await screen.findByText('You voted Against')).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Renders proposal state: Open - participation not reached', () => {
|
|
||||||
renderComponent(
|
|
||||||
generateProposal({
|
|
||||||
state: ProposalState.STATE_OPEN,
|
|
||||||
terms: {
|
|
||||||
enactmentDatetime: nextWeek.toString(),
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
no: generateNoVotes(0),
|
|
||||||
yes: generateYesVotes(0),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
|
||||||
'Participation not reached'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Renders proposal state: Open - majority not reached', () => {
|
it('Renders proposal state: Open - majority not reached', () => {
|
||||||
renderComponent(
|
renderComponent(
|
||||||
generateProposal({
|
generateProposal({
|
||||||
@@ -309,7 +227,7 @@ describe('Proposals list item details', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||||
'Majority not reached'
|
'Currently expected to fail'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -327,42 +245,8 @@ describe('Proposals list item details', () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent('Set to pass');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Renders proposal state: Declined - participation not reached', () => {
|
|
||||||
renderComponent(
|
|
||||||
generateProposal({
|
|
||||||
state: ProposalState.STATE_DECLINED,
|
|
||||||
terms: {
|
|
||||||
enactmentDatetime: lastWeek.toString(),
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
no: generateNoVotes(0),
|
|
||||||
yes: generateYesVotes(0),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
||||||
'Participation not reached'
|
'Currently expected to pass'
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Renders proposal state: Declined - majority not reached', () => {
|
|
||||||
renderComponent(
|
|
||||||
generateProposal({
|
|
||||||
state: ProposalState.STATE_DECLINED,
|
|
||||||
terms: {
|
|
||||||
enactmentDatetime: lastWeek.toString(),
|
|
||||||
},
|
|
||||||
votes: {
|
|
||||||
no: generateNoVotes(1, 1000000000000000000),
|
|
||||||
yes: generateYesVotes(1, 1000000000000000000),
|
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
expect(screen.getByTestId('vote-status')).toHaveTextContent(
|
|
||||||
'Majority not reached'
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+53
-106
@@ -1,9 +1,6 @@
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useVoteInformation } from '../../hooks';
|
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
||||||
import { useUserVote } from '../vote-details/use-user-vote';
|
|
||||||
import { StatusPass } from '../current-proposal-status/current-proposal-status';
|
|
||||||
import { format, formatDistanceToNowStrict } from 'date-fns';
|
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
@@ -14,138 +11,100 @@ import {
|
|||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import { useVoteInformation } from '../../hooks';
|
||||||
const MajorityNotReached = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{t('Majority')} {t('not reached')}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
const ParticipationNotReached = () => {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{t('Participation')} {t('not reached')}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ProposalsListItemDetails = ({
|
export const ProposalsListItemDetails = ({
|
||||||
proposal,
|
proposal,
|
||||||
}: {
|
}: {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const state = proposal?.state;
|
const state = proposal?.state;
|
||||||
const {
|
const { willPassByTokenVote, willPassByLPVote } = useVoteInformation({
|
||||||
willPassByTokenVote,
|
|
||||||
willPassByLPVote,
|
|
||||||
majorityMet,
|
|
||||||
participationMet,
|
|
||||||
} = useVoteInformation({
|
|
||||||
proposal,
|
proposal,
|
||||||
});
|
});
|
||||||
const { t } = useTranslation();
|
|
||||||
const { voteState } = useUserVote(proposal?.id);
|
|
||||||
const isUpdateMarket = proposal?.terms.change.__typename === 'UpdateMarket';
|
|
||||||
const updateMarketWillPass = willPassByTokenVote || willPassByLPVote;
|
const updateMarketWillPass = willPassByTokenVote || willPassByLPVote;
|
||||||
const updateMarketVotePassMethod = willPassByTokenVote
|
const updateMarketVotePassMethod = willPassByTokenVote
|
||||||
? t('byTokenVote')
|
? t('byTokenVote')
|
||||||
: t('byLPVote');
|
: t('byLPVote');
|
||||||
|
const nowToEnactmentInHours = differenceInHours(
|
||||||
|
new Date(proposal?.terms.closingDatetime),
|
||||||
|
new Date()
|
||||||
|
);
|
||||||
|
const isUpdateMarket = proposal?.terms.change.__typename === 'UpdateMarket';
|
||||||
|
|
||||||
let voteDetails: ReactNode;
|
let voteDetails: ReactNode;
|
||||||
let voteStatus: ReactNode;
|
let voteStatus: ReactNode;
|
||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case ProposalState.STATE_ENACTED: {
|
case ProposalState.STATE_ENACTED: {
|
||||||
voteDetails = proposal?.terms.enactmentDatetime && (
|
voteDetails =
|
||||||
<>
|
proposal?.terms.enactmentDatetime &&
|
||||||
{format(
|
t('enactedOn{{date}}', {
|
||||||
new Date(proposal?.terms.enactmentDatetime),
|
enactmentDate:
|
||||||
DATE_FORMAT_DETAILED
|
proposal?.terms.enactmentDatetime &&
|
||||||
)}
|
format(
|
||||||
</>
|
new Date(proposal?.terms.enactmentDatetime),
|
||||||
);
|
DATE_FORMAT_DETAILED
|
||||||
|
),
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_PASSED: {
|
case ProposalState.STATE_PASSED: {
|
||||||
voteDetails = proposal?.terms.change.__typename !== 'NewFreeform' && (
|
voteDetails =
|
||||||
<>
|
proposal?.terms.change.__typename !== 'NewFreeform' &&
|
||||||
{t('toEnactOn')}{' '}
|
t('enactsOn{{date}}', {
|
||||||
{proposal?.terms.enactmentDatetime &&
|
enactmentDate:
|
||||||
|
proposal?.terms.enactmentDatetime &&
|
||||||
format(
|
format(
|
||||||
new Date(proposal.terms.enactmentDatetime),
|
new Date(proposal.terms.enactmentDatetime),
|
||||||
DATE_FORMAT_DETAILED
|
DATE_FORMAT_DETAILED
|
||||||
)}
|
),
|
||||||
</>
|
});
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
case ProposalState.STATE_WAITING_FOR_NODE_VOTE: {
|
||||||
voteDetails = proposal?.terms.change.__typename !== 'NewFreeform' && (
|
voteDetails =
|
||||||
<>
|
proposal?.terms.change.__typename !== 'NewFreeform' &&
|
||||||
{t('toEnactOn')}{' '}
|
t('enactsOn{{date}}', {
|
||||||
{proposal?.terms.enactmentDatetime &&
|
enactmentDate:
|
||||||
|
proposal?.terms.enactmentDatetime &&
|
||||||
format(
|
format(
|
||||||
new Date(proposal.terms.enactmentDatetime),
|
new Date(proposal.terms.enactmentDatetime),
|
||||||
DATE_FORMAT_DETAILED
|
DATE_FORMAT_DETAILED
|
||||||
)}
|
),
|
||||||
</>
|
});
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_OPEN: {
|
case ProposalState.STATE_OPEN: {
|
||||||
voteDetails = (voteState === 'Yes' && (
|
voteDetails = (
|
||||||
<>
|
<span className={nowToEnactmentInHours < 6 ? 'text-vega-pink' : ''}>
|
||||||
{t('youVoted')} {t('voteState_Yes')}
|
{formatDistanceToNowStrict(new Date(proposal?.terms.closingDatetime))}{' '}
|
||||||
</>
|
{t('left to vote')}
|
||||||
)) ||
|
</span>
|
||||||
(voteState === 'No' && (
|
);
|
||||||
<>
|
|
||||||
{t('youVoted')} {t('voteState_No')}
|
|
||||||
</>
|
|
||||||
)) || (
|
|
||||||
<>
|
|
||||||
{formatDistanceToNowStrict(
|
|
||||||
new Date(proposal?.terms.closingDatetime)
|
|
||||||
)}{' '}
|
|
||||||
{t('left to vote')}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
voteStatus =
|
voteStatus =
|
||||||
(isUpdateMarket &&
|
(isUpdateMarket &&
|
||||||
(updateMarketWillPass ? (
|
(updateMarketWillPass ? (
|
||||||
<>
|
<>
|
||||||
{t('Set to')}{' '}
|
{t('currentlySetTo')} {t('pass')} {updateMarketVotePassMethod}
|
||||||
<StatusPass>
|
|
||||||
{t('pass')} {updateMarketVotePassMethod}
|
|
||||||
</StatusPass>
|
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{t('Set to')} {t('fail')}
|
{t('currentlySetTo')} {t('fail')}
|
||||||
</>
|
</>
|
||||||
))) ||
|
))) ||
|
||||||
(!participationMet && <ParticipationNotReached />) ||
|
|
||||||
(!majorityMet && <MajorityNotReached />) ||
|
|
||||||
(willPassByTokenVote ? (
|
(willPassByTokenVote ? (
|
||||||
<>
|
<>
|
||||||
{t('Set to')} {t('pass')}
|
{t('currentlySetTo')} {t('pass')}
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{t('Set to')} {t('fail')}
|
{t('currentlySetTo')} {t('fail')}
|
||||||
</>
|
</>
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ProposalState.STATE_DECLINED: {
|
|
||||||
voteStatus =
|
|
||||||
(!participationMet && <ParticipationNotReached />) ||
|
|
||||||
(!majorityMet && <MajorityNotReached />);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ProposalState.STATE_REJECTED: {
|
case ProposalState.STATE_REJECTED: {
|
||||||
voteStatus = proposal?.rejectionReason && (
|
voteStatus = proposal?.rejectionReason && (
|
||||||
<>{t(ProposalRejectionReasonMapping[proposal.rejectionReason])}</>
|
<>{t(ProposalRejectionReasonMapping[proposal.rejectionReason])}</>
|
||||||
@@ -155,29 +114,17 @@ export const ProposalsListItemDetails = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-[1fr_auto] mt-4 items-start gap-2 text-sm">
|
<div className="mt-4 items-start text-sm">
|
||||||
{voteDetails && (
|
<div className="flex items-center gap-2 text-vega-light-300 mb-2">
|
||||||
<div
|
{voteDetails && <span data-testid="vote-details">{voteDetails}</span>}
|
||||||
className="col-start-1 row-start-2 text-vega-light-300"
|
{voteDetails && voteStatus && <span>·</span>}
|
||||||
data-testid="vote-details"
|
{voteStatus && <span data-testid="vote-status">{voteStatus}</span>}
|
||||||
>
|
</div>
|
||||||
{voteDetails}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{voteStatus && (
|
|
||||||
<div
|
|
||||||
className="col-start-2 row-start-1 justify-self-end"
|
|
||||||
data-testid="vote-status"
|
|
||||||
>
|
|
||||||
{voteStatus}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{proposal?.id && (
|
{proposal?.id && (
|
||||||
<div className="col-start-2 row-start-2 justify-self-end">
|
<Link to={`${Routes.PROPOSALS}/${proposal.id}`}>
|
||||||
<Link to={`${Routes.PROPOSALS}/${proposal.id}`}>
|
<Button data-testid="view-proposal-btn">{t('viewDetails')}</Button>
|
||||||
<Button data-testid="view-proposal-btn">{t('View')}</Button>
|
</Link>
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
+8
-3
@@ -3,18 +3,23 @@ import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
|||||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
interface ProposalsListItemProps {
|
interface ProposalsListItemProps {
|
||||||
proposal?: ProposalFieldsFragment | ProposalQuery['proposal'] | null;
|
proposal?: ProposalFieldsFragment | ProposalQuery['proposal'] | null;
|
||||||
|
networkParams: Partial<NetworkParamsResult> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
|
export const ProposalsListItem = ({
|
||||||
if (!proposal || !proposal.id) return null;
|
proposal,
|
||||||
|
networkParams,
|
||||||
|
}: ProposalsListItemProps) => {
|
||||||
|
if (!proposal || !proposal.id || !networkParams) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li id={proposal.id} data-testid="proposals-list-item">
|
<li id={proposal.id} data-testid="proposals-list-item">
|
||||||
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
||||||
<ProposalHeader proposal={proposal} />
|
<ProposalHeader proposal={proposal} networkParams={networkParams} />
|
||||||
<ProposalsListItemDetails proposal={proposal} />
|
<ProposalsListItemDetails proposal={proposal} />
|
||||||
</RoundedWrapper>
|
</RoundedWrapper>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
+45
-21
@@ -88,31 +88,39 @@ afterAll(() => {
|
|||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('../vote-details/use-user-vote', () => ({
|
||||||
|
useUserVote: jest.fn().mockImplementation(() => ({ voteState: 'NotCast' })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Proposals list', () => {
|
describe('Proposals list', () => {
|
||||||
it('Render a page title and link to the make proposal form', () => {
|
it('Render a page title and link to the make proposal form', async () => {
|
||||||
render(renderComponent([]));
|
render(renderComponent([]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.getByText('Proposals')).toBeInTheDocument();
|
expect(screen.getByText('Proposals')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('new-proposal-link')).toBeInTheDocument();
|
expect(screen.getByTestId('new-proposal-link')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Will hide filter if no proposals', () => {
|
it('Will hide filter if no proposals', async () => {
|
||||||
render(renderComponent([]));
|
render(renderComponent([]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(
|
expect(
|
||||||
screen.queryByTestId('proposals-list-filter')
|
screen.queryByTestId('proposals-list-filter')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Will show filter if there are proposals', () => {
|
it('Will show filter if there are proposals', async () => {
|
||||||
render(renderComponent([enactedProposalClosedLastWeek]));
|
render(renderComponent([enactedProposalClosedLastWeek]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.queryByTestId('proposals-list-filter')).toBeInTheDocument();
|
expect(screen.queryByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Will render a link to rejected proposals', () => {
|
it('Will render a link to rejected proposals', async () => {
|
||||||
render(renderComponent([]));
|
render(renderComponent([]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.getByText('See rejected proposals')).toBeInTheDocument();
|
expect(screen.getByText('See rejected proposals')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Places proposals correctly in open or closed lists', () => {
|
it('Places proposals correctly in open or closed lists', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([
|
renderComponent([
|
||||||
openProposalClosesNextWeek,
|
openProposalClosesNextWeek,
|
||||||
@@ -121,6 +129,7 @@ describe('Proposals list', () => {
|
|||||||
failedProposalClosedLastMonth,
|
failedProposalClosedLastMonth,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
const openProposals = within(screen.getByTestId('open-proposals'));
|
const openProposals = within(screen.getByTestId('open-proposals'));
|
||||||
const closedProposals = within(screen.getByTestId('closed-proposals'));
|
const closedProposals = within(screen.getByTestId('closed-proposals'));
|
||||||
expect(openProposals.getAllByTestId('proposals-list-item').length).toBe(2);
|
expect(openProposals.getAllByTestId('proposals-list-item').length).toBe(2);
|
||||||
@@ -129,42 +138,47 @@ describe('Proposals list', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays info on no proposals', () => {
|
it('Displays info on no proposals', async () => {
|
||||||
render(renderComponent([]));
|
render(renderComponent([]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('no-open-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('no-open-proposals')).toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('closed-proposals')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('closed-proposals')).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('no-closed-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('no-closed-proposals')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays info on no open proposals if only closed are present', () => {
|
it('Displays info on no open proposals if only closed are present', async () => {
|
||||||
render(renderComponent([enactedProposalClosedLastWeek]));
|
render(renderComponent([enactedProposalClosedLastWeek]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('no-open-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('no-open-proposals')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('closed-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('closed-proposals')).toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('no-closed-proposals')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('no-closed-proposals')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays info on no closed proposals if only open are present', () => {
|
it('Displays info on no closed proposals if only open are present', async () => {
|
||||||
render(renderComponent([openProposalClosesNextWeek]));
|
render(renderComponent([openProposalClosesNextWeek]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.getByTestId('open-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('open-proposals')).toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('no-open-proposals')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('no-open-proposals')).not.toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('closed-proposals')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('closed-proposals')).not.toBeInTheDocument();
|
||||||
expect(screen.getByTestId('no-closed-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('no-closed-proposals')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Opens filter form when button is clicked', () => {
|
it('Opens filter form when button is clicked', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Filters list by text - party id', () => {
|
it('Filters list by text - party id', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'bvcx' },
|
target: { value: 'bvcx' },
|
||||||
@@ -174,10 +188,11 @@ describe('Proposals list', () => {
|
|||||||
expect(container.querySelector('#proposal1')).not.toBeInTheDocument();
|
expect(container.querySelector('#proposal1')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Filters list by text - proposal id', () => {
|
it('Filters list by text - proposal id', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'proposal1' },
|
target: { value: 'proposal1' },
|
||||||
@@ -187,10 +202,11 @@ describe('Proposals list', () => {
|
|||||||
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Filters list by text - check for substring matching', () => {
|
it('Filters list by text - check for substring matching', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'osal1' },
|
target: { value: 'osal1' },
|
||||||
@@ -200,10 +216,11 @@ describe('Proposals list', () => {
|
|||||||
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('When filter is used, clear button is visible', () => {
|
it('When filter is used, clear button is visible', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'test' },
|
target: { value: 'test' },
|
||||||
@@ -211,10 +228,11 @@ describe('Proposals list', () => {
|
|||||||
expect(screen.getByTestId('clear-filter')).toBeInTheDocument();
|
expect(screen.getByTestId('clear-filter')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('When clear filter button is used, input is cleared', () => {
|
it('When clear filter button is used, input is cleared', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'test' },
|
target: { value: 'test' },
|
||||||
@@ -225,37 +243,41 @@ describe('Proposals list', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays a toggle for closed proposals if there are both closed governance proposals and closed upgrade proposals', () => {
|
it('Displays a toggle for closed proposals if there are both closed governance proposals and closed upgrade proposals', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent(
|
renderComponent(
|
||||||
[enactedProposalClosedLastWeek],
|
[enactedProposalClosedLastWeek],
|
||||||
[closedProtocolUpgradeProposal]
|
[closedProtocolUpgradeProposal]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(screen.getByTestId('toggle-closed-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('toggle-closed-proposals')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Does not display a toggle for closed proposals if there are only closed upgrade proposals', () => {
|
it('Does not display a toggle for closed proposals if there are only closed upgrade proposals', async () => {
|
||||||
render(renderComponent([], [closedProtocolUpgradeProposal]));
|
render(renderComponent([], [closedProtocolUpgradeProposal]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(
|
expect(
|
||||||
screen.queryByTestId('toggle-closed-proposals')
|
screen.queryByTestId('toggle-closed-proposals')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Does not display a toggle for closed proposals if there are only closed governance proposals', () => {
|
it('Does not display a toggle for closed proposals if there are only closed governance proposals', async () => {
|
||||||
render(renderComponent([enactedProposalClosedLastWeek]));
|
render(renderComponent([enactedProposalClosedLastWeek]));
|
||||||
|
await screen.findByTestId('proposals-list');
|
||||||
expect(
|
expect(
|
||||||
screen.queryByTestId('toggle-closed-proposals')
|
screen.queryByTestId('toggle-closed-proposals')
|
||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Does not display a toggle for closed proposals if the proposal filter is engaged', () => {
|
it('Does not display a toggle for closed proposals if the proposal filter is engaged', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent(
|
renderComponent(
|
||||||
[enactedProposalClosedLastWeek],
|
[enactedProposalClosedLastWeek],
|
||||||
[closedProtocolUpgradeProposal]
|
[closedProtocolUpgradeProposal]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
await screen.findByTestId('proposal-filter-toggle');
|
||||||
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
|
||||||
fireEvent.change(screen.getByTestId('filter-input'), {
|
fireEvent.change(screen.getByTestId('filter-input'), {
|
||||||
target: { value: 'test' },
|
target: { value: 'test' },
|
||||||
@@ -265,7 +287,7 @@ describe('Proposals list', () => {
|
|||||||
).not.toBeInTheDocument();
|
).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays closed governance proposals by default due to default for the toggle', () => {
|
it('Displays closed governance proposals by default due to default for the toggle', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent(
|
renderComponent(
|
||||||
[enactedProposalClosedLastWeek],
|
[enactedProposalClosedLastWeek],
|
||||||
@@ -273,17 +295,19 @@ describe('Proposals list', () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
expect(
|
expect(
|
||||||
screen.getByTestId('closed-governance-proposals')
|
await screen.findByTestId('closed-governance-proposals')
|
||||||
).toBeInTheDocument();
|
).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays closed upgrade proposals when the toggle is clicked', () => {
|
it('Displays closed upgrade proposals when the toggle is clicked', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent(
|
renderComponent(
|
||||||
[enactedProposalClosedLastWeek],
|
[enactedProposalClosedLastWeek],
|
||||||
[closedProtocolUpgradeProposal]
|
[closedProtocolUpgradeProposal]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await screen.findByTestId('toggle-closed-proposals');
|
||||||
fireEvent.click(screen.getByText('Network upgrades'));
|
fireEvent.click(screen.getByText('Network upgrades'));
|
||||||
expect(screen.getByTestId('closed-upgrade-proposals')).toBeInTheDocument();
|
expect(screen.getByTestId('closed-upgrade-proposals')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|||||||
+180
-146
@@ -8,6 +8,7 @@ import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-
|
|||||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import {
|
import {
|
||||||
|
AsyncRenderer,
|
||||||
Button,
|
Button,
|
||||||
Toggle,
|
Toggle,
|
||||||
VegaIcon,
|
VegaIcon,
|
||||||
@@ -19,6 +20,10 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
|
import { DocsLinks, ExternalLinks } from '@vegaprotocol/environment';
|
||||||
|
import {
|
||||||
|
NetworkParams,
|
||||||
|
useNetworkParams,
|
||||||
|
} from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
interface ProposalsListProps {
|
interface ProposalsListProps {
|
||||||
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
proposals: Array<ProposalFieldsFragment | ProposalQuery['proposal']>;
|
||||||
@@ -70,6 +75,20 @@ export const ProposalsList = ({
|
|||||||
lastBlockHeight,
|
lastBlockHeight,
|
||||||
}: ProposalsListProps) => {
|
}: ProposalsListProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
params: networkParams,
|
||||||
|
loading: networkParamsLoading,
|
||||||
|
error: networkParamsError,
|
||||||
|
} = useNetworkParams([
|
||||||
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajorityLP,
|
||||||
|
NetworkParams.governance_proposal_asset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||||
|
]);
|
||||||
|
|
||||||
const [filterString, setFilterString] = useState('');
|
const [filterString, setFilterString] = useState('');
|
||||||
const [closedProposalsView, setClosedProposalsView] =
|
const [closedProposalsView, setClosedProposalsView] =
|
||||||
useState<ClosedProposalsViewOptions>(
|
useState<ClosedProposalsViewOptions>(
|
||||||
@@ -134,166 +153,181 @@ export const ProposalsList = ({
|
|||||||
p?.party?.id?.toString().includes(filterString);
|
p?.party?.id?.toString().includes(filterString);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AsyncRenderer
|
||||||
<div className="grid xs:grid-cols-2 items-center">
|
loading={networkParamsLoading}
|
||||||
<Heading
|
error={networkParamsError}
|
||||||
centerContent={false}
|
data={networkParams}
|
||||||
marginBottom={false}
|
>
|
||||||
title={t('pageTitleProposals')}
|
<div data-testid="proposals-list">
|
||||||
/>
|
<div className="grid xs:grid-cols-2 items-center">
|
||||||
|
<Heading
|
||||||
|
centerContent={false}
|
||||||
|
marginBottom={false}
|
||||||
|
title={t('pageTitleProposals')}
|
||||||
|
/>
|
||||||
|
|
||||||
{DocsLinks && (
|
{DocsLinks && (
|
||||||
<div className="xs:justify-self-end" data-testid="new-proposal-link">
|
<div
|
||||||
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
className="xs:justify-self-end"
|
||||||
<Button variant="primary" size="sm">
|
data-testid="new-proposal-link"
|
||||||
<div className="flex items-center gap-1">
|
>
|
||||||
{t('NewProposal')}
|
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
|
||||||
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
|
<Button variant="primary" size="sm">
|
||||||
</div>
|
<div className="flex items-center gap-1">
|
||||||
</Button>
|
{t('NewProposal')}
|
||||||
</ExternalLink>
|
<VegaIcon name={VegaIconNames.OPEN_EXTERNAL} size={13} />
|
||||||
</div>
|
</div>
|
||||||
|
</Button>
|
||||||
|
</ExternalLink>
|
||||||
|
</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.`
|
||||||
|
)}{' '}
|
||||||
|
<ExternalLink
|
||||||
|
data-testid="proposal-documentation-link"
|
||||||
|
href={ExternalLinks.GOVERNANCE_PAGE}
|
||||||
|
className="text-white"
|
||||||
|
>
|
||||||
|
{t(`Find out more about Vega governance`)}
|
||||||
|
</ExternalLink>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{proposals.length > 0 && (
|
||||||
|
<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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="mb-8">
|
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
||||||
{t(
|
<SubHeading title={t('openProposals')} />
|
||||||
`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.`
|
|
||||||
)}{' '}
|
|
||||||
<ExternalLink
|
|
||||||
data-testid="proposal-documentation-link"
|
|
||||||
href={ExternalLinks.GOVERNANCE_PAGE}
|
|
||||||
className="text-white"
|
|
||||||
>
|
|
||||||
{t(`Find out more about Vega governance`)}
|
|
||||||
</ExternalLink>
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{proposals.length > 0 && (
|
{sortedProposals.open.length > 0 ||
|
||||||
<ProposalsListFilter
|
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
||||||
filterString={filterString}
|
<ul data-testid="open-proposals">
|
||||||
setFilterString={(value) => {
|
{filterString.length < 1 &&
|
||||||
setFilterString(value);
|
sortedProtocolUpgradeProposals.open.map((proposal) => (
|
||||||
if (value.length > 0) {
|
<ProtocolUpgradeProposalsListItem
|
||||||
// If the filter is engaged, ensure the user is viewing governance proposals,
|
key={proposal.upgradeBlockHeight}
|
||||||
// as network upgrades do not have IDs to filter by and will be excluded.
|
proposal={proposal}
|
||||||
setClosedProposalsView(
|
/>
|
||||||
ClosedProposalsViewOptions.NetworkGovernance
|
))}
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
|
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
||||||
<SubHeading title={t('openProposals')} />
|
<ProposalsListItem
|
||||||
|
key={proposal?.id}
|
||||||
{sortedProposals.open.length > 0 ||
|
|
||||||
sortedProtocolUpgradeProposals.open.length > 0 ? (
|
|
||||||
<ul data-testid="open-proposals">
|
|
||||||
{filterString.length < 1 &&
|
|
||||||
sortedProtocolUpgradeProposals.open.map((proposal) => (
|
|
||||||
<ProtocolUpgradeProposalsListItem
|
|
||||||
key={proposal.upgradeBlockHeight}
|
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
|
networkParams={networkParams}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<p className="mb-0" data-testid="no-open-proposals">
|
||||||
|
{t('noOpenProposals')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
|
||||||
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
|
<section className="relative">
|
||||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
<SubHeading title={t('closedProposals')} />
|
||||||
))}
|
{sortedProposals.closed.length > 0 ||
|
||||||
</ul>
|
sortedProtocolUpgradeProposals.closed.length > 0 ? (
|
||||||
) : (
|
<>
|
||||||
<p className="mb-0" data-testid="no-open-proposals">
|
{
|
||||||
{t('noOpenProposals')}
|
// We need both the closed proposals and closed protocol upgrade
|
||||||
</p>
|
// 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
|
||||||
</section>
|
// do not have the necessary fields for filtering.
|
||||||
|
sortedProposals.closed.length > 0 &&
|
||||||
<section className="relative">
|
sortedProtocolUpgradeProposals.closed.length > 0 &&
|
||||||
<SubHeading title={t('closedProposals')} />
|
filterString.length < 1 && (
|
||||||
{sortedProposals.closed.length > 0 ||
|
<div
|
||||||
sortedProtocolUpgradeProposals.closed.length > 0 ? (
|
className="grid w-full justify-end xl:-mt-12 pb-6"
|
||||||
<>
|
data-testid="toggle-closed-proposals"
|
||||||
{
|
>
|
||||||
// We need both the closed proposals and closed protocol upgrade
|
<div className="w-[440px]">
|
||||||
// proposals to be present for there to be a toggle. It also gets
|
<Toggle
|
||||||
// hidden if the user has filtered the list, as the upgrade proposals
|
name="closed-proposals-toggle"
|
||||||
// do not have the necessary fields for filtering.
|
toggles={[
|
||||||
sortedProposals.closed.length > 0 &&
|
{
|
||||||
sortedProtocolUpgradeProposals.closed.length > 0 &&
|
label: t(
|
||||||
filterString.length < 1 && (
|
ClosedProposalsViewOptions.NetworkGovernance
|
||||||
<div
|
),
|
||||||
className="grid w-full justify-end xl:-mt-12 pb-6"
|
value:
|
||||||
data-testid="toggle-closed-proposals"
|
ClosedProposalsViewOptions.NetworkGovernance,
|
||||||
>
|
},
|
||||||
<div className="w-[440px]">
|
{
|
||||||
<Toggle
|
label: t(
|
||||||
name="closed-proposals-toggle"
|
ClosedProposalsViewOptions.NetworkUpgrades
|
||||||
toggles={[
|
),
|
||||||
{
|
value: ClosedProposalsViewOptions.NetworkUpgrades,
|
||||||
label: t(
|
},
|
||||||
ClosedProposalsViewOptions.NetworkGovernance
|
]}
|
||||||
),
|
checkedValue={closedProposalsView}
|
||||||
value: ClosedProposalsViewOptions.NetworkGovernance,
|
onChange={(e) =>
|
||||||
},
|
setClosedProposalsView(
|
||||||
{
|
e.target.value as ClosedProposalsViewOptions
|
||||||
label: t(
|
)
|
||||||
ClosedProposalsViewOptions.NetworkUpgrades
|
}
|
||||||
),
|
/>
|
||||||
value: ClosedProposalsViewOptions.NetworkUpgrades,
|
</div>
|
||||||
},
|
|
||||||
]}
|
|
||||||
checkedValue={closedProposalsView}
|
|
||||||
onChange={(e) =>
|
|
||||||
setClosedProposalsView(
|
|
||||||
e.target.value as ClosedProposalsViewOptions
|
|
||||||
)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
<ul data-testid="closed-proposals">
|
<ul data-testid="closed-proposals">
|
||||||
{closedProposalsView ===
|
{closedProposalsView ===
|
||||||
ClosedProposalsViewOptions.NetworkUpgrades && (
|
ClosedProposalsViewOptions.NetworkUpgrades && (
|
||||||
<div data-testid="closed-upgrade-proposals">
|
<div data-testid="closed-upgrade-proposals">
|
||||||
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
|
||||||
<ProtocolUpgradeProposalsListItem
|
<ProtocolUpgradeProposalsListItem
|
||||||
key={proposal.upgradeBlockHeight}
|
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}
|
proposal={proposal}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</ul>
|
|
||||||
</>
|
|
||||||
) : (
|
|
||||||
<p className="mb-0" data-testid="no-closed-proposals">
|
|
||||||
{t('noClosedProposals')}
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
|
{closedProposalsView ===
|
||||||
{t('seeRejectedProposals')}
|
ClosedProposalsViewOptions.NetworkGovernance && (
|
||||||
</Link>
|
<div data-testid="closed-governance-proposals">
|
||||||
</>
|
{sortedProposals.closed
|
||||||
|
.filter(filterPredicate)
|
||||||
|
.map((proposal) => (
|
||||||
|
<ProposalsListItem
|
||||||
|
key={proposal?.id}
|
||||||
|
proposal={proposal}
|
||||||
|
networkParams={networkParams}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<p className="mb-0" data-testid="no-closed-proposals">
|
||||||
|
{t('noClosedProposals')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
|
||||||
|
{t('seeRejectedProposals')}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+24
-10
@@ -5,7 +5,7 @@ import { BrowserRouter as Router } from 'react-router-dom';
|
|||||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||||
import { RejectedProposalsList } from './rejected-proposals-list';
|
import { RejectedProposalsList } from './rejected-proposals-list';
|
||||||
import { ProposalState } from '@vegaprotocol/types';
|
import { ProposalState } from '@vegaprotocol/types';
|
||||||
import { render, screen, within } from '@testing-library/react';
|
import { render, screen, waitFor, within } from '@testing-library/react';
|
||||||
import {
|
import {
|
||||||
mockWalletContext,
|
mockWalletContext,
|
||||||
networkParamsQueryMock,
|
networkParamsQueryMock,
|
||||||
@@ -55,24 +55,38 @@ afterAll(() => {
|
|||||||
jest.useRealTimers();
|
jest.useRealTimers();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('../vote-details/use-user-vote', () => ({
|
||||||
|
useUserVote: jest.fn().mockImplementation(() => ({ voteState: 'NotCast' })),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('Rejected proposals list', () => {
|
describe('Rejected proposals list', () => {
|
||||||
it('Renders a list of proposals', () => {
|
it('Renders a list of proposals', async () => {
|
||||||
render(
|
render(
|
||||||
renderComponent([
|
renderComponent([
|
||||||
rejectedProposalClosedLastMonth,
|
rejectedProposalClosedLastMonth,
|
||||||
rejectedProposalClosesNextWeek,
|
rejectedProposalClosesNextWeek,
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
const rejectedProposals = within(screen.getByTestId('rejected-proposals'));
|
|
||||||
const rejectedProposalsItems = rejectedProposals.getAllByTestId(
|
await waitFor(() => {
|
||||||
'proposals-list-item'
|
const rejectedProposals = within(
|
||||||
);
|
screen.getByTestId('rejected-proposals')
|
||||||
expect(rejectedProposalsItems).toHaveLength(2);
|
);
|
||||||
|
const rejectedProposalsItems = rejectedProposals.getAllByTestId(
|
||||||
|
'proposals-list-item'
|
||||||
|
);
|
||||||
|
expect(rejectedProposalsItems).toHaveLength(2);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Displays text when there are no proposals', () => {
|
it('Displays text when there are no proposals', async () => {
|
||||||
render(renderComponent([]));
|
render(renderComponent([]));
|
||||||
expect(screen.queryByTestId('rejected-proposals')).not.toBeInTheDocument();
|
|
||||||
expect(screen.getByTestId('no-rejected-proposals')).toBeInTheDocument();
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('no-rejected-proposals')).toBeInTheDocument();
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('rejected-proposals')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+29
-3
@@ -1,10 +1,15 @@
|
|||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { Heading } from '../../../../components/heading';
|
import { Heading } from '../../../../components/heading';
|
||||||
import { ProposalsListItem } from '../proposals-list-item';
|
import { ProposalsListItem } from '../proposals-list-item';
|
||||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import {
|
||||||
|
NetworkParams,
|
||||||
|
useNetworkParams,
|
||||||
|
} from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
interface ProposalsListProps {
|
interface ProposalsListProps {
|
||||||
proposals: Array<ProposalQuery['proposal'] | ProposalFieldsFragment>;
|
proposals: Array<ProposalQuery['proposal'] | ProposalFieldsFragment>;
|
||||||
@@ -12,6 +17,19 @@ interface ProposalsListProps {
|
|||||||
|
|
||||||
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
const {
|
||||||
|
params: networkParams,
|
||||||
|
loading: networkParamsLoading,
|
||||||
|
error: networkParamsError,
|
||||||
|
} = useNetworkParams([
|
||||||
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajorityLP,
|
||||||
|
NetworkParams.governance_proposal_asset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||||
|
]);
|
||||||
const [filterString, setFilterString] = useState('');
|
const [filterString, setFilterString] = useState('');
|
||||||
|
|
||||||
const filterPredicate = (
|
const filterPredicate = (
|
||||||
@@ -21,7 +39,11 @@ export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
|||||||
p?.party?.id?.toString().includes(filterString);
|
p?.party?.id?.toString().includes(filterString);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<AsyncRenderer
|
||||||
|
loading={networkParamsLoading}
|
||||||
|
error={networkParamsError}
|
||||||
|
data={networkParams}
|
||||||
|
>
|
||||||
<Heading title={t('pageTitleRejectedProposals')} />
|
<Heading title={t('pageTitleRejectedProposals')} />
|
||||||
<ProposalsListFilter
|
<ProposalsListFilter
|
||||||
filterString={filterString}
|
filterString={filterString}
|
||||||
@@ -31,7 +53,11 @@ export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
|||||||
{proposals.length > 0 ? (
|
{proposals.length > 0 ? (
|
||||||
<ul data-testid="rejected-proposals">
|
<ul data-testid="rejected-proposals">
|
||||||
{proposals.filter(filterPredicate).map((proposal) => (
|
{proposals.filter(filterPredicate).map((proposal) => (
|
||||||
<ProposalsListItem key={proposal?.id} proposal={proposal} />
|
<ProposalsListItem
|
||||||
|
key={proposal?.id}
|
||||||
|
proposal={proposal}
|
||||||
|
networkParams={networkParams}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
@@ -40,6 +66,6 @@ export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</section>
|
</section>
|
||||||
</>
|
</AsyncRenderer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
+36
-8
@@ -4,16 +4,20 @@ import { render } from '@testing-library/react';
|
|||||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
||||||
import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info';
|
import { ProtocolUpgradeProposalDetailInfo } from './protocol-upgrade-proposal-detail-info';
|
||||||
|
|
||||||
const renderComponent = () =>
|
const PROPOSAL = {
|
||||||
|
vegaReleaseTag: 'v0.1.234',
|
||||||
|
status:
|
||||||
|
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
||||||
|
upgradeBlockHeight: '12345',
|
||||||
|
approvers: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderComponent = (lastBlockHeight?: string, time?: string) =>
|
||||||
render(
|
render(
|
||||||
<ProtocolUpgradeProposalDetailInfo
|
<ProtocolUpgradeProposalDetailInfo
|
||||||
proposal={{
|
proposal={PROPOSAL}
|
||||||
vegaReleaseTag: 'v0.1.234',
|
lastBlockHeight={lastBlockHeight}
|
||||||
status:
|
time={time}
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
|
||||||
upgradeBlockHeight: '12345',
|
|
||||||
approvers: [],
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -43,4 +47,28 @@ describe('ProtocolUpgradeProposalDetailInfo', () => {
|
|||||||
'v0.1.234'
|
'v0.1.234'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should display estimated time if proposal is still pending', () => {
|
||||||
|
const { getByTestId } = renderComponent('12', 'time');
|
||||||
|
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
|
||||||
|
'Estimated time to upgrade'
|
||||||
|
);
|
||||||
|
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display upgraded at if proposal is done', () => {
|
||||||
|
const { getByTestId } = renderComponent('123456', 'time');
|
||||||
|
expect(getByTestId('protocol-upgrade-time-label')).toHaveTextContent(
|
||||||
|
'Upgraded at'
|
||||||
|
);
|
||||||
|
expect(getByTestId('protocol-upgrade-time')).toHaveTextContent('time');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not display time if none provided', () => {
|
||||||
|
const { queryByTestId } = renderComponent('123456', undefined);
|
||||||
|
expect(
|
||||||
|
queryByTestId('protocol-upgrade-time-label')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
expect(queryByTestId('protocol-upgrade-time')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+30
-3
@@ -5,19 +5,33 @@ import {
|
|||||||
RoundedWrapper,
|
RoundedWrapper,
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
|
import * as Types from '@vegaprotocol/types';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
|
|
||||||
export interface ProtocolUpgradeProposalDetailInfoProps {
|
export interface ProtocolUpgradeProposalDetailInfoProps {
|
||||||
proposal: ProtocolUpgradeProposalFieldsFragment;
|
proposal: ProtocolUpgradeProposalFieldsFragment;
|
||||||
lastBlockHeight?: string;
|
lastBlockHeight?: string;
|
||||||
|
time?: string | ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ProtocolUpgradeProposalDetailInfo = ({
|
export const ProtocolUpgradeProposalDetailInfo = ({
|
||||||
proposal,
|
proposal,
|
||||||
lastBlockHeight,
|
lastBlockHeight,
|
||||||
|
time,
|
||||||
}: ProtocolUpgradeProposalDetailInfoProps) => {
|
}: ProtocolUpgradeProposalDetailInfoProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const shouldShowUpgradeTime =
|
||||||
|
![
|
||||||
|
Types.ProtocolUpgradeProposalStatus
|
||||||
|
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
|
||||||
|
Types.ProtocolUpgradeProposalStatus
|
||||||
|
.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
|
||||||
|
].includes(proposal.status) && time;
|
||||||
|
|
||||||
|
const pending = Number(proposal.upgradeBlockHeight) > Number(lastBlockHeight);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-10">
|
<div className="mb-10">
|
||||||
<SubHeading title={t('proposal')} />
|
<SubHeading title={t('proposal')} />
|
||||||
@@ -29,13 +43,26 @@ export const ProtocolUpgradeProposalDetailInfo = ({
|
|||||||
<span data-testid="protocol-upgrade-block-height">
|
<span data-testid="protocol-upgrade-block-height">
|
||||||
{proposal.upgradeBlockHeight}{' '}
|
{proposal.upgradeBlockHeight}{' '}
|
||||||
{lastBlockHeight && (
|
{lastBlockHeight && (
|
||||||
<>
|
<span className="text-vega-light-300 dark:text-vega-dark-300">
|
||||||
({t('currently')} {lastBlockHeight})
|
({t('currently')} {lastBlockHeight})
|
||||||
</>
|
</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
</KeyValueTableRow>
|
</KeyValueTableRow>
|
||||||
|
|
||||||
|
{shouldShowUpgradeTime && (
|
||||||
|
<KeyValueTableRow>
|
||||||
|
<span
|
||||||
|
data-testid="protocol-upgrade-time-label"
|
||||||
|
className="uppercase"
|
||||||
|
>
|
||||||
|
{pending ? t('Estimated time to upgrade') : t('Upgraded at')}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<span data-testid="protocol-upgrade-time">{time}</span>
|
||||||
|
</KeyValueTableRow>
|
||||||
|
)}
|
||||||
|
|
||||||
<KeyValueTableRow>
|
<KeyValueTableRow>
|
||||||
<span className="uppercase">{t('state')}</span>
|
<span className="uppercase">{t('state')}</span>
|
||||||
|
|
||||||
|
|||||||
-37
@@ -20,43 +20,6 @@ const renderComponent = (proposal: ProtocolUpgradeProposalFieldsFragment) =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
describe('ProtocolUpgradeProposalsListItem', () => {
|
describe('ProtocolUpgradeProposalsListItem', () => {
|
||||||
it('renders the correct status icon for each proposal status', () => {
|
|
||||||
const statuses = [
|
|
||||||
{
|
|
||||||
status:
|
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED,
|
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-rejected',
|
|
||||||
text: 'Declined by validators',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status:
|
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING,
|
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-pending',
|
|
||||||
text: 'Waiting for validator votes',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status:
|
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
|
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-approved',
|
|
||||||
text: 'Approved by validators',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
status:
|
|
||||||
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED,
|
|
||||||
icon: 'protocol-upgrade-proposal-status-icon-unspecified',
|
|
||||||
text: 'Unspecified',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
statuses.forEach(({ status, icon, text }) => {
|
|
||||||
renderComponent({ ...proposal, status });
|
|
||||||
const statusIcon = screen.getByTestId(icon);
|
|
||||||
const textContent = screen.getByText(text);
|
|
||||||
expect(statusIcon).toBeInTheDocument();
|
|
||||||
expect(textContent).toBeInTheDocument();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('renders the correct Vega release tag', () => {
|
it('renders the correct Vega release tag', () => {
|
||||||
renderComponent(proposal);
|
renderComponent(proposal);
|
||||||
const releaseTag = screen.getByTestId(
|
const releaseTag = screen.getByTestId(
|
||||||
|
|||||||
+28
-73
@@ -1,17 +1,10 @@
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import { Button, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||||
Button,
|
|
||||||
Icon,
|
|
||||||
Lozenge,
|
|
||||||
RoundedWrapper,
|
|
||||||
} from '@vegaprotocol/ui-toolkit';
|
|
||||||
import { stripFullStops } from '@vegaprotocol/utils';
|
import { stripFullStops } from '@vegaprotocol/utils';
|
||||||
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
|
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import type { ReactNode } from 'react';
|
|
||||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
|
|
||||||
interface ProtocolProposalsListItemProps {
|
interface ProtocolProposalsListItemProps {
|
||||||
@@ -24,45 +17,21 @@ export const ProtocolUpgradeProposalsListItem = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
if (!proposal || !proposal.upgradeBlockHeight) return null;
|
if (!proposal || !proposal.upgradeBlockHeight) return null;
|
||||||
|
|
||||||
let proposalStatusIcon: ReactNode;
|
|
||||||
|
|
||||||
switch (proposal.status) {
|
|
||||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_REJECTED:
|
|
||||||
proposalStatusIcon = (
|
|
||||||
<span data-testid="protocol-upgrade-proposal-status-icon-rejected">
|
|
||||||
<Icon name={'cross'} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_PENDING:
|
|
||||||
proposalStatusIcon = (
|
|
||||||
<span data-testid="protocol-upgrade-proposal-status-icon-pending">
|
|
||||||
<Icon name={'time'} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED:
|
|
||||||
proposalStatusIcon = (
|
|
||||||
<span data-testid="protocol-upgrade-proposal-status-icon-approved">
|
|
||||||
<Icon name={'tick'} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_UNSPECIFIED:
|
|
||||||
proposalStatusIcon = (
|
|
||||||
<span data-testid="protocol-upgrade-proposal-status-icon-unspecified">
|
|
||||||
<Icon name={'disable'} />
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
id={proposal.upgradeBlockHeight}
|
id={proposal.upgradeBlockHeight}
|
||||||
data-testid="protocol-upgrade-proposals-list-item"
|
data-testid="protocol-upgrade-proposals-list-item"
|
||||||
>
|
>
|
||||||
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
<RoundedWrapper paddingBottom={true} heightFull={true}>
|
||||||
|
<div
|
||||||
|
data-testid="protocol-upgrade-proposal-type"
|
||||||
|
className="flex items-center gap-2 mb-4 text-sm"
|
||||||
|
>
|
||||||
|
<ProposalInfoLabel variant="highlight">
|
||||||
|
{t('networkUpgrade')}
|
||||||
|
</ProposalInfoLabel>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
data-testid="protocol-upgrade-proposal-title"
|
data-testid="protocol-upgrade-proposal-title"
|
||||||
className="text-sm mb-2"
|
className="text-sm mb-2"
|
||||||
@@ -70,30 +39,13 @@ export const ProtocolUpgradeProposalsListItem = ({
|
|||||||
<SubHeading title={`Vega release ${proposal.vegaReleaseTag}`} />
|
<SubHeading title={`Vega release ${proposal.vegaReleaseTag}`} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-sm">
|
<div className="flex items-center gap-4 text-vega-light-200">
|
||||||
<div className="flex gap-2">
|
|
||||||
<div
|
|
||||||
data-testid="protocol-upgrade-proposal-type"
|
|
||||||
className="flex items-center gap-2 mb-4"
|
|
||||||
>
|
|
||||||
<ProposalInfoLabel variant="highlight">
|
|
||||||
{t('networkUpgrade')}
|
|
||||||
</ProposalInfoLabel>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div data-testid="protocol-upgrade-proposal-status">
|
|
||||||
<ProposalInfoLabel>
|
|
||||||
{t(`${proposal.status}`)} {proposalStatusIcon}
|
|
||||||
</ProposalInfoLabel>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
data-testid="protocol-upgrade-proposal-release-tag"
|
data-testid="protocol-upgrade-proposal-release-tag"
|
||||||
className="mb-2"
|
className="mb-2"
|
||||||
>
|
>
|
||||||
<span>{t('vegaReleaseTag')}:</span>{' '}
|
<span>{t('vegaReleaseTag')}:</span>{' '}
|
||||||
<Lozenge>{proposal.vegaReleaseTag}</Lozenge>
|
<span>{proposal.vegaReleaseTag}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
@@ -101,21 +53,24 @@ export const ProtocolUpgradeProposalsListItem = ({
|
|||||||
className="mb-2"
|
className="mb-2"
|
||||||
>
|
>
|
||||||
<span>{t('upgradeBlockHeight')}:</span>{' '}
|
<span>{t('upgradeBlockHeight')}:</span>{' '}
|
||||||
<Lozenge>{proposal.upgradeBlockHeight}</Lozenge>
|
<span>{proposal.upgradeBlockHeight}</span>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 mt-3">
|
|
||||||
<div className="justify-self-end">
|
|
||||||
<Link
|
|
||||||
to={`${Routes.PROTOCOL_UPGRADES}/${stripFullStops(
|
|
||||||
proposal.vegaReleaseTag
|
|
||||||
)}`}
|
|
||||||
>
|
|
||||||
<Button data-testid="view-proposal-btn">{t('View')}</Button>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="text-sm text-vega-light-300 mb-4"
|
||||||
|
data-testid="protocol-upgrade-proposal-status"
|
||||||
|
>
|
||||||
|
{t(`${proposal.status}`)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Link
|
||||||
|
to={`${Routes.PROTOCOL_UPGRADES}/${stripFullStops(
|
||||||
|
proposal.vegaReleaseTag
|
||||||
|
)}`}
|
||||||
|
>
|
||||||
|
<Button data-testid="view-proposal-btn">{t('viewDetails')}</Button>
|
||||||
|
</Link>
|
||||||
</RoundedWrapper>
|
</RoundedWrapper>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ interface VoteButtonsContainerProps {
|
|||||||
voteDatetime: Date | null;
|
voteDatetime: Date | null;
|
||||||
proposalId: string | null;
|
proposalId: string | null;
|
||||||
proposalState: ProposalState;
|
proposalState: ProposalState;
|
||||||
minVoterBalance: string | null;
|
minVoterBalance: string | null | undefined;
|
||||||
spamProtectionMinTokens: string | null;
|
spamProtectionMinTokens: string | null | undefined;
|
||||||
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
submit: (voteValue: VoteValue, proposalId: string | null) => Promise<void>;
|
||||||
dialog: (props: DialogProps) => JSX.Element;
|
dialog: (props: DialogProps) => JSX.Element;
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|||||||
|
|
||||||
interface VoteDetailsProps {
|
interface VoteDetailsProps {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
minVoterBalance: string | null;
|
minVoterBalance: string | null | undefined;
|
||||||
spamProtectionMinTokens: string | null;
|
spamProtectionMinTokens: string | null | undefined;
|
||||||
proposalType: ProposalType | null;
|
proposalType: ProposalType | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useEffect } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
|
|
||||||
import { Proposal } from '../components/proposal';
|
import { Proposal } from '../components/proposal';
|
||||||
@@ -10,12 +10,43 @@ import { ENV } from '../../../config';
|
|||||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||||
import { useAssetQuery } from '@vegaprotocol/assets';
|
import { useAssetQuery } from '@vegaprotocol/assets';
|
||||||
|
import {
|
||||||
|
NetworkParams,
|
||||||
|
useNetworkParams,
|
||||||
|
} from '@vegaprotocol/network-parameters';
|
||||||
|
|
||||||
export const ProposalContainer = () => {
|
export const ProposalContainer = () => {
|
||||||
|
const [
|
||||||
|
mostRecentlyEnactedAssociatedMarketProposal,
|
||||||
|
setMostRecentlyEnactedAssociatedMarketProposal,
|
||||||
|
] = useState(undefined);
|
||||||
const params = useParams<{ proposalId: string }>();
|
const params = useParams<{ proposalId: string }>();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
state: { data: restData },
|
params: networkParams,
|
||||||
|
loading: networkParamsLoading,
|
||||||
|
error: networkParamsError,
|
||||||
|
} = useNetworkParams([
|
||||||
|
NetworkParams.governance_proposal_market_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_asset_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_updateAsset_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_updateNetParam_minVoterBalance,
|
||||||
|
NetworkParams.governance_proposal_freeform_minVoterBalance,
|
||||||
|
NetworkParams.spam_protection_voting_min_tokens,
|
||||||
|
NetworkParams.governance_proposal_market_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateMarket_requiredMajorityLP,
|
||||||
|
NetworkParams.governance_proposal_asset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateAsset_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
||||||
|
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||||
|
]);
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: { data: restData, loading: restLoading, error: restError },
|
||||||
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
|
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
|
||||||
|
|
||||||
const { data, loading, error, refetch } = useProposalQuery({
|
const { data, loading, error, refetch } = useProposalQuery({
|
||||||
fetchPolicy: 'network-only',
|
fetchPolicy: 'network-only',
|
||||||
errorPolicy: 'ignore',
|
errorPolicy: 'ignore',
|
||||||
@@ -23,6 +54,35 @@ export const ProposalContainer = () => {
|
|||||||
skip: !params.proposalId,
|
skip: !params.proposalId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: {
|
||||||
|
data: originalMarketProposalRestData,
|
||||||
|
loading: originalMarketProposalRestLoading,
|
||||||
|
error: originalMarketProposalRestError,
|
||||||
|
},
|
||||||
|
} = useFetch(
|
||||||
|
`${ENV.rest}governance?proposalId=${
|
||||||
|
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||||
|
data?.proposal.terms.change.marketId
|
||||||
|
}`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: {
|
||||||
|
data: previouslyEnactedMarketProposalsRestData,
|
||||||
|
loading: previouslyEnactedMarketProposalsRestLoading,
|
||||||
|
error: previouslyEnactedMarketProposalsRestError,
|
||||||
|
},
|
||||||
|
} = useFetch(
|
||||||
|
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
||||||
|
undefined,
|
||||||
|
true,
|
||||||
|
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: newMarketData,
|
data: newMarketData,
|
||||||
loading: newMarketLoading,
|
loading: newMarketLoading,
|
||||||
@@ -55,6 +115,39 @@ export const ProposalContainer = () => {
|
|||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
previouslyEnactedMarketProposalsRestData &&
|
||||||
|
data?.proposal?.terms.change.__typename === 'UpdateMarket'
|
||||||
|
) {
|
||||||
|
const change = data?.proposal?.terms?.change as { marketId: string };
|
||||||
|
|
||||||
|
const filteredProposals =
|
||||||
|
// @ts-ignore rest data is not typed
|
||||||
|
previouslyEnactedMarketProposalsRestData.connection.edges.filter(
|
||||||
|
// @ts-ignore rest data is not typed
|
||||||
|
({ node }) =>
|
||||||
|
node?.proposal?.terms?.updateMarket?.marketId === change.marketId
|
||||||
|
);
|
||||||
|
|
||||||
|
const sortedProposals = filteredProposals.sort(
|
||||||
|
// @ts-ignore rest data is not typed
|
||||||
|
(a, b) =>
|
||||||
|
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
|
||||||
|
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
setMostRecentlyEnactedAssociatedMarketProposal(
|
||||||
|
sortedProposals[sortedProposals.length - 1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
previouslyEnactedMarketProposalsRestData,
|
||||||
|
params.proposalId,
|
||||||
|
data?.proposal?.terms.change.__typename,
|
||||||
|
data?.proposal?.terms.change,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(refetch, 2000);
|
const interval = setInterval(refetch, 2000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
@@ -62,20 +155,53 @@ export const ProposalContainer = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
loading={loading || newMarketLoading || assetLoading}
|
loading={
|
||||||
error={error || newMarketError || assetError}
|
loading ||
|
||||||
|
newMarketLoading ||
|
||||||
|
assetLoading ||
|
||||||
|
networkParamsLoading ||
|
||||||
|
(restLoading ? (restLoading as boolean) : false) ||
|
||||||
|
(originalMarketProposalRestLoading
|
||||||
|
? (originalMarketProposalRestLoading as boolean)
|
||||||
|
: false) ||
|
||||||
|
(previouslyEnactedMarketProposalsRestLoading
|
||||||
|
? (previouslyEnactedMarketProposalsRestLoading as boolean)
|
||||||
|
: false)
|
||||||
|
}
|
||||||
|
error={
|
||||||
|
error ||
|
||||||
|
newMarketError ||
|
||||||
|
assetError ||
|
||||||
|
restError ||
|
||||||
|
originalMarketProposalRestError ||
|
||||||
|
previouslyEnactedMarketProposalsRestError ||
|
||||||
|
networkParamsError
|
||||||
|
}
|
||||||
data={{
|
data={{
|
||||||
...data,
|
...data,
|
||||||
|
...networkParams,
|
||||||
...(newMarketData ? { newMarketData } : {}),
|
...(newMarketData ? { newMarketData } : {}),
|
||||||
...(assetData ? { assetData } : {}),
|
...(assetData ? { assetData } : {}),
|
||||||
|
...(restData ? { restData } : {}),
|
||||||
|
...(originalMarketProposalRestData
|
||||||
|
? { originalMarketProposalRestData }
|
||||||
|
: {}),
|
||||||
|
...(previouslyEnactedMarketProposalsRestData
|
||||||
|
? { previouslyEnactedMarketProposalsRestData }
|
||||||
|
: {}),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{data?.proposal ? (
|
{data?.proposal ? (
|
||||||
<Proposal
|
<Proposal
|
||||||
proposal={data.proposal}
|
proposal={data.proposal}
|
||||||
|
networkParams={networkParams}
|
||||||
restData={restData}
|
restData={restData}
|
||||||
newMarketData={newMarketData}
|
newMarketData={newMarketData}
|
||||||
assetData={assetData}
|
assetData={assetData}
|
||||||
|
originalMarketProposalRestData={originalMarketProposalRestData}
|
||||||
|
mostRecentlyEnactedAssociatedMarketProposal={
|
||||||
|
mostRecentlyEnactedAssociatedMarketProposal
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ProposalNotFound />
|
<ProposalNotFound />
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ import * as Types from '@vegaprotocol/types';
|
|||||||
import { gql } from '@apollo/client';
|
import { gql } from '@apollo/client';
|
||||||
import * as Apollo from '@apollo/client';
|
import * as Apollo from '@apollo/client';
|
||||||
const defaultOptions = {} as const;
|
const defaultOptions = {} as const;
|
||||||
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
|
export type ProposalFieldsFragment = { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } };
|
||||||
|
|
||||||
export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
export type ProposalsQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||||
|
|
||||||
|
|
||||||
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
export type ProposalsQuery = { __typename?: 'Query', proposalsConnection?: { __typename?: 'ProposalsConnection', edges?: Array<{ __typename?: 'ProposalEdge', node: { __typename?: 'Proposal', id?: string | null, reference: string, state: Types.ProposalState, datetime: any, rejectionReason?: Types.ProposalRejectionReason | null, errorDetails?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string }, party: { __typename?: 'Party', id: string }, terms: { __typename?: 'ProposalTerms', closingDatetime: any, enactmentDatetime?: any | null, change: { __typename?: 'CancelTransfer' } | { __typename: 'NewAsset', name: string, symbol: string, decimals: number, quantum: string, source: { __typename?: 'BuiltinAsset', maxFaucetAmountMint: string } | { __typename?: 'ERC20', contractAddress: string, withdrawThreshold: string, lifetimeLimit: string } } | { __typename?: 'NewFreeform' } | { __typename?: 'NewMarket', instrument: { __typename?: 'InstrumentConfiguration', name: string, code: string, futureProduct?: { __typename?: 'FutureProduct', settlementAsset: { __typename?: 'Asset', symbol: string } } | null } } | { __typename?: 'NewTransfer' } | { __typename?: 'UpdateAsset', quantum: string, assetId: string, source: { __typename?: 'UpdateERC20', lifetimeLimit: string, withdrawThreshold: string } } | { __typename?: 'UpdateMarket', marketId: string } | { __typename?: 'UpdateNetworkParameter', networkParameter: { __typename?: 'NetworkParameter', key: string, value: string } } }, votes: { __typename?: 'ProposalVotes', yes: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string }, no: { __typename?: 'ProposalVoteSide', totalTokens: string, totalNumber: string, totalEquityLikeShareWeight: string } } } } | null> | null } | null };
|
||||||
|
|
||||||
export const ProposalFieldsFragmentDoc = gql`
|
export const ProposalFieldsFragmentDoc = gql`
|
||||||
fragment ProposalFields on Proposal {
|
fragment ProposalFields on Proposal {
|
||||||
|
|||||||
+38
-2
@@ -1,14 +1,23 @@
|
|||||||
import { useEffect, useMemo } from 'react';
|
import { useEffect, useMemo } from 'react';
|
||||||
import { useParams } from 'react-router-dom';
|
import { useParams } from 'react-router-dom';
|
||||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||||
import { removePaginationWrapper, stripFullStops } from '@vegaprotocol/utils';
|
import {
|
||||||
|
convertToCountdownString,
|
||||||
|
formatDateWithLocalTimezone,
|
||||||
|
removePaginationWrapper,
|
||||||
|
stripFullStops,
|
||||||
|
} from '@vegaprotocol/utils';
|
||||||
import * as Schema from '@vegaprotocol/types';
|
import * as Schema from '@vegaprotocol/types';
|
||||||
|
|
||||||
import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal';
|
import { ProtocolUpgradeProposal } from './protocol-upgrade-proposal';
|
||||||
import { ProposalNotFound } from '../components/proposal-not-found';
|
import { ProposalNotFound } from '../components/proposal-not-found';
|
||||||
import { useNodesQuery } from '../../staking/home/__generated__/Nodes';
|
import { useNodesQuery } from '../../staking/home/__generated__/Nodes';
|
||||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||||
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
import {
|
||||||
|
useProtocolUpgradeProposalsQuery,
|
||||||
|
useTimeToUpgrade,
|
||||||
|
} from '@vegaprotocol/proposals';
|
||||||
|
import { useBlockInfo } from '@vegaprotocol/tendermint';
|
||||||
|
|
||||||
export const ProtocolUpgradeProposalContainer = () => {
|
export const ProtocolUpgradeProposalContainer = () => {
|
||||||
const params = useParams<{ proposalReleaseTag: string }>();
|
const params = useParams<{ proposalReleaseTag: string }>();
|
||||||
@@ -58,6 +67,20 @@ export const ProtocolUpgradeProposalContainer = () => {
|
|||||||
);
|
);
|
||||||
}, [nodesData]);
|
}, [nodesData]);
|
||||||
|
|
||||||
|
const pending =
|
||||||
|
Number(protocolUpgradeProposal?.upgradeBlockHeight) >
|
||||||
|
Number(data?.lastBlockHeight);
|
||||||
|
|
||||||
|
const {
|
||||||
|
state: { data: blockInfo },
|
||||||
|
} = useBlockInfo(
|
||||||
|
Number(protocolUpgradeProposal?.upgradeBlockHeight),
|
||||||
|
!pending
|
||||||
|
);
|
||||||
|
const time = useTimeToUpgrade(
|
||||||
|
Number(protocolUpgradeProposal?.upgradeBlockHeight)
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer
|
<AsyncRenderer
|
||||||
loading={loading || nodesLoading}
|
loading={loading || nodesLoading}
|
||||||
@@ -69,6 +92,19 @@ export const ProtocolUpgradeProposalContainer = () => {
|
|||||||
proposal={protocolUpgradeProposal}
|
proposal={protocolUpgradeProposal}
|
||||||
lastBlockHeight={data?.lastBlockHeight}
|
lastBlockHeight={data?.lastBlockHeight}
|
||||||
consensusValidators={consensusValidators}
|
consensusValidators={consensusValidators}
|
||||||
|
time={
|
||||||
|
pending && time ? (
|
||||||
|
convertToCountdownString(time, '0:00:00:00')
|
||||||
|
) : blockInfo?.result ? (
|
||||||
|
<span title={blockInfo.result.block.header.time}>
|
||||||
|
{formatDateWithLocalTimezone(
|
||||||
|
new Date(blockInfo.result.block.header.time)
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
)
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ProposalNotFound />
|
<ProposalNotFound />
|
||||||
|
|||||||
+5
@@ -60,6 +60,11 @@ const mockConsensusValidators: NodesFragmentFragment[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
jest.mock('@vegaprotocol/environment', () => ({
|
||||||
|
useVegaRelease: jest.fn(),
|
||||||
|
useVegaReleases: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
const renderComponent = () =>
|
const renderComponent = () =>
|
||||||
render(
|
render(
|
||||||
<ProtocolUpgradeProposal proposal={mockProposal} consensusValidators={[]} />
|
<ProtocolUpgradeProposal proposal={mockProposal} consensusValidators={[]} />
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
import type { ReactNode } from 'react';
|
||||||
import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header';
|
import { ProtocolUpgradeProposalDetailHeader } from '../components/protocol-upgrade-proposal-detail-header';
|
||||||
import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals';
|
import { ProtocolUpdateProposalDetailApprovals } from '../components/protocol-upgrade-proposal-detail-approvals';
|
||||||
import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info';
|
import { ProtocolUpgradeProposalDetailInfo } from '../components/protocol-upgrade-proposal-detail-info';
|
||||||
import { getNormalisedVotingPower } from '../../staking/shared';
|
import { getNormalisedVotingPower } from '../../staking/shared';
|
||||||
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
|
import type { NodesFragmentFragment } from '../../staking/home/__generated__/Nodes';
|
||||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||||
import { useVegaRelease } from '@vegaprotocol/environment';
|
import { useVegaRelease } from '@vegaprotocol/environment';
|
||||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
@@ -13,6 +14,7 @@ export interface ProtocolUpgradeProposalProps {
|
|||||||
proposal: ProtocolUpgradeProposalFieldsFragment;
|
proposal: ProtocolUpgradeProposalFieldsFragment;
|
||||||
consensusValidators: NodesFragmentFragment[] | null;
|
consensusValidators: NodesFragmentFragment[] | null;
|
||||||
lastBlockHeight?: string;
|
lastBlockHeight?: string;
|
||||||
|
time?: string | ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getConsensusApprovals = (
|
export const getConsensusApprovals = (
|
||||||
@@ -44,6 +46,7 @@ export const ProtocolUpgradeProposal = ({
|
|||||||
proposal,
|
proposal,
|
||||||
lastBlockHeight,
|
lastBlockHeight,
|
||||||
consensusValidators,
|
consensusValidators,
|
||||||
|
time,
|
||||||
}: ProtocolUpgradeProposalProps) => {
|
}: ProtocolUpgradeProposalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
|
const releaseInfo = useVegaRelease(proposal.vegaReleaseTag);
|
||||||
@@ -73,6 +76,7 @@ export const ProtocolUpgradeProposal = ({
|
|||||||
<ProtocolUpgradeProposalDetailInfo
|
<ProtocolUpgradeProposalDetailInfo
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
lastBlockHeight={lastBlockHeight}
|
lastBlockHeight={lastBlockHeight}
|
||||||
|
time={time}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{consensusValidators && consensusApprovals && (
|
{consensusValidators && consensusApprovals && (
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
|||||||
import type { MockedResponse } from '@apollo/client/testing';
|
import type { MockedResponse } from '@apollo/client/testing';
|
||||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||||
import type { PubKey } from '@vegaprotocol/wallet';
|
import type { PubKey } from '@vegaprotocol/wallet';
|
||||||
|
import type { VoteValue } from '@vegaprotocol/types';
|
||||||
|
import type { UserVoteQuery } from '../components/vote-details/__generated__/Vote';
|
||||||
|
import { UserVoteDocument } from '../components/vote-details/__generated__/Vote';
|
||||||
|
import faker from 'faker';
|
||||||
|
|
||||||
export const mockPubkey: PubKey = {
|
export const mockPubkey: PubKey = {
|
||||||
publicKey: '0x123',
|
publicKey: '0x123',
|
||||||
@@ -49,6 +53,16 @@ export const networkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const mockNetworkParams = {
|
||||||
|
governance_proposal_asset_requiredMajority: '0.66',
|
||||||
|
governance_proposal_freeform_requiredMajority: '0.66',
|
||||||
|
governance_proposal_market_requiredMajority: '0.66',
|
||||||
|
governance_proposal_updateAsset_requiredMajority: '0.66',
|
||||||
|
governance_proposal_updateMarket_requiredMajority: '0.66',
|
||||||
|
governance_proposal_updateMarket_requiredMajorityLP: '0.66',
|
||||||
|
governance_proposal_updateNetParam_requiredMajority: '0.5',
|
||||||
|
};
|
||||||
|
|
||||||
const oneMinute = 1000 * 60;
|
const oneMinute = 1000 * 60;
|
||||||
const oneHour = oneMinute * 60;
|
const oneHour = oneMinute * 60;
|
||||||
const oneDay = oneHour * 24;
|
const oneDay = oneHour * 24;
|
||||||
@@ -62,3 +76,34 @@ export const lastWeek = new Date(-oneWeek);
|
|||||||
export const nextWeek = new Date(oneWeek);
|
export const nextWeek = new Date(oneWeek);
|
||||||
export const lastMonth = new Date(-oneMonth);
|
export const lastMonth = new Date(-oneMonth);
|
||||||
export const nextMonth = new Date(oneMonth);
|
export const nextMonth = new Date(oneMonth);
|
||||||
|
|
||||||
|
export const createUserVoteQueryMock = (
|
||||||
|
proposalId: string,
|
||||||
|
value: VoteValue
|
||||||
|
): MockedResponse<UserVoteQuery> => ({
|
||||||
|
request: {
|
||||||
|
query: UserVoteDocument,
|
||||||
|
variables: {
|
||||||
|
partyId: mockPubkey.publicKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: {
|
||||||
|
data: {
|
||||||
|
party: {
|
||||||
|
votesConnection: {
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
node: {
|
||||||
|
proposalId,
|
||||||
|
vote: {
|
||||||
|
value,
|
||||||
|
datetime: faker.date.past().toISOString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ export const StakingForm = ({
|
|||||||
text: t('associateVegaNow'),
|
text: t('associateVegaNow'),
|
||||||
action: () => navigate(Routes.ASSOCIATE),
|
action: () => navigate(Routes.ASSOCIATE),
|
||||||
className: 'py-1',
|
className: 'py-1',
|
||||||
size: 'sm',
|
size: 'small',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -97,3 +97,33 @@
|
|||||||
.react-markdown-container ul li {
|
.react-markdown-container ul li {
|
||||||
list-style: circle;
|
list-style: circle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.jsondiffpatch-delta,
|
||||||
|
.jsondiffpatch-delta pre {
|
||||||
|
font-family: 'Roboto Mono', monospace !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsondiffpatch-delta pre {
|
||||||
|
padding: 0 0.25em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsondiffpatch-added .jsondiffpatch-property-name,
|
||||||
|
.jsondiffpatch-added .jsondiffpatch-value pre,
|
||||||
|
.jsondiffpatch-modified .jsondiffpatch-right-value pre,
|
||||||
|
.jsondiffpatch-textdiff-added {
|
||||||
|
background: theme(colors.vega.green[650]) !important;
|
||||||
|
color: theme(colors.white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsondiffpatch-deleted .jsondiffpatch-property-name,
|
||||||
|
.jsondiffpatch-deleted pre,
|
||||||
|
.jsondiffpatch-modified .jsondiffpatch-left-value pre,
|
||||||
|
.jsondiffpatch-textdiff-deleted {
|
||||||
|
background: theme(colors.vega.pink[650]) !important;
|
||||||
|
color: theme(colors.white) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.jsondiffpatch-moved .jsondiffpatch-moved-destination {
|
||||||
|
background: theme(colors.vega.yellow[350]) !important;
|
||||||
|
color: theme(colors.vega.dark[200]) !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,3 +33,7 @@ CYPRESS_VEGA_TOKEN_URL=https://governance.fairground.wtf
|
|||||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||||
|
|
||||||
|
# Cosmic elevator flags (MUST be doubled with CYPRESS_ prefix)
|
||||||
|
NX_SUCCESSOR_MARKETS=true
|
||||||
|
CYPRESS_NX_SUCCESSOR_MARKETS=true
|
||||||
@@ -54,7 +54,7 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
|
|
||||||
it('can deposit', function () {
|
it('can deposit', function () {
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
|
|
||||||
// 1001-DEPO-001
|
// 1001-DEPO-001
|
||||||
// 1001-DEPO-002
|
// 1001-DEPO-002
|
||||||
@@ -117,10 +117,10 @@ describe('capsule - without MultiSign', { tags: '@slow' }, () => {
|
|||||||
it('can key to key transfers', function () {
|
it('can key to key transfers', function () {
|
||||||
// 1003-TRAN-023
|
// 1003-TRAN-023
|
||||||
// 1003-TRAN-006
|
// 1003-TRAN-006
|
||||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
|
|
||||||
cy.getByTestId(collateralTab).click();
|
cy.getByTestId(collateralTab).click();
|
||||||
cy.getByTestId('open-transfer-dialog').click();
|
cy.getByTestId('open-transfer').click();
|
||||||
cy.getByTestId('transfer-form').should('be.visible');
|
cy.getByTestId('transfer-form').should('be.visible');
|
||||||
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
|
cy.getByTestId('transfer-form').find('[name="toAddress"]').select(1);
|
||||||
cy.get('select option')
|
cy.get('select option')
|
||||||
@@ -187,19 +187,21 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
// 0006-NETW-010
|
// 0006-NETW-010
|
||||||
const market = this.market;
|
const market = this.market;
|
||||||
cy.visit(`/#/markets/${market.id}`);
|
cy.visit(`/#/markets/${market.id}`);
|
||||||
|
cy.getByTestId('node-health-trigger').realHover();
|
||||||
cy.getByTestId('node-health')
|
cy.getByTestId('node-health')
|
||||||
.children()
|
.children()
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', 'Operational')
|
.should('contain.text', 'Operational')
|
||||||
.next()
|
|
||||||
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname)
|
|
||||||
.next()
|
|
||||||
.then(($el) => {
|
.then(($el) => {
|
||||||
const blockHeight = parseInt($el.text());
|
const blockHeight = parseInt($el.text());
|
||||||
// block height will increase over the course of the test run so best
|
// block height will increase over the course of the test run so best
|
||||||
// we can do here is check that its showing something sensible
|
// we can do here is check that its showing something sensible
|
||||||
expect(blockHeight).to.be.greaterThan(0);
|
expect(blockHeight).to.be.greaterThan(0);
|
||||||
});
|
});
|
||||||
|
cy.getByTestId('node-health')
|
||||||
|
.children()
|
||||||
|
.eq(1)
|
||||||
|
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can place and receive an order', function () {
|
it('can place and receive an order', function () {
|
||||||
@@ -342,7 +344,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
|
const ethWalletAddress = Cypress.env('ETHEREUM_WALLET_ADDRESS');
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||||
cy.getByTestId('Withdrawals').click();
|
cy.getByTestId('Withdrawals').click();
|
||||||
cy.getByTestId('withdraw-dialog-button').click();
|
cy.getByTestId('withdraw-dialog-button').click();
|
||||||
@@ -429,7 +431,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
// 1001-DEPO-006
|
// 1001-DEPO-006
|
||||||
// 1001-DEPO-007
|
// 1001-DEPO-007
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||||
cy.getByTestId(depositsTab).click();
|
cy.getByTestId(depositsTab).click();
|
||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
@@ -451,7 +453,7 @@ describe('capsule', { tags: '@slow', testIsolation: true }, () => {
|
|||||||
// 1002-WITH-007
|
// 1002-WITH-007
|
||||||
|
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.get('main[data-testid="/portfolio"]', txTimeout).should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]', txTimeout).should('exist');
|
||||||
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
cy.getByTestId(toastCloseBtn, txTimeout).click();
|
||||||
cy.getByTestId(depositsTab).click();
|
cy.getByTestId(depositsTab).click();
|
||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
|
|||||||
@@ -351,12 +351,6 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 6001-MARK-017
|
|
||||||
cy.get(rowSelector)
|
|
||||||
.first()
|
|
||||||
.find('[col-id="realisedPNL"]')
|
|
||||||
.should('have.text', '-');
|
|
||||||
|
|
||||||
// 6001-MARK-018
|
// 6001-MARK-018
|
||||||
cy.get(rowSelector)
|
cy.get(rowSelector)
|
||||||
.first()
|
.first()
|
||||||
@@ -369,6 +363,12 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
|||||||
.first()
|
.first()
|
||||||
.find('button svg')
|
.find('button svg')
|
||||||
.should('exist');
|
.should('exist');
|
||||||
|
if (Cypress.env('NX_SUCCESSOR_MARKETS')) {
|
||||||
|
cy.get(rowSelector)
|
||||||
|
.find('[col-id="successorMarket"]')
|
||||||
|
.first()
|
||||||
|
.should('have.text', '-');
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// test market list for market in terminated state
|
// test market list for market in terminated state
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ describe('deposit form validation', { tags: '@smoke' }, () => {
|
|||||||
cy.mockTradingPage();
|
cy.mockTradingPage();
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
cy.getByTestId('Deposits').click();
|
cy.getByTestId('Deposits').click();
|
||||||
cy.getByTestId('deposit-button').click();
|
cy.getByTestId('deposit-button').click();
|
||||||
cy.wait('@Assets');
|
|
||||||
connectEthereumWallet('MetaMask');
|
connectEthereumWallet('MetaMask');
|
||||||
|
cy.wait('@Assets');
|
||||||
}
|
}
|
||||||
|
|
||||||
before(() => {
|
before(() => {
|
||||||
@@ -102,8 +102,6 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
|||||||
cy.mockSubscription();
|
cy.mockSubscription();
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
cy.visit('/#/markets/market-1');
|
cy.visit('/#/markets/market-1');
|
||||||
cy.wait('@MarketsCandles');
|
|
||||||
cy.getByTestId('dialog-close').click();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Deposit to trade is visble', () => {
|
it('Deposit to trade is visble', () => {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
const dialogContent = 'dialog-content';
|
const dialogContent = 'dialog-content';
|
||||||
const nodeHealth = 'node-health';
|
const nodeHealth = 'node-health';
|
||||||
|
const nodeHealthTrigger = 'node-health-trigger';
|
||||||
|
|
||||||
describe('home', { tags: '@regression' }, () => {
|
describe('home', { tags: '@regression' }, () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
@@ -8,22 +9,23 @@ describe('home', { tags: '@regression' }, () => {
|
|||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('footer', () => {
|
describe('node health', () => {
|
||||||
it('shows current block height', () => {
|
it('shows current block height', () => {
|
||||||
// 0006-NETW-004
|
// 0006-NETW-004
|
||||||
// 0006-NETW-008
|
// 0006-NETW-008
|
||||||
// 0006-NETW-009
|
// 0006-NETW-009
|
||||||
|
cy.getByTestId(nodeHealthTrigger).realHover();
|
||||||
cy.getByTestId(nodeHealth)
|
cy.getByTestId(nodeHealth)
|
||||||
.children()
|
.children()
|
||||||
.first()
|
.first()
|
||||||
.should('contain.text', 'Operational', {
|
.should('contain.text', 'Operational', {
|
||||||
timeout: 10000,
|
timeout: 10000,
|
||||||
})
|
})
|
||||||
.next()
|
|
||||||
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname)
|
|
||||||
.next()
|
|
||||||
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
|
.should('contain.text', '100'); // all mocked queries have x-block-height header set to 100
|
||||||
|
cy.getByTestId(nodeHealth)
|
||||||
|
.children()
|
||||||
|
.eq(1)
|
||||||
|
.should('contain.text', new URL(Cypress.env('VEGA_URL')).hostname);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows node switcher details', () => {
|
it('shows node switcher details', () => {
|
||||||
@@ -32,7 +34,7 @@ describe('home', { tags: '@regression' }, () => {
|
|||||||
// 0006-NETW-014
|
// 0006-NETW-014
|
||||||
// 0006-NETW-015
|
// 0006-NETW-015
|
||||||
// 0006-NETW-016
|
// 0006-NETW-016
|
||||||
cy.getByTestId(nodeHealth).click();
|
cy.getByTestId(nodeHealthTrigger).click();
|
||||||
cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
|
cy.getByTestId(dialogContent).should('contain.text', 'Connected node');
|
||||||
cy.getByTestId(dialogContent).should(
|
cy.getByTestId(dialogContent).should(
|
||||||
'contain.text',
|
'contain.text',
|
||||||
@@ -56,7 +58,7 @@ describe('home', { tags: '@regression' }, () => {
|
|||||||
// 0006-NETW-018
|
// 0006-NETW-018
|
||||||
// 0006-NETW-019
|
// 0006-NETW-019
|
||||||
// 0006-NETW-020
|
// 0006-NETW-020
|
||||||
cy.getByTestId(nodeHealth).click();
|
cy.getByTestId(nodeHealthTrigger).click();
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
cy.getByTestId('node-url-custom').click();
|
cy.getByTestId('node-url-custom').click();
|
||||||
cy.getByTestId('connect').should('be.disabled');
|
cy.getByTestId('connect').should('be.disabled');
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ describe('home', { tags: '@regression' }, () => {
|
|||||||
cy.visit('/');
|
cy.visit('/');
|
||||||
cy.wait('@Markets');
|
cy.wait('@Markets');
|
||||||
|
|
||||||
cy.get('main[data-testid^="/markets/"]');
|
cy.get('[data-testid^="pathname-/markets/"]');
|
||||||
|
|
||||||
// the choose market overlay is no longer showing
|
// the choose market overlay is no longer showing
|
||||||
cy.contains('Loading...').should('not.exist');
|
cy.contains('Loading...').should('not.exist');
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
|
|||||||
'Description',
|
'Description',
|
||||||
'Trading mode',
|
'Trading mode',
|
||||||
'Status',
|
'Status',
|
||||||
|
'Successor market',
|
||||||
'Best bid',
|
'Best bid',
|
||||||
'Best offer',
|
'Best offer',
|
||||||
'Mark price',
|
'Mark price',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||||
import { MarketState } from '@vegaprotocol/types';
|
import { MarketState } from '@vegaprotocol/types';
|
||||||
|
import compact from 'lodash/compact';
|
||||||
|
|
||||||
const accordionContent = 'accordion-content';
|
const accordionContent = 'accordion-content';
|
||||||
const blockExplorerLink = 'block-explorer-link';
|
const blockExplorerLink = 'block-explorer-link';
|
||||||
@@ -66,17 +67,24 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
|||||||
// 6002-MDET-201
|
// 6002-MDET-201
|
||||||
cy.getByTestId(marketTitle).contains('Key details').click();
|
cy.getByTestId(marketTitle).contains('Key details').click();
|
||||||
|
|
||||||
validateMarketDataRow(0, 'Name', 'BTCUSD Monthly (30 Jun 2022)');
|
const rows: [string, string][] = compact([
|
||||||
validateMarketDataRow(1, 'Market ID', 'market-0');
|
['Name', 'BTCUSD Monthly (30 Jun 2022)'],
|
||||||
validateMarketDataRow(2, 'Parent Market ID', 'market-1');
|
['Market ID', 'market-0'],
|
||||||
validateMarketDataRow(
|
Cypress.env('NX_SUCCESSOR_MARKETS') && ['Parent Market ID', 'PARENT-A'],
|
||||||
3,
|
Cypress.env('NX_SUCCESSOR_MARKETS') && [
|
||||||
'Trading Mode',
|
'Insurance Pool Fraction',
|
||||||
MarketTradingModeMapping.TRADING_MODE_CONTINUOUS
|
'0.75',
|
||||||
);
|
],
|
||||||
validateMarketDataRow(4, 'Market Decimal Places', '5');
|
['Trading Mode', MarketTradingModeMapping.TRADING_MODE_CONTINUOUS],
|
||||||
validateMarketDataRow(5, 'Position Decimal Places', '0');
|
['Market Decimal Places', '5'],
|
||||||
validateMarketDataRow(6, 'Settlement Asset Decimal Places', '5');
|
['Position Decimal Places', '0'],
|
||||||
|
['Settlement Asset Decimal Places', '5'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
for (const rowNumber in rows) {
|
||||||
|
const [name, value] = rows[rowNumber];
|
||||||
|
validateMarketDataRow(Number(rowNumber), name, value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('instrument displayed', () => {
|
it('instrument displayed', () => {
|
||||||
|
|||||||
@@ -132,9 +132,10 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
|||||||
it('can see header title', () => {
|
it('can see header title', () => {
|
||||||
// 5002-LIQP-004
|
// 5002-LIQP-004
|
||||||
// 5002-LIQP-005
|
// 5002-LIQP-005
|
||||||
cy.getByTestId('header-title')
|
cy.getByTestId('header-title').should(
|
||||||
.should('contain.text', 'BTCUSD.MF21 liquidity provision')
|
'contain.text',
|
||||||
.and('contain.text', 'Go to trading');
|
'BTCUSD.MF21 liquidity provision'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can see target stake', () => {
|
it('can see target stake', () => {
|
||||||
@@ -171,7 +172,7 @@ describe('liquidity table view', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId('liquidity-supplied').within(() => {
|
cy.getByTestId('liquidity-supplied').within(() => {
|
||||||
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
|
cy.getByTestId(itemHeader).should('have.text', 'Liquidity supplied');
|
||||||
cy.getByTestId('indicator').should('be.visible');
|
cy.getByTestId('indicator').should('be.visible');
|
||||||
cy.getByTestId(itemValue).should('have.text', '0.10%').realHover();
|
cy.getByTestId(itemValue).should('have.text', ' 0.10%').realHover();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,15 +22,12 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
|||||||
|
|
||||||
cy.wait('@Markets');
|
cy.wait('@Markets');
|
||||||
cy.wait('@MarketsData');
|
cy.wait('@MarketsData');
|
||||||
cy.wait('@MarketsCandles');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 6001-MARK-066
|
// 6001-MARK-066
|
||||||
it('can toggle the sidebar', () => {
|
it('can open popover to view markets', () => {
|
||||||
cy.getByTestId('market-selector').should('be.visible');
|
|
||||||
cy.getByTestId('sidebar-toggle').click();
|
|
||||||
cy.getByTestId('market-selector').should('not.exist');
|
cy.getByTestId('market-selector').should('not.exist');
|
||||||
cy.getByTestId('sidebar-toggle').click();
|
cy.getByTestId('header-title').should('be.visible').click();
|
||||||
cy.getByTestId('market-selector').should('be.visible');
|
cy.getByTestId('market-selector').should('be.visible');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -40,29 +37,26 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
|||||||
const data = [
|
const data = [
|
||||||
{
|
{
|
||||||
code: 'SOLUSD',
|
code: 'SOLUSD',
|
||||||
markPrice: '84.41XYZalpha',
|
markPrice: '84.41',
|
||||||
change: '',
|
vol: '0.00',
|
||||||
vol: '0.0024h vol',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'ETHBTC.QM21',
|
code: 'ETHBTC.QM21',
|
||||||
markPrice: '46,126.90058tBTC',
|
markPrice: '46,126.90058',
|
||||||
change: '',
|
vol: '0.00',
|
||||||
vol: '0.0024h vol',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'BTCUSD.MF21',
|
code: 'BTCUSD.MF21',
|
||||||
markPrice: '46,126.90058tDAI',
|
markPrice: '46,126.90058',
|
||||||
change: '',
|
vol: '0.00',
|
||||||
vol: '0.0024h vol',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
code: 'AAPL.MF21',
|
code: 'AAPL.MF21',
|
||||||
markPrice: '46,126.90058tUSDC',
|
markPrice: '46,126.90058',
|
||||||
change: '',
|
vol: '0.00',
|
||||||
vol: '0.0024h vol',
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
cy.getByTestId('header-title').should('be.visible').click();
|
||||||
cy.getByTestId(list)
|
cy.getByTestId(list)
|
||||||
.find('a')
|
.find('a')
|
||||||
.each((item, i) => {
|
.each((item, i) => {
|
||||||
@@ -71,33 +65,20 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
|||||||
// 6001-MARK-022
|
// 6001-MARK-022
|
||||||
expect(item.find('h3').text()).equals(market.code);
|
expect(item.find('h3').text()).equals(market.code);
|
||||||
expect(
|
expect(
|
||||||
item.find('[data-testid="market-selector-data-row"]').eq(0).text()
|
item.find('[data-testid="market-selector-volume"]').text()
|
||||||
).contains(market.vol);
|
).contains(market.vol);
|
||||||
// 6001-MARK-024
|
// 6001-MARK-024
|
||||||
expect(
|
expect(
|
||||||
item.find('[data-testid="market-selector-data-row"]').eq(1).text()
|
item.find('[data-testid="market-selector-price"]').text()
|
||||||
).contains(market.markPrice);
|
).contains(market.markPrice);
|
||||||
// 6001-MARK-023
|
|
||||||
expect(item.find('[data-testid="market-item-change"]').text()).equals(
|
|
||||||
market.change
|
|
||||||
);
|
|
||||||
// 6001-MARK-025
|
// 6001-MARK-025
|
||||||
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
|
expect(item.find('[data-testid="sparkline-svg"]')).to.not.exist;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('can see all markets link', () => {
|
|
||||||
// 6001-MARK-026
|
|
||||||
cy.getByTestId('market-selector').within(() => {
|
|
||||||
cy.getByTestId('all-markets-link')
|
|
||||||
.should('be.visible')
|
|
||||||
.and('have.text', 'All markets')
|
|
||||||
.and('have.attr', 'href')
|
|
||||||
.and('contain', '#/markets/all');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('can use the filter options', () => {
|
it('can use the filter options', () => {
|
||||||
|
cy.getByTestId('header-title').should('be.visible').click();
|
||||||
|
|
||||||
// 6001-MARK-027
|
// 6001-MARK-027
|
||||||
// product type
|
// product type
|
||||||
cy.getByTestId('product-Spot').click();
|
cy.getByTestId('product-Spot').click();
|
||||||
@@ -118,6 +99,8 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('can sort by by top gaining and top losing market', () => {
|
it('can sort by by top gaining and top losing market', () => {
|
||||||
|
cy.getByTestId('header-title').should('be.visible').click();
|
||||||
|
|
||||||
// 6001-MARK-030
|
// 6001-MARK-030
|
||||||
// 6001-MARK-031
|
// 6001-MARK-031
|
||||||
// 6001-MARK-032
|
// 6001-MARK-032
|
||||||
@@ -135,6 +118,8 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('can filter by settlement asset', () => {
|
it('can filter by settlement asset', () => {
|
||||||
|
cy.getByTestId('header-title').should('be.visible').click();
|
||||||
|
|
||||||
// 6001-MARK-028
|
// 6001-MARK-028
|
||||||
cy.getByTestId('asset-trigger').click();
|
cy.getByTestId('asset-trigger').click();
|
||||||
cy.getByTestId('asset-id-asset-3').contains('tBTC').click();
|
cy.getByTestId('asset-id-asset-3').contains('tBTC').click();
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
describe('market bottom panel', { tags: '@smoke' }, () => {
|
|
||||||
before(() => {
|
|
||||||
cy.clearAllLocalStorage();
|
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
|
||||||
cy.visit('/#/markets/market-0');
|
|
||||||
cy.wait('@MarketData');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('on xxl screen should be splitted out into two tables', () => {
|
|
||||||
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
|
|
||||||
cy.getByTestId('tab-open-orders').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-closed-orders').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-rejected-orders').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
|
|
||||||
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
|
|
||||||
cy.getByTestId('tab-accounts').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.viewport(1801, 1000);
|
|
||||||
cy.getByTestId('tab-positions').should('have.attr', 'data-state', 'active');
|
|
||||||
cy.getByTestId('tab-open-orders').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-closed-orders').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-rejected-orders').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
|
|
||||||
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'inactive');
|
|
||||||
cy.getByTestId('tab-accounts').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
|
|
||||||
cy.getByTestId('Fills').click();
|
|
||||||
cy.getByTestId('Collateral').click();
|
|
||||||
cy.getByTestId('tab-positions').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'inactive'
|
|
||||||
);
|
|
||||||
cy.getByTestId('tab-orders').should('have.attr', 'data-state', 'inactive');
|
|
||||||
cy.getByTestId('tab-fills').should('have.attr', 'data-state', 'active');
|
|
||||||
cy.getByTestId('tab-accounts').should('have.attr', 'data-state', 'active');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -19,6 +19,7 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
|
|||||||
'Description',
|
'Description',
|
||||||
'Settlement asset',
|
'Settlement asset',
|
||||||
'State',
|
'State',
|
||||||
|
'Parent market',
|
||||||
'Voting',
|
'Voting',
|
||||||
'Closing date',
|
'Closing date',
|
||||||
'Enactment date',
|
'Enactment date',
|
||||||
|
|||||||
@@ -99,9 +99,6 @@ describe('Navbar', { tags: '@smoke' }, () => {
|
|||||||
cy.getByTestId('menu-drawer').should('not.be.visible');
|
cy.getByTestId('menu-drawer').should('not.be.visible');
|
||||||
cy.getByTestId('button-menu-drawer').click();
|
cy.getByTestId('button-menu-drawer').click();
|
||||||
cy.getByTestId('menu-drawer').should('be.visible');
|
cy.getByTestId('menu-drawer').should('be.visible');
|
||||||
cy.getByTestId('menu-drawer')
|
|
||||||
.find('[data-testid="Settings"]')
|
|
||||||
.should('be.visible');
|
|
||||||
cy.getByTestId('button-menu-drawer').click();
|
cy.getByTestId('button-menu-drawer').click();
|
||||||
cy.getByTestId('menu-drawer').should('not.be.visible');
|
cy.getByTestId('menu-drawer').should('not.be.visible');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
const orderbookTab = 'Orderbook';
|
const orderbookTab = 'Orderbook';
|
||||||
const orderbookTable = 'tab-orderbook';
|
const orderbookTable = 'tab-orderbook';
|
||||||
const askPrice = 'price-9894585';
|
const askPrice = 'price-9894185';
|
||||||
const bidPrice = 'price-9889001';
|
const bidPrice = 'price-9889001';
|
||||||
const askVolume = 'ask-vol-9894585';
|
const askVolume = 'ask-vol-9894185';
|
||||||
const bidVolume = 'bid-vol-9889001';
|
const bidVolume = 'bid-vol-9889001';
|
||||||
const askCumulative = 'cumulative-vol-9894585';
|
const askCumulative = 'cumulative-vol-9894185';
|
||||||
const bidCumulative = 'cumulative-vol-9889001';
|
const bidCumulative = 'cumulative-vol-9889001';
|
||||||
const midPrice = 'middle-mark-price-4612690000';
|
const midPrice = 'middle-mark-price-4612690000';
|
||||||
const priceResolution = 'resolution';
|
const priceResolution = 'resolution';
|
||||||
const dealTicketPrice = 'order-price';
|
const dealTicketPrice = 'order-price';
|
||||||
|
const dealTicketSize = 'order-size';
|
||||||
const resPrice = 'price-990';
|
const resPrice = 'price-990';
|
||||||
|
|
||||||
describe('order book', { tags: '@smoke' }, () => {
|
describe('order book', { tags: '@smoke' }, () => {
|
||||||
@@ -33,7 +34,7 @@ describe('order book', { tags: '@smoke' }, () => {
|
|||||||
|
|
||||||
it('show orders prices', () => {
|
it('show orders prices', () => {
|
||||||
// 6003-ORDB-003
|
// 6003-ORDB-003
|
||||||
cy.getByTestId(askPrice).should('have.text', '98.94585');
|
cy.getByTestId(askPrice).should('have.text', '98.94185');
|
||||||
cy.getByTestId(bidPrice).should('have.text', '98.89001');
|
cy.getByTestId(bidPrice).should('have.text', '98.89001');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ describe('order book', { tags: '@smoke' }, () => {
|
|||||||
|
|
||||||
it('show prices cumulative volumes', () => {
|
it('show prices cumulative volumes', () => {
|
||||||
// 6003-ORDB-005
|
// 6003-ORDB-005
|
||||||
cy.getByTestId(askCumulative).should('have.text', '39');
|
cy.getByTestId(askCumulative).should('have.text', '38');
|
||||||
cy.getByTestId(bidCumulative).should('have.text', '7');
|
cy.getByTestId(bidCumulative).should('have.text', '7');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -71,7 +72,19 @@ describe('order book', { tags: '@smoke' }, () => {
|
|||||||
it('copy price to deal ticket form', () => {
|
it('copy price to deal ticket form', () => {
|
||||||
// 6003-ORDB-009
|
// 6003-ORDB-009
|
||||||
cy.getByTestId(askPrice).click();
|
cy.getByTestId(askPrice).click();
|
||||||
cy.getByTestId(dealTicketPrice).should('have.value', '98.94585');
|
cy.getByTestId(dealTicketPrice).should('have.value', '98.94185');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('copy size to deal ticket form', () => {
|
||||||
|
// 6003-ORDB-009
|
||||||
|
cy.getByTestId(bidCumulative).click();
|
||||||
|
cy.getByTestId(dealTicketSize).should('have.value', '7');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('copy size to deal ticket form', () => {
|
||||||
|
// 6003-ORDB-009
|
||||||
|
cy.getByTestId(bidVolume).click();
|
||||||
|
cy.getByTestId(dealTicketSize).should('have.value', '1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('change price resolution', () => {
|
it('change price resolution', () => {
|
||||||
@@ -88,13 +101,14 @@ describe('order book', { tags: '@smoke' }, () => {
|
|||||||
'1,000',
|
'1,000',
|
||||||
'10,000',
|
'10,000',
|
||||||
];
|
];
|
||||||
cy.getByTestId(priceResolution)
|
cy.getByTestId(priceResolution).click();
|
||||||
.find('option')
|
cy.get('[role="menu"]')
|
||||||
|
.find('[role="menuitem"]')
|
||||||
.each(($el, index) => {
|
.each(($el, index) => {
|
||||||
expect($el.text()).to.equal(resolutions[index]);
|
expect($el.text()).to.equal(resolutions[index]);
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.getByTestId(priceResolution).select('0.0');
|
cy.get('[role="menuitem"]').eq(4).click();
|
||||||
cy.getByTestId(resPrice).should('have.text', '99.0');
|
cy.getByTestId(resPrice).should('have.text', '99.0');
|
||||||
cy.getByTestId(askPrice).should('not.exist');
|
cy.getByTestId(askPrice).should('not.exist');
|
||||||
cy.getByTestId(bidPrice).should('not.exist');
|
cy.getByTestId(bidPrice).should('not.exist');
|
||||||
|
|||||||
@@ -1,42 +1,29 @@
|
|||||||
describe('Settings page', { tags: '@smoke' }, () => {
|
describe('Settings page', { tags: '@smoke' }, () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.clearLocalStorage().then(() => {
|
cy.clearLocalStorage();
|
||||||
cy.mockTradingPage();
|
|
||||||
cy.mockSubscription();
|
cy.mockTradingPage();
|
||||||
cy.visit('/');
|
cy.mockSubscription();
|
||||||
cy.get('[aria-label="cog icon"]').click();
|
cy.visit('/');
|
||||||
|
|
||||||
|
// Only click if not already active otherwise sidebar will close
|
||||||
|
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
|
||||||
|
if ($sidebarContent.find('h2').text() !== 'Settings') {
|
||||||
|
cy.get('[data-testid="sidebar"] [data-testid="Settings"]').click();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('telemetry checkbox should work well', () => {
|
it('telemetry checkbox should work well', () => {
|
||||||
cy.location('hash').should('equal', '#/settings');
|
const telemetrySwitch = '#switch-settings-telemetry-switch';
|
||||||
cy.getByTestId('telemetry-approval').should(
|
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||||
'have.attr',
|
cy.get(telemetrySwitch).click();
|
||||||
'data-state',
|
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked');
|
||||||
'unchecked'
|
|
||||||
);
|
|
||||||
cy.get('[for="telemetry-approval"]').click();
|
|
||||||
cy.getByTestId('telemetry-approval').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'checked'
|
|
||||||
);
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.getByTestId('telemetry-approval').should(
|
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'checked');
|
||||||
'have.attr',
|
cy.get(telemetrySwitch).click();
|
||||||
'data-state',
|
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||||
'checked'
|
|
||||||
);
|
|
||||||
cy.get('[for="telemetry-approval"]').click();
|
|
||||||
cy.getByTestId('telemetry-approval').should(
|
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'unchecked'
|
|
||||||
);
|
|
||||||
cy.reload();
|
cy.reload();
|
||||||
cy.getByTestId('telemetry-approval').should(
|
cy.get(telemetrySwitch).should('have.attr', 'data-state', 'unchecked');
|
||||||
'have.attr',
|
|
||||||
'data-state',
|
|
||||||
'unchecked'
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ describe(
|
|||||||
{
|
{
|
||||||
name: 'Price monitoring bounds',
|
name: 'Price monitoring bounds',
|
||||||
infoText:
|
infoText:
|
||||||
'Price Monitoring Bounds: Min 162.56291Max 182.96869Reference 172.47489',
|
'Price Monitoring Bounds 1: Min 162.56291Max 182.96869Reference 172.47489',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
|||||||
cy.mockSubscription();
|
cy.mockSubscription();
|
||||||
cy.visit('/#/markets/market-0');
|
cy.visit('/#/markets/market-0');
|
||||||
cy.wait('@Markets');
|
cy.wait('@Markets');
|
||||||
|
|
||||||
|
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
|
||||||
|
if (!$form.length) {
|
||||||
|
cy.getByTestId('Order').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ describe(
|
|||||||
'account validation',
|
'account validation',
|
||||||
{ tags: '@regression', testIsolation: true },
|
{ tags: '@regression', testIsolation: true },
|
||||||
() => {
|
() => {
|
||||||
describe('zero balance error', () => {
|
describe.skip('zero balance error', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.setVegaWallet();
|
cy.setVegaWallet();
|
||||||
cy.mockTradingPage();
|
cy.mockTradingPage();
|
||||||
@@ -59,6 +59,12 @@ describe(
|
|||||||
cy.mockSubscription();
|
cy.mockSubscription();
|
||||||
cy.visit('/#/markets/market-0');
|
cy.visit('/#/markets/market-0');
|
||||||
cy.wait('@Markets');
|
cy.wait('@Markets');
|
||||||
|
|
||||||
|
cy.get('[data-testid="deal-ticket-form"]').then(($form) => {
|
||||||
|
if (!$form.length) {
|
||||||
|
cy.getByTestId('Order').click();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should display info and button for deposit', () => {
|
it('should display info and button for deposit', () => {
|
||||||
@@ -74,8 +80,8 @@ describe(
|
|||||||
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
|
'You may not have enough margin available to open this position. 5.00 tDAI is currently required. You have only 0.01001 tDAI available.'
|
||||||
);
|
);
|
||||||
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
cy.getByTestId('deal-ticket-deposit-dialog-button').click();
|
||||||
cy.getByTestId('dialog-content')
|
cy.getByTestId('sidebar-content')
|
||||||
.find('h1')
|
.find('h2')
|
||||||
.eq(0)
|
.eq(0)
|
||||||
.should('have.text', 'Deposit');
|
.should('have.text', 'Deposit');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ describe('fills', { tags: '@regression' }, () => {
|
|||||||
|
|
||||||
it('renders fills on portfolio page', () => {
|
it('renders fills on portfolio page', () => {
|
||||||
cy.visit('/#/portfolio');
|
cy.visit('/#/portfolio');
|
||||||
cy.get('main[data-testid="/portfolio"]').should('exist');
|
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||||
cy.getByTestId('Fills').click();
|
cy.getByTestId('Fills').click();
|
||||||
validateFillsDisplayed();
|
validateFillsDisplayed();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -431,6 +431,8 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const orderId = '1234567890';
|
const orderId = '1234567890';
|
||||||
|
|
||||||
|
// this test is flakey
|
||||||
it('must be able to amend the price of an order', () => {
|
it('must be able to amend the price of an order', () => {
|
||||||
// 7003-MORD-007
|
// 7003-MORD-007
|
||||||
// 7003-MORD-012
|
// 7003-MORD-012
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user