Compare commits

..
Author SHA1 Message Date
Madalina Raicu cf7db57565 Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into fix/order-stopped-intent-toasts 2023-04-03 19:41:01 +01:00
dexturr 662753c74b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-03 18:09:46 +00:00
Madalina Raicu 83c97ac0ff fix: add reduce and post only tests 2023-04-03 18:49:54 +01:00
Madalina Raicu d21a328f81 fix: update tooltip 2023-04-03 18:36:31 +01:00
Madalina Raicu b449d2671a fix: reduce only disable on persistent orders 2023-04-03 18:33:27 +01:00
Madalina Raicu 6f48fd2034 fix: update deal ticket postOnly selection 2023-04-03 18:05:17 +01:00
Madalina Raicu e753a77fb6 fix: reset post only and reduce only when changing TIF and type 2023-04-03 17:32:08 +01:00
Madalina Raicu 319edacbfc fix: order list text translation 2023-04-03 16:11:43 +01:00
Madalina Raicu a3308b9966 fix: remove one log 2023-04-03 16:01:50 +01:00
daro-majandm.ray 71aa8882bc test(trading): renders correctly liquidity in trading tab (#3343)
Co-authored-by: m.ray <16125548+MadalinaRaicu@users.noreply.github.com>
2023-04-03 14:33:10 +00:00
Madalina Raicu 1078c15f95 fix: order list rejected sjowing undefined 2023-04-03 15:29:33 +01:00
Mikołaj Młodzikowski ecfbccf8ed feat(trading,governance,explorer): dist on s3 (#3332) 2023-04-03 15:53:50 +02:00
Madalina Raicu 8c856e482c fix: update reduce/post only labels 2023-04-03 14:49:12 +01:00
Madalina Raicu f0d3cb9ca2 Merge branch 'feat/3248-total-transfer-amount-fee' of github.com:vegaprotocol/frontend-monorepo into fix/order-stopped-intent-toasts 2023-04-03 14:32:28 +01:00
Madalina Raicu dedd81dd1c feat(trading): show total amount plus transfer fee 2023-04-03 14:31:20 +01:00
Madalina Raicu 0bc94efc72 fix: show reduce/post only in order list 2023-04-03 14:25:31 +01:00
Bartłomiej Głownia 2aad6b1a14 feat(deal-ticket): show total margin available for an order, and where it will come from (#3318) 2023-04-03 15:17:23 +02:00
Madalina Raicu 5b015858f2 fix: fix intent on rder stopped 2023-04-03 14:16:11 +01:00
Bartłomiej Głownia 8463d371ad chore(trading): liquidity table DRY (#3346) 2023-04-03 15:02:43 +02:00
Madalina Raicu f1b5728a17 fix: update post only on disabled to be false 2023-04-03 14:02:23 +01:00
Madalina Raicu 92293bfd98 fix: order rejection appearance 2023-04-03 13:35:16 +01:00
dexturr 0914e7ce4b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-03 12:08:48 +00:00
Madalina Raicu 1b1d1865f8 fix: remove redundant log 2023-04-03 12:29:59 +01:00
Madalina Raicu f781ae4ae1 feat(trading): show total amount plus transfer fee 2023-04-03 12:27:30 +01:00
Madalina Raicu b8dcbd2e44 Merge branch 'develop' of github.com:vegaprotocol/frontend-monorepo into develop 2023-04-03 10:09:46 +01:00
Maciek 7c82144f37 chore(trading): add metaKey detection - clean up some unit tests (#3337) 2023-04-03 10:20:20 +02:00
Joe Tsang 17ad0cc975 test(governance): add e2e tests for banner and footer links (#3340) 2023-04-03 09:16:42 +01:00
dexturr eb58dcd350 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-03 06:09:59 +00:00
dexturr c8c0bf2cc0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-03 00:16:15 +00:00
dexturr 00579fcb1f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 18:06:45 +00:00
dexturr 5e13173efb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 12:09:35 +00:00
dexturr 3a6c0554cc chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 06:06:55 +00:00
dexturr d4b237a6ba chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-02 00:17:15 +00:00
dexturr 5e765f3172 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 18:07:57 +00:00
dexturr 5ef8c11e6e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 12:07:28 +00:00
dexturr 84bae12def chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 06:06:47 +00:00
dexturr c5b22f5bfb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-04-01 00:14:47 +00:00
dexturr 071dcc61a8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 18:09:16 +00:00
m.ray ce0ccdfebc feat(trading): add post only and reduce only orders (#3311) 2023-03-31 17:00:55 +01:00
Art f1524d3fcd feat(utils): make release script (#3290) 2023-03-31 17:01:19 +02:00
MaciekandMatthew Russell 82e5128ba1 chore(trading): add "holding CMD + click" external link (#3273)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-03-31 15:23:44 +02:00
Maciek 2bfc3abd15 chore(trading): liquidity provision, change query for set flag live on true (#3335) 2023-03-31 14:20:44 +01:00
dexturr bd3b557f3d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 12:10:35 +00:00
MaciekandBartłomiej Głownia c899da52c2 fix(trading): stabilize the liquidity provision table (#3320)
Co-authored-by: Bartłomiej Głownia <bglownia@gmail.com>
2023-03-31 11:41:16 +01:00
Matthew Russell b9c4057ce5 feat(oracles,trading): 3224 add oracle links to market info (#3297) 2023-03-31 09:06:21 +01:00
dexturr d02feee5c6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 06:07:09 +00:00
dexturr 6d1ab36a72 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-31 00:14:21 +00:00
dexturr 5690fe91a8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-30 18:09:18 +00:00
Joe Tsang 1daae6a233 chore(governance): additional acs added for governance proposals (#3322) 2023-03-30 17:18:41 +01:00
dexturr cd35a34246 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-30 12:10:13 +00:00
Bartłomiej Głownia 77ada964b5 feat(market-depth): always show the same number of decimal places in order book (#3317) 2023-03-30 10:54:13 +01:00
dexturr d7404bcd76 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-30 06:07:12 +00:00
dexturr 91862701a7 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-30 00:14:12 +00:00
dexturr 0136efd78f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-29 18:14:47 +00:00
daro-maj c3a4954d0d test(trading): update live e2e tests (#3307) 2023-03-29 15:26:12 +02:00
botondandMikołaj Młodzikowski 528fd96721 feat(announcements): announcements (#3252)
Co-authored-by: Mikołaj Młodzikowski <mikolaj.mlodzikowski@gmail.com>
2023-03-29 15:20:49 +02:00
dexturr 155923ff8e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-29 12:07:49 +00:00
Matthew Russellandmaciek 5f7e60d22f chore(orders): update orders filters (#3277)
Co-authored-by: maciek <maciek@vegaprotocol.io>
2023-03-29 13:12:29 +02:00
Joe Tsang 351583ce8f chore(cypress): fix error seen in CI for staking test (#3304) 2023-03-29 11:52:46 +01:00
Maciek 88bf58b974 chore(trading): remove files of envs to kill (#3303) 2023-03-29 10:57:33 +01:00
Maciek fcda4d7fcd chore(wallet,positions): use reduceOnly and IOC order for close position tx (#3263) 2023-03-28 23:50:11 -07:00
dexturr b1b0039b33 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-29 06:08:40 +00:00
Art 6854980b32 fix(trading): volumes formatting (#3293) 2023-03-28 22:12:52 -07:00
dexturr d8aac83f2d chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-29 00:18:30 +00:00
Sam Keen 5c17045752 fix(governance): paginate rewards as well as delegations data (#3288) 2023-03-28 20:35:29 +01:00
Bartłomiej Głownia 8863209342 feat(trading): persist state of chart intervals studies and overlays (#3261) 2023-03-28 21:03:53 +02:00
Joe Tsang 462bf6b8b5 test(governance): my stake e2e tests (#3295) 2023-03-28 19:03:58 +01:00
Ciaran McGhie e0b701e05a chore(react-helpers): react-helpers v0.1.0 (#3289) 2023-03-28 16:13:34 +01:00
Sam KeenandJoe eca212eee0 feat(governance): see user stake on validators table (#3250)
Co-authored-by: Joe <joe@vega.xyz>
2023-03-28 15:28:15 +01:00
Sam Keen c25858037a fix(governance): order proposals list by closing date (#3276) 2023-03-28 14:04:24 +01:00
Ciaran McGhie 081f5b48e2 chore(ui-toolkit): version 0.9.0 - includes healthbar in ui-toolkit (#3287) 2023-03-28 13:36:29 +01:00
dexturr e306d1c9fa chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-28 12:08:16 +00:00
dexturr 737dec4f5f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-28 06:08:37 +00:00
dexturr 6780b2f618 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-28 00:15:54 +00:00
Penny Andrews 08dc60086d chore(ci): update name of release issue template (#3279) 2023-03-27 11:47:35 -07:00
Bartłomiej Głownia 4595db07b0 fix(trading): throttle deal ticket submit (#3282) 2023-03-27 11:46:40 -07:00
dexturr c3da31146f chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-27 18:08:26 +00:00
dexturr b6a9faaa7c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-27 12:10:42 +00:00
daro-maj 477d523582 test(trading): fixed capsule tests (#3281) 2023-03-27 12:50:22 +02:00
Maciek a2b112d898 fix(positions): make positions table smoothly updated (again) (#3266) 2023-03-27 12:30:55 +02:00
Mikołaj Młodzikowski 621ba497e8 feat: remove env vars file after setting up js file 2023-03-27 11:03:06 +02:00
dexturr a6964bc3cf chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-27 06:06:57 +00:00
dexturr 48a9998441 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-27 00:16:27 +00:00
dexturr bd1bd94ee7 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-26 18:07:12 +00:00
dexturr 2fdebbd694 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-26 12:10:22 +00:00
dexturr 96af8035d0 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-26 06:07:03 +00:00
dexturr 051e3da366 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-26 00:17:44 +00:00
dexturr af5e666d5e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-25 18:09:35 +00:00
dexturr 333a7d6206 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-25 12:10:21 +00:00
dexturr cb6b988466 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-25 06:09:33 +00:00
dexturr 806ea74f7e chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-25 00:14:18 +00:00
Matthew Russell 8693f3bfd8 chore(trading): dont show faucet button until connected (#3251) 2023-03-24 17:06:22 -07:00
Bartłomiej Głownia 58c6652e29 feat(trading): use mini scroll in deal ticket, market info for and landing dialog (#3269) 2023-03-24 17:01:12 -07:00
Bartłomiej Głownia e8adff25c4 fix(trading): handle lack of approval in trading deposit flow (#3278) 2023-03-24 16:57:00 -07:00
Penny Andrews 3e594a783a chore(ci): add release issue template (#3272) 2023-03-24 16:55:22 -07:00
dexturr 1f7117075b chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-24 18:09:07 +00:00
Bartłomiej Głownia 4c850ecead fix(trading): change etherscan link label on toast (#3262) 2023-03-24 17:57:59 +01:00
Mikołaj Młodzikowski 19f8fa909e feat(explorer,trading,governance): use nx to know which apps needs building (#3267) 2023-03-24 16:42:33 +01:00
Sam Keen 8214858685 feat(governance): lp vote now reflected on proposal cards (#3256) 2023-03-24 14:56:56 +00:00
dexturr 868f8e21dc chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-24 12:08:16 +00:00
dexturr 6ead84b57c chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-24 06:09:30 +00:00
dexturr 3975477985 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-24 00:14:29 +00:00
Art 8a0c15ac11 feat(explorer): breadcrumbs (#3254) 2023-03-23 19:11:34 +00:00
dexturr a4f512f946 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-23 18:08:43 +00:00
dexturr 88f3c278f8 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-23 12:08:36 +00:00
Mikołaj Młodzikowski 34d5f3fd13 feat(explorer): build docker image on pull request + refactor dockerfile for ipfs (#3245) 2023-03-23 10:50:23 +01:00
daro-maj a060f94146 test(trading): 0003-WTXN submit vega transaction e2e tests (#3246) 2023-03-23 09:46:28 +01:00
MaciekandMatthew Russell f649d78565 chore(orders): remove infinite scroll from order table (#3247)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-03-23 09:13:07 +01:00
dexturr b611d30b31 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-23 06:08:50 +00:00
dexturr 545324ccd6 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-23 00:16:17 +00:00
Bartłomiej Głownia 8c16759621 fix(trading): use unique keys in LocalStoragePersistTabs (#3255) 2023-03-22 16:34:58 -07:00
Matthew Russell e724aac2f1 feat(trading): add resources dropdown to console nav (#3249) 2023-03-22 16:33:29 -07:00
dexturr e3a6b75130 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-22 18:09:03 +00:00
Joe Tsang 8d8f6b4928 test(governance): governance tests refactor (#3059) 2023-03-22 17:35:00 +00:00
m.ray 6c1c5bf2a9 feat(trading): pin collateral row (#3218) 2023-03-22 13:05:05 +01:00
Ciaran McGhie 74a3aa8566 refactor(ui-toolkit): move healthbar to ui-toolkit (#3121) 2023-03-22 11:33:46 +00:00
dexturr 4cbd7a4928 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-22 06:09:20 +00:00
dexturr 201cf07a01 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-03-22 00:13:57 +00:00
291 changed files with 9285 additions and 5306 deletions
+4
View File
@@ -3,3 +3,7 @@ apps/**/node_modules/*
tmp/*
.dockerignore
dockerfiles
node_modules
.git
.github
.vscode
+21
View File
@@ -0,0 +1,21 @@
---
name: Release
about: A template to outline the steps needed to for a successful release of our frontend apps
title: 'Release [add dapp version]-core-[add core version]'
labels:
assignees: ''
---
### Tasks
- [ ] Review [link to core release](xxx)
- [ ] Tag frontend-monorepo
- [ ] Create release and generate release notes
- [ ] Run `@smoke` tests
- [ ] Run `@regression` tests
- [ ] Run `@slow` tests
- [ ] Explorative testing of key flows
- [ ] Set `release/[network]` to tagged commit
- [ ] Verify builds (on Netlify and Fleek) are successful
- [ ] Verify build has been deployed
- [ ] Smoke testing on deployed app
+1 -1
View File
@@ -13,7 +13,7 @@ env:
jobs:
add_issue:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: 'Add issue to project board'
run: |
+110
View File
@@ -0,0 +1,110 @@
name: CI/CD
on:
push:
branches:
- release/*
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
lint-test-build:
runs-on: ubuntu-22.04
name: '(CI) lint + unit test + build'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup node
uses: actions/setup-node@v3
with:
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Install root dependencies
run: yarn install --frozen-lockfile
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v3
with:
main-branch-name: develop
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
run: yarn nx affected:lint --max-warnings=0
- name: Build affected spec
run: yarn nx affected --target=build-spec
- name: Test affected
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build
# See affected apps
- name: See affected apps
run: |
echo ">>>> debug"
echo "NX Version: $nx_version"
echo "NX_BASE: ${{ env.NX_BASE }}"
echo "NX_HEAD: ${{ env.NX_HEAD }}"
echo ">>>> eof debug"
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
echo -n "Affected projects: $affected"
projects_e2e=""
if [[ $affected == *"governance"* ]]; then projects_e2e+='"governance-e2e" '; fi
if [[ $affected == *"trading"* ]]; then projects_e2e+='"trading-e2e" '; fi
if [[ $affected == *"explorer"* ]]; then projects_e2e+='"explorer-e2e" '; fi
if [[ -z "$projects_e2e" ]]; then projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '; fi
projects_e2e=${projects_e2e%?}
projects_e2e=[${projects_e2e// /,}]
echo PROJECTS_E2E=$projects_e2e >> $GITHUB_ENV
echo PROJECTS=$(echo $projects_e2e | sed 's|-e2e||g') >> $GITHUB_ENV
outputs:
projects: ${{ env.PROJECTS }}
projects-e2e: ${{ env.PROJECTS_E2E }}
cypress:
needs: lint-test-build
name: '(CI) cypress'
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
projects: ${{ needs.lint-test-build.outputs.projects-e2e }}
tags: '@smoke @regression'
publish-dist:
needs: lint-test-build
name: '(CD) publish dist'
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/publish-dist.yml
secrets: inherit
with:
projects: ${{ needs.lint-test-build.outputs.projects }}
# Report single result at the end, to avoid mess with required checks in PR
cypress-result:
if: ${{ always() }}
needs: cypress
runs-on: ubuntu-22.04
steps:
- run: |
result="${{ needs.cypress.result }}"
if [[ $result == "success" || $result == "skipped" ]]; then
exit 0
else
exit 1
fi
+1 -1
View File
@@ -13,7 +13,7 @@ on:
jobs:
cypress-run:
name: Run Cypress Trading tests -- live environment
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
-78
View File
@@ -1,78 +0,0 @@
name: Cypress tests - PR
on:
push:
branches:
- develop
- main
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
pr:
runs-on: ubuntu-latest
steps:
- name: Checkout frontend mono repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup node
uses: actions/setup-node@v3
- name: Remove package.json & yarn.lock to avoid installing everything
run: rm package.json yarn.lock
- name: Install nx
run: yarn add nx
# Check SHAs
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: ${{ github.base_ref || github.ref_name }}
set-environment-variables-for-job: true
# See affected apps
- name: See affected apps
run: |
affected=$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)
echo -n "Affected projects: $affected"
projects=""
if [[ $affected == *"governance"* ]]; then projects+='"governance-e2e" '; fi
if [[ $affected == *"trading"* ]]; then projects+='"trading-e2e" '; fi
if [[ $affected == *"explorer"* ]]; then projects+='"explorer-e2e" '; fi
if [[ -z "$projects" ]]; then projects+='"governance-e2e" "trading-e2e" "explorer-e2e" '; fi
projects=${projects%?}
projects=[${projects// /,}]
echo PROJECTS=$projects >> $GITHUB_ENV
outputs:
projects: ${{ env.PROJECTS }}
run:
needs: pr
if: ${{ needs.pr.outputs.projects != '[]' }}
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
projects: ${{ needs.pr.outputs.projects }}
tags: '@smoke @regression'
# Report single result at the end, to avoid mess with required checks in PR
result:
if: ${{ always() }}
needs: run
runs-on: ubuntu-latest
name: Cypress result
steps:
- run: |
result="${{ needs.run.result }}"
if [[ $result == "success" || $result == "skipped" ]]; then
exit 0
else
exit 1
fi
+2
View File
@@ -1,3 +1,4 @@
name: (CI) Cypress Run
on:
workflow_call:
inputs:
@@ -17,6 +18,7 @@ jobs:
fail-fast: false
matrix:
project: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.project }}
runs-on: self-hosted-runner
timeout-minutes: 30
steps:
+12 -9
View File
@@ -8,22 +8,25 @@ on:
jobs:
master:
name: Generate Queries
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Install root dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Generate queries
run: node ./scripts/get-queries.js
- uses: actions/upload-artifact@v2
with:
name: queries
+17 -10
View File
@@ -3,21 +3,28 @@ name: Verify PR title
on:
pull_request:
types: [opened, ready_for_review, reopened, edited, synchronize]
types:
- opened
- ready_for_review
- reopened
- edited
- synchronize
jobs:
lint_pr:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v2
uses: actions/checkout@v3
- name: Setup node
uses: actions/setup-node@v3
with:
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.15.1
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Install root dependencies
run: yarn install
run: yarn install --frozen-lockfile
- name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx commitlint --config ./commitlint.config-ci.js
+1 -1
View File
@@ -7,7 +7,7 @@ on:
jobs:
master:
name: Generate Queries
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
+108
View File
@@ -0,0 +1,108 @@
name: (CD) Publish docker + s3
on:
workflow_call:
inputs:
projects:
required: true
type: string
jobs:
publish-dist:
strategy:
fail-fast: false
matrix:
app: ${{ fromJSON(inputs.projects) }}
name: ${{ matrix.app }}
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Available platforms
run: echo ${{ steps.qemu.outputs.platforms }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# https://docs.github.com/en/actions/learn-github-actions/contexts
- name: Check node version
id: tags
run: |
nodeVersion=$(cat .nvmrc | head -n 1)
echo ::set-output name=nodeVersion::${nodeVersion}
if [[ "${{ github.event_name }}" = "push" ]]; then
envName="$(echo ${{ github.ref }} | rev | cut -d '/' -f 1 | rev)"
bucketName="${{ github.event.repository.name }}-$envName"
echo ::set-output name=bucketName::${bucketName}
echo ::set-output name=envName::${envName}
fi
- name: Build and export to local Docker
id: docker_build
uses: docker/build-push-action@v3
with:
load: true
build-args: |
APP=${{ matrix.app }}
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
ENV_NAME=${{ steps.tags.outputs.envName || '' }}
tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- name: Sanity check docker image
run: |
echo "Check ipfs-hash"
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local cat ipfs-hash
echo "List html directory"
docker run --rm ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local ls -lah
echo "Copy dist to local filesystem"
docker create --name=dist ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
docker cp dist:/usr/share/nginx/html dist
echo "Check local dist"
ls -al dist
- name: Publish dist as docker image
uses: docker/build-push-action@v3
if: ${{ github.event_name == 'pull_request' }}
with:
push: true
build-args: |
APP=${{ matrix.app }}
NODE_VERSION=${{ steps.tags.outputs.nodeVersion }}
tags: |
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:${{ github.event.pull_request.head.sha || github.sha }}
# - uses: shallwefootball/s3-upload-action@master
# if: ${{ github.event_name == 'push' }}
# name: Upload dist S3
# with:
# aws_key_id: ${{ secrets.AWS_KEY_ID }}
# aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY}}
# aws_bucket: ${{ steps.tags.outputs.bucketName }}
# source_dir: 'dist'
- name: Add preview label
uses: actions-ecosystem/action-add-labels@v1
if: ${{ github.event_name == 'pull_request' }}
with:
labels: ${{ matrix.app }}-preview
number: ${{ github.event.number }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
@@ -1,126 +0,0 @@
name: Publish docker containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-*'
workflow_dispatch:
inputs:
publish:
description: 'Publish tag to Docker Hub & GitHub Registry'
required: false
type: boolean
default: false
tag:
description: 'Git Tag to build and publish'
required: false
type: string
default: ''
apps:
description: 'Applications to build and publish'
required: false
type: choice
options:
- '["explorer", "token", "trading"]'
- '["explorer"]'
- '["token"]'
- '["trading"]'
archs:
description: 'Architecture to build and publish'
required: false
type: choice
options:
- linux/amd64, linux/arm64
- linux/amd64
- linux/arm64
jobs:
master:
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(inputs.apps || '["explorer", "token", "trading"]') }}
name: Build the ${{ matrix.app }} image
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
with:
ref: ${{ inputs.tag }}
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Available platforms
run: echo ${{ steps.qemu.outputs.platforms }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
if: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine Docker Image tag
id: tags
run: |
hash=$(git rev-parse HEAD|cut -b1-8)
versionTag=${{ inputs.tag || startsWith(github.ref, 'refs/tags/') && github.ref_name || '${hash}' }}
echo ::set-output name=version::${versionTag}
echo ::set-output name=npmVersion::$(cat dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }} | grep FROM | head -n 1 | awk '{print $2}' | cut -d ':' -f 2 | cut -d '-' -f 1 )
- name: Print config
run: |
git rev-parse --verify HEAD
git status
echo "inputs.tag=${{ inputs.tag }}"
echo "inputs.publish=${{ inputs.publish }}"
echo "inputs.apps=${{ inputs.apps }}"
echo "inputs.archs=${{ inputs.archs }}"
echo "steps.tags.outputs.version=${{ steps.tags.outputs.version }}"
- uses: actions/setup-node@v3
with:
node-version: ${{ steps.tags.outputs.npmVersion }}
- name: Build frontend dists
run: |
yarn --verbose --pure-lockfile
yarn nx ${{ matrix.app =='trading' && 'export' || 'build' }} ${{ matrix.app }} --pure-lockfile
- name: Build and export to local Docker
uses: docker/build-push-action@v3
with:
context: .
push: false
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
load: true
tags: vegaprotocol/${{ matrix.app }}:local
- name: Sanity check docker image
run: |
docker run --rm vegaprotocol/${{ matrix.app }}:local cat .env
docker run --rm vegaprotocol/${{ matrix.app }}:local ls -lah
- name: Build and push to DockerHub
id: docker_build
uses: docker/build-push-action@v3
with:
context: .
push: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
platforms: ${{ inputs.archs || 'linux/amd64, linux/arm64' }}
tags: |
vegaprotocol/${{ matrix.app }}:latest
vegaprotocol/${{ matrix.app }}:${{ steps.tags.outputs.version }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
+10 -11
View File
@@ -8,6 +8,7 @@ on:
required: true
type: choice
options:
- announcements
- ui-toolkit
- react-helpers
- tailwindcss-config
@@ -18,29 +19,27 @@ on:
jobs:
publish:
name: Build & Publish - Tag
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
permissions:
contents: 'read'
actions: 'read'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: User Node.js 16
id: Node
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: 16.15.1
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }}
node-version-file: '.nvmrc'
# https://stackoverflow.com/questions/61010294/how-to-cache-yarn-packages-in-github-actions
cache: yarn
- name: Install root dependencies
run: yarn install --frozen-lockfile
- name: Build project
run: yarn nx build ${{inputs.project}}
- name: Publish project to @vegaprotocol
uses: JS-DevTools/npm-publish@v1
with:
-46
View File
@@ -1,46 +0,0 @@
name: Unit tests & build
on:
push:
branches:
- develop
- main
pull_request:
jobs:
pr:
name: Test and lint - PR
runs-on: ubuntu-latest
permissions:
contents: 'read'
actions: 'read'
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: ${{ github.base_ref }}
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v3
with:
node-version: 16.15.1
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }}
- name: Install root dependencies
run: yarn install --frozen-lockfile
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
run: yarn nx affected:lint --max-warnings=0
- name: Test affected
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build
- name: Build affected spec
run: yarn nx affected --target=build-spec
+28
View File
@@ -0,0 +1,28 @@
# Build container
ARG NODE_VERSION
FROM --platform=amd64 node:${NODE_VERSION}-alpine3.16 as build
WORKDIR /app
# Argument to allow building of different apps
ARG APP
ARG ENV_NAME=""
RUN apk add --update --no-cache \
python3 \
make \
gcc \
g++
COPY . ./
RUN yarn --network-timeout 100000 --pure-lockfile
# work around for different build process in trading
RUN sh ./docker-build.sh
# Server environment
# if this fails you need to docker pull nginx:1.23-alpine and pin new SHA
# this is to ensure that we run always same version of alpine to make sure ipfs is indempotent
FROM --platform=amd64 nginx:1.23-alpine@sha256:6318314189b40e73145a48060bff4783a116c34cc7241532d0d94198fb2c9629
# configuration of system
EXPOSE 80
# Copy dist
WORKDIR /usr/share/nginx/html
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=build /app/dist/apps/${APP} /usr/share/nginx/html
RUN apk add --no-cache go-ipfs; ipfs init && echo "$(ipfs add -rQ .)" > ipfs-hash; apk del go-ipfs
+1
View File
@@ -3,6 +3,7 @@ NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_ENV=CUSTOM
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -8,3 +8,4 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -6,3 +6,4 @@ NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest/
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://token.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
+1
View File
@@ -6,6 +6,7 @@ NX_VEGA_ENV=MIRROR
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.xyz/rest/
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://mainnet-mirror.token.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
# App flags
NX_EXPLORER_ASSETS=1
+1
View File
@@ -9,3 +9,4 @@ NX_TENDERMINT_URL=https://tm.sandbox.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.sandbox.vega.xyz/websocket
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://sandbox.token.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -11,3 +11,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://stagnet1.token.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -5,3 +5,4 @@ NX_VEGA_ENV=STAGNET3
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
NX_VEGA_GOVERNANCE_URL=https://stagnet3.token.vega.xyz
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega-dev-releases/releases/
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -8,3 +8,4 @@ NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://token.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -5,6 +5,7 @@ NX_VEGA_URL=https://api-validators-testnet.vega.rocks/graphql
NX_VEGA_REST=https://api-validators-testnet.vega.rocks/
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
+1
View File
@@ -4,3 +4,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
NX_VEGA_ENV=CUSTOM
NX_BLOCK_EXPLORER=
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
+9 -32
View File
@@ -3,16 +3,14 @@ import {
useAssetDetailsDialogStore,
} from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { useEnvironment } from '@vegaprotocol/environment';
import { AnnouncementBanner } from '@vegaprotocol/announcements';
import {
AnnouncementBanner,
BackgroundVideo,
BreadcrumbsContainer,
ButtonLink,
ExternalLink,
Icon,
} from '@vegaprotocol/ui-toolkit';
import classNames from 'classnames';
import { useState } from 'react';
import {
isRouteErrorResponse,
Link,
@@ -37,35 +35,9 @@ const DialogsContainer = () => {
);
};
const MainnetSimAd = () => {
const [shouldDisplayBanner, setShouldDisplayBanner] = useState<boolean>(true);
// Return an empty div so that the grid layout in _app.page.ts
// renders correctly
if (!shouldDisplayBanner) {
return <div />;
}
return (
<AnnouncementBanner>
<div className="grid grid-cols-[auto_1fr] gap-4 font-alpha calt uppercase text-center text-lg text-white">
<button
className="flex items-center"
onClick={() => setShouldDisplayBanner(false)}
>
<Icon name="cross" className="w-6 h-6" ariaLabel="dismiss" />
</button>
<div>
<span className="pr-4">Mainnet sim 3 is live!</span>
<ExternalLink href="https://fairground.wtf/">Learn more</ExternalLink>
</div>
</div>
</AnnouncementBanner>
);
};
export const Layout = () => {
const isHome = Boolean(useMatch(Routes.HOME));
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
return (
<>
<div
@@ -79,7 +51,12 @@ export const Layout = () => {
)}
>
<div>
<MainnetSimAd />
{ANNOUNCEMENTS_CONFIG_URL && (
<AnnouncementBanner
app="explorer"
configUrl={ANNOUNCEMENTS_CONFIG_URL}
/>
)}
<Header />
</div>
<div>
+2
View File
@@ -13,6 +13,8 @@ NX_LOCAL_PROVIDER_URL=http://localhost:8545/
NX_VEGA_WALLET_URL=http://localhost:1789
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
#Test configuration variables
CYPRESS_FAIRGROUND=false
@@ -1,9 +1,28 @@
import { associateTokenStartOfTests } from '../../support/common.functions';
import {
navigateTo,
waitForSpinner,
navigation,
} from '../../support/common.functions';
import {
convertUnixTimestampToDateformat,
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getGovernanceProposalDateFormatForSpecifiedDays,
getProposalIdFromList,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
} from '../../support/governance.functions';
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../../../governance-e2e/src/support/governance.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governance-e2e/src/support/staking.functions';
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
const proposalVoteProgressForPercentage =
'[data-testid="vote-progress-indicator-percentage-for"]';
@@ -25,24 +44,23 @@ describe(
function () {
before('connect wallets and set approval limit', function () {
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
associateTokenStartOfTests();
vegaWalletSetSpecifiedApprovalAmount('1000');
});
beforeEach('visit proposals tab', function () {
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.navigate_to_page_if_not_already_loaded('proposals');
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals);
});
// 3001-VOTE-055
// 3001-VOTE-050 3001-VOTE-054 3001-VOTE-055 3002-PROP-019
it('Newly created raw proposal details - shows proposal title and full description', function () {
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_proposal_id_from_list(rawProposal.rationale.title);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalIdFromList(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
cy.get(`#${proposalId}`).within(() => {
@@ -69,30 +87,26 @@ describe(
it('Newly created freeform proposal details - shows proposed and closing dates', function () {
const closingVoteHrs = '72';
const proposalTitle = generateFreeFormProposalTitle();
const proposalTimeStamp = createTenDigitUnixTimeStampForSpecifiedDays(3);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.create_ten_digit_unix_timestamp_for_specified_days('3').then(
(closingDateTimestamp) => {
cy.enter_unique_freeform_proposal_body(closingVoteHrs, proposalTitle);
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(
() => cy.get(viewProposalButton).click()
);
cy.convert_unix_timestamp_to_governance_data_table_date_format(
closingDateTimestamp
).then((closingDate) => {
cy.get_proposal_information_from_table('Closes on')
.contains(closingDate)
.should('be.visible');
});
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(closingVoteHrs, proposalTitle);
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
convertUnixTimestampToDateformat(proposalTimeStamp).then(
(closingDate) => {
getProposalInformationFromTable('Closes on')
.contains(closingDate)
.should('be.visible');
}
);
cy.get_governance_proposal_date_format_for_specified_days('0').then(
getGovernanceProposalDateFormatForSpecifiedDays(0).then(
(proposalDate) => {
cy.get_proposal_information_from_table('Proposed on')
getProposalInformationFromTable('Proposed on')
.contains(proposalDate)
.should('be.visible');
}
@@ -104,25 +118,25 @@ describe(
// 3001-VOTE-040
// 3001-VOTE-067
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
'be.visible'
);
cy.get_proposal_information_from_table('Expected to pass')
getProposalInformationFromTable('Expected to pass')
.contains('👎')
.should('be.visible');
// 3001-VOTE-062
// 3001-VOTE-040
// 3001-VOTE-070
cy.get_proposal_information_from_table('Token majority met')
getProposalInformationFromTable('Token majority met')
.contains('👎')
.should('be.visible');
// 3001-VOTE-068
cy.get_proposal_information_from_table('Token participation met')
getProposalInformationFromTable('Token participation met')
.contains('👎')
.should('be.visible');
});
@@ -130,28 +144,27 @@ describe(
// 3001-VOTE-080 3001-VOTE-090 3001-VOTE-069 3001-VOTE-072 3001-VOTE-073
it('Newly created proposal details - ability to vote for and against proposal - with minimum required tokens associated', function () {
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
// 3001-VOTE-080
cy.getByTestId('vote-buttons').contains('against').should('be.visible');
cy.getByTestId('vote-buttons').contains('for').should('be.visible');
cy.vote_for_proposal('for');
cy.get_governance_proposal_date_format_for_specified_days(
'0',
'shortMonth'
).then((votedDate) => {
// 3001-VOTE-051
// 3001-VOTE-093
cy.contains('You voted:')
.siblings()
.contains('For')
.siblings()
.contains(votedDate)
.should('be.visible');
});
voteForProposal('for');
getGovernanceProposalDateFormatForSpecifiedDays(0, 'shortMonth').then(
(votedDate) => {
// 3001-VOTE-051
// 3001-VOTE-093
cy.contains('You voted:')
.siblings()
.contains('For')
.siblings()
.contains(votedDate)
.should('be.visible');
}
);
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
.contains('100.00%')
.and('be.visible');
@@ -162,38 +175,38 @@ describe(
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', parseFloat(1).toFixed(2))
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
// 3001-VOTE-061
cy.get_proposal_information_from_table('Participation required')
.contains(0.001)
getProposalInformationFromTable('Participation required')
.contains('0.00%')
.should('be.visible');
// 3001-VOTE-066
cy.get_proposal_information_from_table('Majority Required') // 3001-VOTE-073
.contains(`${parseFloat(100).toFixed(2)}%`)
getProposalInformationFromTable('Majority Required') // 3001-VOTE-073
.contains(`${(66).toFixed(2)}%`)
.should('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('for');
voteForProposal('for');
// 3001-VOTE-064
cy.get_proposal_information_from_table('Tokens for proposal')
.should('have.text', parseFloat(1).toFixed(2))
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('against');
voteForProposal('against');
cy.get(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
.should('have.text', parseFloat(1).toFixed(2))
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
});
@@ -201,30 +214,31 @@ describe(
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
it('Newly created proposal details - ability to increase associated tokens - by voting again after association', function () {
createRawProposal();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
)
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
.as('submittedProposal')
.within(() => cy.get(viewProposalButton).click());
});
cy.vote_for_proposal('for');
voteForProposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.get_proposal_information_from_table('Total Supply')
getProposalInformationFromTable('Total Supply')
.invoke('text')
.then((totalSupply) => {
let tokensRequiredToAchieveResult = parseFloat(
(totalSupply.replace(/,/g, '') * 0.001) / 100
const tokensRequiredToAchieveResult = (
(Number(totalSupply.replace(/,/g, '')) * 0.001) /
100
).toFixed(2);
cy.ensure_specified_unstaked_tokens_are_associated(
ensureSpecifiedUnstakedTokensAreAssociated(
tokensRequiredToAchieveResult
);
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click()
);
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
.as('submittedProposal')
.within(() => cy.get(viewProposalButton).click());
});
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
@@ -233,38 +247,36 @@ describe(
.and('be.visible');
// 3001-VOTE-065
cy.get(changeVoteButton).should('be.visible').click();
cy.vote_for_proposal('for');
voteForProposal('for');
cy.get(proposalVoteProgressForTokens)
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.get_proposal_information_from_table(
'Total tokens voted percentage'
)
getProposalInformationFromTable('Total tokens voted percentage')
.should('have.text', '0.00%')
.and('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', tokensRequiredToAchieveResult)
.and('be.visible');
cy.get_proposal_information_from_table('Tokens against proposal')
getProposalInformationFromTable('Tokens against proposal')
.should('have.text', '0.00')
.and('be.visible');
cy.get_proposal_information_from_table('Number of voting parties')
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.get_proposal_information_from_table('Expected to pass')
getProposalInformationFromTable('Expected to pass')
.contains('👍')
.should('be.visible');
// 3001-VOTE-062
cy.get_proposal_information_from_table('Token majority met')
getProposalInformationFromTable('Token majority met')
.contains('👍')
.should('be.visible');
cy.get_proposal_information_from_table('Token participation met')
getProposalInformationFromTable('Token participation met')
.contains('👍')
.should('be.visible');
cy.get_proposal_information_from_table('Tokens for proposal')
getProposalInformationFromTable('Tokens for proposal')
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
});
@@ -1,10 +1,21 @@
/// <reference types="cypress" />
import { associateTokenStartOfTests } from '../../support/governance.functions';
import {
navigateTo,
navigation,
waitForSpinner,
} from '../../support/common.functions';
import {
getProposalInformationFromTable,
voteForProposal,
} from '../../support/governance.functions';
import {
createUpdateNetworkProposalTxBody,
createFreeFormProposalTxBody,
} from '../../support/proposal.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const closedProposals = '[data-testid="closed-proposals"]';
const proposalStatus = '[data-testid="proposal-status"]';
@@ -21,20 +32,19 @@ context(
function () {
before('Connect wallets and set approval', function () {
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
associateTokenStartOfTests();
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated('1');
cy.clearLocalStorage();
});
beforeEach('visit proposals', function () {
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.navigate_to('proposals');
ethereumWalletConnect();
navigateTo(navigation.proposals);
});
// 3001-VOTE-006
@@ -43,7 +53,7 @@ context(
cy.createMarket();
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.get(closedProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil('[data-testid="proposals-list-item"]')
@@ -53,7 +63,7 @@ context(
});
});
cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Enacted')
.and('be.visible');
cy.get(votesTable).within(() => {
@@ -68,22 +78,22 @@ context(
const proposalTx = createUpdateNetworkProposalTxBody();
cy.VegaWalletSubmitProposal(proposalTx);
cy.navigate_to('proposals');
navigateTo(navigation.proposals);
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.get(openProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil('[data-testid="proposals-list-item"]')
.within(() => cy.get(viewProposalButton).click());
});
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
cy.vote_for_proposal('for');
cy.get_proposal_information_from_table('State') // 3001-VOTE-047
voteForProposal('for');
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Passed', proposalTimeout)
.and('be.visible');
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
cy.get(votesTable).within(() => {
@@ -101,43 +111,43 @@ context(
const proposalTx = createFreeFormProposalTxBody();
cy.VegaWalletSubmitProposal(proposalTx);
cy.navigate_to('proposals');
navigateTo(navigation.proposals);
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.get(openProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil('[data-testid="proposals-list-item"]')
.within(() => cy.get(viewProposalButton).click());
});
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
cy.vote_for_proposal('for');
cy.get_proposal_information_from_table('State')
voteForProposal('for');
getProposalInformationFromTable('State')
.contains('Enacted', proposalTimeout)
.and('be.visible');
});
// 3001-VOTE-048 3001-VOTE-049
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
it('Able to fail proposal due to lack of participation', function () {
const proposalTitle = 'Add New free form proposal with short enactment';
const proposalTx = createFreeFormProposalTxBody();
cy.VegaWalletSubmitProposal(proposalTx);
cy.navigate_to('proposals');
navigateTo(navigation.proposals);
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.get(openProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil('[data-testid="proposals-list-item"]')
.within(() => cy.get(viewProposalButton).click());
});
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
cy.get_proposal_information_from_table('State') // 3001-VOTE-047
getProposalInformationFromTable('State') // 3001-VOTE-047
.contains('Declined', proposalTimeout)
.and('be.visible');
cy.get_proposal_information_from_table('Rejection reason')
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
.and('be.visible');
});
@@ -1,22 +1,47 @@
/// <reference types="cypress" />
import {
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../support/governance.functions';
import { associateTokenStartOfTests } from '../../support/common.functions';
import {
verifyUnstakedBalance,
waitForSpinner,
navigateTo,
navigation,
closeDialog,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
closeStakingDialog,
ensureSpecifiedUnstakedTokensAreAssociated,
stakingPageDisassociateTokens,
stakingValidatorPageAddStake,
} from '../../support/staking.functions';
import {
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-teardown.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const vegaWalletStakedBalances =
'[data-testid="vega-wallet-balance-staked-validators"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletAssociatedBalance = '[data-testid="associated-amount"]';
const vegaWalletNameElement = '[data-testid="wallet-name"]';
const vegaWallet = '[data-testid="vega-wallet"]';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const rawProposalData = '[data-testid="proposal-data"]';
const minVoteButton = '[data-testid="min-vote"]';
@@ -40,30 +65,31 @@ context(
cy.visit('/');
cy.get_network_parameters().then((network_parameters) => {
cy.wrap(
network_parameters['spam.protection.proposal.min.tokens'] /
Number(network_parameters['spam.protection.proposal.min.tokens']) /
1000000000000000000
).as('minProposerBalance');
cy.wrap(
network_parameters['spam.protection.voting.min.tokens'] /
Number(network_parameters['spam.protection.voting.min.tokens']) /
1000000000000000000
).as('minVoterBalance');
cy.wrap(
network_parameters['governance.proposal.freeform.requiredMajority'] *
100
Number(
network_parameters['governance.proposal.freeform.requiredMajority']
) * 100
).as('requiredMajority');
});
cy.vega_wallet_set_specified_approval_amount('1000');
associateTokenStartOfTests();
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.associateTokensToVegaWallet('1');
});
beforeEach('visit governance tab', function () {
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.navigate_to_page_if_not_already_loaded('proposals');
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals);
});
it('Should be able to see that no proposals exist', function () {
@@ -80,7 +106,7 @@ context(
// 3002-PROP-003
it('Submit a proposal form - shows how many vega tokens are required to make a proposal', function () {
// 3002-PROP-005
cy.go_to_make_new_proposal(governanceProposalType.NEW_MARKET);
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.contains(
`You must have at least 1 VEGA associated to make a proposal`
).should('be.visible');
@@ -88,7 +114,7 @@ context(
// 3002-PROP-011
it('Able to submit a valid freeform proposal - with minimum required tokens associated', function () {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
goToMakeNewProposal(governanceProposalType.FREEFORM);
cy.get(minVoteButton).should('be.visible'); // 3002-PROP-008
cy.get(maxVoteButton).should('be.visible');
cy.get(votingDate).should('not.be.empty');
@@ -96,40 +122,31 @@ context(
'contain.text',
'we add 2 minutes of extra time'
);
cy.enter_unique_freeform_proposal_body(
'50',
generateFreeFormProposalTitle()
);
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
// 3002-PROP-012
// 3002-PROP-016
cy.wait_for_proposal_submitted();
waitForProposalSubmitted();
});
it('Able to submit a valid freeform proposal - with minimum required tokens associated - but also staked', function () {
cy.ensure_specified_unstaked_tokens_are_associated('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', '2');
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.close_staking_dialog();
ensureSpecifiedUnstakedTokensAreAssociated('2');
verifyUnstakedBalance(2);
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
closeStakingDialog();
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
cy.navigate_to('proposals');
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
'50',
generateFreeFormProposalTitle()
);
cy.wait_for_proposal_submitted();
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', generateFreeFormProposalTitle());
waitForProposalSubmitted();
});
it('Creating a proposal - proposal rejected - when closing time sooner than system default', function () {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
'0.1',
generateFreeFormProposalTitle()
);
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('0.1', generateFreeFormProposalTitle());
cy.contains('Awaiting network confirmation', epochTimeout).should(
'not.exist'
);
@@ -139,8 +156,8 @@ context(
});
it('Creating a proposal - proposal rejected - when closing time later than system default', function () {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body(
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody(
'100000',
generateFreeFormProposalTitle()
);
@@ -154,22 +171,18 @@ context(
// 3001-VOTE-006
it('Creating a proposal - proposal rejected - able to access rejected proposals', function () {
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('1000').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as('rawProposal');
}
);
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(1000));
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
closeDialog();
waitForProposalSync();
navigateTo(navigation.proposals);
cy.get(rejectProposalsLink).click();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => {
cy.contains('Rejected').should('be.visible');
@@ -177,79 +190,68 @@ context(
cy.get(viewProposalButton).click();
});
});
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Rejected')
.and('be.visible');
cy.get_proposal_information_from_table('Rejection reason')
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
.and('be.visible');
cy.get_proposal_information_from_table('Error details')
getProposalInformationFromTable('Error details')
.contains('proposal closing time too late')
.and('be.visible');
});
// 0005-ETXN-004
it('Unable to create a proposal - when no tokens are associated', function () {
cy.vega_wallet_teardown();
const errorMsg =
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)';
vegaWalletTeardown();
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
txTimeout
);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as;
}
);
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection'
);
cy.get(dialogCloseButton).click();
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
});
// 3002-PROP-009
it('Unable to create a proposal - when some but not enough tokens are associated', function () {
cy.ensure_specified_unstaked_tokens_are_associated(0.000001);
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp);
}
);
const errorMsg =
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)';
ensureSpecifiedUnstakedTokensAreAssociated('0.000001');
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Network error: the network blocked the transaction through the spam protection'
);
cy.get(dialogCloseButton).click();
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
});
it('Unable to create a freeform proposal - when json parent section contains unexpected field', function () {
const errorMsg =
'Invalid params: the transaction does not use a valid Vega command: unknown field unexpected" in vega.commands.v1.ProposalSubmission';
// 3001-VOTE-038 3002-PROP-013 3002-PROP-014
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.fixture('/proposals/raw.json').then((freeformProposal) => {
freeformProposal.terms.closingTimestamp = closingDateTimestamp;
freeformProposal.unexpected = `i shouldn't be here`;
let proposalPayload = JSON.stringify(freeformProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
}
);
goToMakeNewProposal(governanceProposalType.RAW);
cy.fixture('/proposals/raw.json').then((freeformProposal) => {
freeformProposal.terms.closingTimestamp =
createTenDigitUnixTimeStampForSpecifiedDays(8);
freeformProposal.unexpected = `i shouldn't be here`;
const proposalPayload = JSON.stringify(freeformProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is malformed'
);
cy.get(dialogCloseButton).click();
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
cy.get(rawProposalData)
.invoke('val')
.should('contain', "i shouldn't be here");
@@ -257,71 +259,64 @@ context(
it('Unable to create a freeform proposal - when json terms section contains unexpected field', function () {
// 3001-VOTE-038
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp = closingDateTimestamp;
rawProposal.terms.unexpectedField = `i shouldn't be here`;
let proposalPayload = JSON.stringify(rawProposal);
const errorMsg =
'Invalid params: the transaction does not use a valid Vega command: unknown field "unexpectedField" in vega.ProposalTerms';
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
}
);
goToMakeNewProposal(governanceProposalType.RAW);
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp =
createTenDigitUnixTimeStampForSpecifiedDays(8);
rawProposal.terms.unexpectedField = `i shouldn't be here`;
const proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is malformed'
);
cy.get(dialogCloseButton).click();
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
});
// 1005-PROP-009
it.skip(
'Unable to vote on a freeform proposal - when some but not enough vega associated',
{ tags: '@smoke' },
function () {
const proposalTitle = generateFreeFormProposalTitle();
it('Unable to vote on a freeform proposal - when some but not enough vega associated', function () {
const proposalTitle = generateFreeFormProposalTitle();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', proposalTitle);
cy.wait_for_proposal_submitted();
cy.staking_page_disassociate_tokens('0.0001');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance).should('have.length', 1);
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
cy.navigate_to('proposals');
cy.get_submitted_proposal_from_proposal_list(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
ensureSpecifiedUnstakedTokensAreAssociated('1');
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', proposalTitle);
waitForProposalSubmitted();
stakingPageDisassociateTokens('0.0001');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
cy.contains('Vote breakdown').should('be.visible', {
timeout: 10000,
});
cy.get(voteButtons).should('not.exist');
cy.getByTestId('min-proposal-requirements').should(
'have.text',
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
);
}
);
});
navigateTo(navigation.proposals);
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
timeout: 10000,
});
cy.get(voteButtons).should('not.exist');
cy.getByTestId('min-proposal-requirements').should(
'have.text',
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
);
});
it('Unable to vote on a proposal - when vega wallet disconnected - option to connect from within', function () {
createRawProposal();
cy.get('[data-testid="manage-vega-wallet"]').click();
cy.get('[data-testid="disconnect"]').click();
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(
rawProposal.rationale.title
).within(() => cy.get(viewProposalButton).click());
});
@@ -339,7 +334,7 @@ context(
'1.00',
txTimeout
);
cy.vote_for_proposal('against');
voteForProposal('against');
// 3001-VOTE-079
cy.contains('You voted: Against').should('be.visible');
});
@@ -1,3 +1,23 @@
import {
closeDialog,
navigateTo,
navigation,
waitForSpinner,
} from '../../support/common.functions';
import {
getProposalInformationFromTable,
goToMakeNewProposal,
voteForProposal,
waitForProposalSubmitted,
} from '../../support/governance.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-teardown.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const openProposals = '[data-testid="open-proposals"]';
const proposalType = '[data-testid="proposal-type"]';
@@ -20,7 +40,6 @@ const maxVoteDeadline = '[data-testid="max-vote"]';
const minValidationDeadline = '[data-testid="min-validation"]';
const minEnactDeadline = '[data-testid="min-enactment"]';
const maxEnactDeadline = '[data-testid="max-enactment"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const inputError = '[data-testid="input-error-text"]';
const enactmentDeadlineError =
'[data-testid="enactment-before-voting-deadline"]';
@@ -29,6 +48,7 @@ const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status';
const tokenVoteStatus = 'token-votes-status';
const proposalTermsSection = 'proposal';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
@@ -49,22 +69,22 @@ context(
{ tags: '@slow' },
function () {
before('connect wallets and set approval limit', function () {
cy.createMarket();
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
vegaWalletSetSpecifiedApprovalAmount('1000');
});
beforeEach('visit governance tab', function () {
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated('1');
cy.navigate_to('proposals');
ethereumWalletConnect();
cy.createMarket();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals);
});
it('Able to submit valid update network parameter proposal', function () {
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
// 3002-PROP-006
cy.get(newProposalTitle).type('Test update network parameter proposal');
// 3002-PROP-007
@@ -78,12 +98,12 @@ context(
cy.get(currentParameterValue).should('have.value', '2s');
cy.get(newProposedParameterValue).type('5s'); // 3007-PNEC-003
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
waitForProposalSubmitted();
});
it('Unable to submit network parameter with missing/invalid fields', function () {
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type(
@@ -108,7 +128,7 @@ context(
it('Able to download network param proposal json', function () {
const downloadFolder = './cypress/downloads/';
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.log('Download proposal file');
cy.get(proposalDownloadBtn)
.should('be.visible')
@@ -156,8 +176,8 @@ context(
});
it('Unable to submit network parameter proposal with vote deadline above enactment deadline', function () {
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.go_to_make_new_proposal(governanceProposalType.NETWORK_PARAMETER);
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.get(newProposalTitle).type('Test update network parameter proposal');
cy.get(newProposalDescription).type('invalid deadlines');
cy.get(proposalParameterSelect).select(
@@ -175,36 +195,39 @@ context(
'have.text',
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)'
);
cy.get(dialogCloseButton).click();
closeDialog();
cy.get(minVoteDeadline).click();
cy.get(enactmentDeadlineError).should('not.exist');
});
// 3003-PMAN-001
it('Able to submit valid new market proposal', function () {
cy.go_to_make_new_proposal(governanceProposalType.NEW_MARKET);
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
let newMarketPayload = JSON.stringify(newMarketProposal);
const newMarketPayload = JSON.stringify(newMarketProposal);
cy.get(newProposalTerms).type(newMarketPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
waitForProposalSubmitted();
});
it('Unable to submit new market proposal with missing/invalid fields', function () {
cy.go_to_make_new_proposal(governanceProposalType.NEW_MARKET);
const errorMsg =
'Invalid params: the transaction is not a valid Vega command: unknown field "filters" in vega.DataSourceDefinition';
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
newMarketProposal.invalid = 'I am an invalid field';
let newMarketPayload = JSON.stringify(newMarketProposal);
const newMarketPayload = JSON.stringify(newMarketProposal);
cy.get(newProposalTerms).type(newMarketPayload, {
parseSpecialCharSequences: false,
delay: 2,
@@ -212,21 +235,18 @@ context(
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.get(feedbackError).should(
'have.text',
'Invalid params: the transaction is not a valid Vega command: unknown field "invalid" in vega.NewMarket'
);
cy.get(feedbackError).should('have.text', errorMsg);
});
// Will fail if run after 'Able to submit update market proposal and vote for proposal'
// 3002-PROP-022
it('Unable to submit update market proposal without equity-like share in the market', function () {
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
@@ -237,23 +257,23 @@ context(
cy.getByTestId('dialog-content')
.find('p')
.should('have.text', 'PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
cy.ensure_specified_unstaked_tokens_are_associated('1');
ensureSpecifiedUnstakedTokensAreAssociated('1');
});
// 3002-PROP-020
it('Unable to submit update market proposal without minimum amount of tokens', function () {
cy.vega_wallet_teardown();
cy.vega_wallet_faucet_assets_without_check(
vegaWalletTeardown();
vegaWalletFaucetAssetsWithoutCheck(
'fUSDC',
'1000000',
vegaWalletPublicKey
);
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
@@ -267,14 +287,14 @@ context(
);
});
// 3001-VOTE-092
// 3001-VOTE-092 3004-PMAC-001
it('Able to submit update market proposal and vote for proposal', function () {
cy.vega_wallet_faucet_assets_without_check(
vegaWalletFaucetAssetsWithoutCheck(
'fUSDC',
'1000000',
vegaWalletPublicKey
);
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_MARKET);
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.get(newProposalTitle).type('Test update market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
@@ -285,20 +305,20 @@ context(
cy.get('dd').eq(2).invoke('text').as('EnactedMarketId');
});
cy.get('@EnactedMarketId').then((marketId) => {
cy.VegaWalletSubmitLiquidityProvision(marketId, '1');
cy.VegaWalletSubmitLiquidityProvision(String(marketId), '1');
});
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
let newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.navigate_to('proposals');
waitForProposalSubmitted();
navigateTo(navigation.proposals);
cy.get('@EnactedMarketId').then((marketId) => {
cy.contains(marketId)
cy.contains(String(marketId))
.parentsUntil(proposalListItem)
.within(() => {
cy.getByTestId(viewProposalBtn).click();
@@ -312,7 +332,7 @@ context(
'contain.text',
'Currently expected to fail'
);
cy.vote_for_proposal('for');
voteForProposal('for');
cy.getByTestId(liquidityVoteStatus).should(
'contain.text',
'Currently expected to pass'
@@ -321,18 +341,19 @@ context(
'contain.text',
'Currently expected to pass'
);
cy.get_proposal_information_from_table('Expected to pass')
.contains('👍 by Token vote')
getProposalInformationFromTable('Expected to pass')
.contains('👍 by token vote')
.should('be.visible');
});
// 3001-VOTE-026 3001-VOTE-027 3001-VOTE-028 3001-VOTE-095 3001-VOTE-096 3005-PASN-001
it('Able to submit new asset proposal using min deadlines', function () {
cy.go_to_make_new_proposal(governanceProposalType.NEW_ASSET);
cy.get(newProposalTitle).type('Test new asset proposal');
const proposalTitle = 'Test new asset proposal';
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.get(newProposalTitle).type(proposalTitle);
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-asset').then((newAssetProposal) => {
let newAssetPayload = JSON.stringify(newAssetProposal);
const newAssetPayload = JSON.stringify(newAssetProposal);
cy.get(newProposalTerms).type(newAssetPayload, {
parseSpecialCharSequences: false,
delay: 2,
@@ -348,7 +369,7 @@ context(
cy.contains('Proposal waiting for node vote', proposalTimeout).should(
'be.visible'
);
cy.get(dialogCloseButton).click();
closeDialog();
cy.get(newProposalSubmitButton).should('be.visible').click();
// cannot submit a proposal with ERC20 address already in use
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
@@ -358,10 +379,21 @@ context(
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
);
});
closeDialog();
navigateTo(navigation.proposals);
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.within(() => {
cy.getByTestId(viewProposalBtn).click();
});
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('USDT Coin').should('be.visible');
cy.contains('USDT').should('be.visible');
});
});
it('Unable to submit new asset proposal with missing/invalid fields', function () {
cy.go_to_make_new_proposal(governanceProposalType.NEW_ASSET);
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type('Invalid new asset proposal');
@@ -377,13 +409,13 @@ context(
const assetId =
'ebcd94151ae1f0d39a4bde3b21a9c7ae81a80ea4352fb075a92e07608d9c953d';
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_ASSET);
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails();
cy.get(minVoteDeadline).click();
cy.get(minEnactDeadline).click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
cy.navigate_to('proposals');
waitForProposalSubmitted();
navigateTo(navigation.proposals);
cy.get(openProposals).within(() => {
cy.get(proposalType)
.contains('Update asset')
@@ -393,22 +425,28 @@ context(
cy.getByTestId(viewProposalBtn).click();
});
});
cy.get_proposal_information_from_table('Proposed enactment') // 3001-VOTE-044
getProposalInformationFromTable('Proposed enactment') // 3001-VOTE-044
.invoke('text')
.should('not.be.empty');
// 3001-VOTE-030 3001-VOTE-031
cy.getByTestId(proposalTermsSection).within(() => {
cy.contains('UpdateAsset').should('be.visible');
cy.contains('UpdateERC20').should('be.visible');
cy.contains('"lifetimeLimit": "10"').should('be.visible');
});
});
it('Able to submit update asset proposal using max deadline', function () {
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_ASSET);
it.only('Able to submit update asset proposal using max deadline', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails();
cy.get(maxVoteDeadline).click();
cy.get(maxEnactDeadline).click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wait_for_proposal_submitted();
waitForProposalSubmitted();
});
it('Unable to submit edit asset proposal with missing/invalid fields', function () {
cy.go_to_make_new_proposal(governanceProposalType.UPDATE_ASSET);
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
});
@@ -428,7 +466,7 @@ context(
cy.get(newProposalTitle).type('Test update asset proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/update-asset').then((newAssetProposal) => {
let newAssetPayload = JSON.stringify(newAssetProposal);
const newAssetPayload = JSON.stringify(newAssetProposal);
cy.get(newProposalTerms).type(newAssetPayload, {
parseSpecialCharSequences: false,
delay: 2,
@@ -1,9 +1,28 @@
import type { testFreeformProposal } from '../../support/common-interfaces';
import {
navigateTo,
navigation,
waitForSpinner,
} from '../../support/common.functions';
import {
createFreeformProposal,
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterRawProposalBody,
generateFreeFormProposalTitle,
getProposalIdFromList,
getProposalInformationFromTable,
getSortOrderOfSuppliedArray,
getSubmittedProposalFromProposalList,
goToMakeNewProposal,
governanceProposalType,
voteForProposal,
waitForProposalSubmitted,
waitForProposalSync,
} from '../../support/governance.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
const proposalDetailsTitle = '[data-testid="proposal-title"]';
const openProposals = '[data-testid="open-proposals"]';
@@ -12,17 +31,17 @@ const viewProposalButton = '[data-testid="view-proposal-btn"]';
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
before('connect wallets and set approval limit', function () {
cy.vega_wallet_set_specified_approval_amount('1000');
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.visit('/');
});
beforeEach('visit proposals tab', function () {
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.ensure_specified_unstaked_tokens_are_associated(1);
cy.navigate_to_page_if_not_already_loaded('proposals');
ethereumWalletConnect();
ensureSpecifiedUnstakedTokensAreAssociated('1');
navigateTo(navigation.proposals);
});
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
@@ -30,32 +49,30 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
const maxCloseDays = 3;
// 3001-VOTE-005
let proposalDays = [
const proposalDays = [
minCloseDays + 1,
maxCloseDays,
minCloseDays + 3,
minCloseDays + 2,
];
for (var index = 0; index < proposalDays.length; index++) {
cy.go_to_make_new_proposal(governanceProposalType.RAW);
cy.create_ten_digit_unix_timestamp_for_specified_days(
proposalDays[index]
).then((closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp);
});
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
for (let index = 0; index < proposalDays.length; index++) {
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(
createTenDigitUnixTimeStampForSpecifiedDays(proposalDays[index])
);
waitForProposalSubmitted();
waitForProposalSync();
}
let arrayOfProposals = [];
const arrayOfProposals: string[] = [];
cy.navigate_to('proposals');
navigateTo(navigation.proposals);
cy.get(proposalDetailsTitle)
.each((proposalTitleElement) => {
arrayOfProposals.push(proposalTitleElement.text());
})
.then(() => {
cy.get_sort_order_of_supplied_array(arrayOfProposals).should(
cy.wrap(getSortOrderOfSuppliedArray(arrayOfProposals)).should(
'equal',
'descending'
);
@@ -67,7 +84,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
const proposalTitle = generateFreeFormProposalTitle();
createFreeformProposal(proposalTitle);
cy.get_proposal_id_from_list(proposalTitle);
getProposalIdFromList(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
@@ -77,8 +94,8 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
it('Newly created proposals list - shows title and portion of summary', function () {
createRawProposal(this.minProposerBalance); // 3001-VOTE-052
cy.get('@rawProposal').then((rawProposal) => {
cy.get_proposal_id_from_list(rawProposal.rationale.title);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalIdFromList(rawProposal.rationale.title);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(openProposals).within(() => {
// 3001-VOTE-008
@@ -102,21 +119,22 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
// 3001-VOTE-004
// 3001-VOTE-035
createRawProposal(this.minProposerBalance);
cy.get('@rawProposal').then((rawProposal) => {
cy.get_submitted_proposal_from_proposal_list(
rawProposal.rationale.title
).within(() => {
cy.get(viewProposalButton).should('be.visible').click();
});
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getSubmittedProposalFromProposalList(rawProposal.rationale.title).within(
() => {
cy.get(viewProposalButton).should('be.visible').click();
}
);
cy.get('@proposalIdText').then((proposalId) => {
cy.get_proposal_information_from_table('ID')
.contains(proposalId)
getProposalInformationFromTable('ID')
.contains(String(proposalId))
.and('be.visible');
});
cy.get_proposal_information_from_table('State')
getProposalInformationFromTable('State')
.contains('Open')
.and('be.visible');
cy.get_proposal_information_from_table('Type')
getProposalInformationFromTable('Type')
.contains('Freeform')
.and('be.visible');
});
@@ -125,38 +143,21 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
// 3001-VOTE-071
it('Newly created freeform proposals list - shows proposal participation - both met and not', function () {
const proposalTitle = generateFreeFormProposalTitle();
const requiredParticipation = 0.001;
createFreeformProposal(proposalTitle);
cy.get_submitted_proposal_from_proposal_list(proposalTitle)
.as('submittedProposal')
.within(() => {
// 3001-VOTE-039
cy.get(voteStatus).should('have.text', 'Participation not reached');
cy.get(viewProposalButton).click();
});
cy.vote_for_proposal('for');
cy.get_proposal_information_from_table('Total Supply')
.invoke('text')
.then((totalSupply) => {
let tokensRequiredToAchieveResult = parseFloat(
(totalSupply.replace(/,/g, '') * requiredParticipation) / 100
).toFixed(2);
cy.ensure_specified_unstaked_tokens_are_associated(
tokensRequiredToAchieveResult
);
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(viewProposalButton).click()
);
cy.get_proposal_information_from_table('Token participation met')
.contains('👍')
.should('be.visible');
cy.navigate_to('proposals');
cy.get('@submittedProposal').within(() =>
cy.get(voteStatus).should('have.text', 'Set to pass')
);
});
getSubmittedProposalFromProposalList(proposalTitle).within(() => {
// 3001-VOTE-039
cy.get(voteStatus).should('have.text', 'Participation not reached');
cy.get(viewProposalButton).click();
});
voteForProposal('for');
navigateTo(navigation.proposals);
getSubmittedProposalFromProposalList(proposalTitle).within(() => {
cy.get(voteStatus).should('have.text', 'Set to pass');
cy.get(viewProposalButton).click();
});
getProposalInformationFromTable('Token participation met')
.contains('👍')
.should('be.visible');
});
});
@@ -1,3 +1,21 @@
import {
navigateTo,
navigation,
waitForSpinner,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
closeStakingDialog,
stakingPageAssociateTokens,
stakingValidatorPageAddStake,
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
depositAsset,
vegaWalletTeardown,
} from '../../support/wallet-teardown.functions';
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
@@ -8,29 +26,29 @@ const rewardsTimeOut = { timeout: 60000 };
context('rewards - flow', { tags: '@slow' }, function () {
before('set up environment to allow rewards', function () {
cy.visit('/');
cy.wait_for_spinner();
cy.deposit_asset(vegaAssetAddress, '1000');
waitForSpinner();
depositAsset(vegaAssetAddress, '1000');
cy.validatorsSelfDelegate();
cy.ethereum_wallet_connect();
ethereumWalletConnect();
cy.connectVegaWallet();
topUpRewardsPool();
cy.navigate_to('validators');
cy.vega_wallet_teardown();
cy.staking_page_associate_tokens('6000');
cy.VegaWalletTopUpRewardsPool(30, 200);
navigateTo(navigation.validators);
vegaWalletTeardown();
stakingPageAssociateTokens('6000');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
'6,000.0',
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('3000');
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(1);
cy.staking_validator_page_add_stake('3000');
cy.close_staking_dialog();
cy.navigate_to('rewards');
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3000');
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(1);
stakingValidatorPageAddStake('3000');
closeStakingDialog();
navigateTo(navigation.rewards);
});
it('Should display rewards per epoch', function () {
@@ -54,7 +72,7 @@ context('rewards - flow', { tags: '@slow' }, function () {
.within(() => {
cy.get('h2').first().invoke('text').as('epochNumber');
});
cy.wait_for_beginning_of_epoch();
waitForBeginningOfEpoch();
cy.get('@epochNumber').then((epochNumber) => {
cy.getByTestId(rewardsTable)
.first()
@@ -76,28 +94,12 @@ context('rewards - flow', { tags: '@slow' }, function () {
cy.get('h2').first().should('contain.text', 'EPOCH');
cy.getByTestId('individual-rewards-asset').should('have.text', 'Vega');
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD')
.should('contain.text', '0.4415')
.and('contain.text', '(44.15%)');
.should('contain.text', '0.1177')
.and('contain.text', '(11.7733%)');
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')
.should('contain.text', '0.0004')
.and('contain.text', '(44.15%)');
cy.getByTestId('total').should('have.text', '0.4419');
.should('contain.text', '0.0001')
.and('contain.text', '(11.7733%)');
cy.getByTestId('total').should('have.text', '0.1179');
});
});
function topUpRewardsPool() {
// Must ensure that test wallet contains assets already and that the tests are within the start and end epochs
cy.exec(
`vega wallet transaction send --wallet ${Cypress.env(
'vegaWalletName'
)} --pubkey ${Cypress.env(
'vegaWalletPublicKey'
)} -p "./src/fixtures/wallet/passphrase" --network DV '{"transfer":{"fromAccountType":4,"toAccountType":12,"to":"0000000000000000000000000000000000000000000000000000000000000000","asset":"b4f2726571fbe8e33b442dc92ed2d7f0d810e21835b7371a7915a365f07ccd9b","amount":"1000000000000000000","recurring":{"startEpoch":30, "endEpoch": 200, "factor":"1"}}}' --home ${Cypress.env(
'vegaWalletLocation'
)}`,
{ failOnNonZeroExit: false }
)
.its('stderr')
.should('contain', '');
}
});
@@ -1,876 +0,0 @@
/// <reference types="cypress" />
const stakeValidatorListTotalStake = '[col-id="stake"] > div > span';
const stakeValidatorListTotalShare = '[col-id="stakeShare"] > div > span';
const stakeValidatorListValidatorStake = '[col-id="stake"] > div > span';
const stakeRemoveStakeRadioButton = '[data-testid="remove-stake-radio"]';
const stakeTokenAmountInputBox = '[data-testid="token-amount-input"]';
const stakeTokenSubmitButton = '[data-testid="token-input-submit-button"]';
const stakeNextEpochValue = '[data-testid="stake-next-epoch"]';
const stakeThisEpochValue = '[data-testid="stake-this-epoch"]';
const stakeAddStakeRadioButton = '[data-testid="add-stake-radio"]';
const stakeMaximumTokens = '[data-testid="token-amount-use-maximum"]';
const totalStake = '[data-testid="total-stake"]';
const stakeShare = '[data-testid="stake-percentage"]';
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]:visible';
const vegaWalletStakedBalances =
'[data-testid="vega-wallet-balance-staked-validators"]';
const ethWalletAssociatedBalances =
'[data-testid="eth-wallet-associated-balances"]';
const ethWalletTotalAssociatedBalance =
'[data-testid="currency-locked"]:visible';
const ethWalletContainer = '[data-testid="ethereum-wallet"]:visible';
const vegaWallet = '[data-testid="vega-wallet"]';
const partValidatorId = '…';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
context(
'Staking Tab - with eth and vega wallets connected',
{ tags: '@slow' },
function () {
// 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
cy.vega_wallet_set_specified_approval_amount('1000');
});
describe('Eth wallet - contains VEGA tokens', function () {
beforeEach(
'teardown wallet & drill into a specific validator',
function () {
cy.reload();
cy.wait_for_spinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.vega_wallet_teardown();
cy.navigate_to('validators');
}
);
it('Able to stake against a validator - using vega from wallet', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('3.0', txTimeout)
.should('be.visible');
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort, txTimeout)
.parent()
.should('contain', 3.0, txTimeout);
cy.get('button').contains('Select a validator to nominate').click();
// 2001-STKE-031
cy.click_on_validator_from_list(0);
// 2001-STKE-033, 2001-STKE-034, 2001-STKE-037
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
// 2001-STKE-039
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', partValidatorId);
cy.get(stakeNextEpochValue, epochTimeout) // 2001-STKE-016 2001-STKE-038
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(stakeThisEpochValue, epochTimeout) // 2001-STKE-013
.contains(2.0, epochTimeout)
.should('be.visible');
cy.close_staking_dialog();
cy.navigate_to('validators');
// 2002-SINC-007
cy.validate_validator_list_total_stake_and_share(
'0',
'2.00',
'100.00%'
);
});
it('Able to stake against a validator - using vega from vesting contract', function () {
cy.staking_page_associate_tokens('3', { type: 'contract' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('3.0', txTimeout)
.should('be.visible');
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort, txTimeout)
.parent()
.should('contain', 3.0, txTimeout);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', partValidatorId);
cy.get(stakeNextEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(stakeThisEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share(
'0',
'2.00',
'100.00%'
);
});
it('Able to stake against a validator - using vega from both wallet and vesting contract', function () {
cy.staking_page_associate_tokens('3', { type: 'contract' });
cy.navigate_to('validators');
cy.staking_page_associate_tokens('4', { type: 'wallet' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
7.0,
txTimeout
);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('3.0', txTimeout)
.should('be.visible');
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('4.0', txTimeout)
.should('be.visible');
cy.get(ethWalletAssociatedBalances, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get(ethWalletAssociatedBalances, txTimeout).should(
'contain',
4.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('6');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 6.0, txTimeout)
.and('contain', partValidatorId);
cy.get(stakeNextEpochValue, epochTimeout)
.contains(6.0, epochTimeout)
.should('be.visible');
cy.get(stakeThisEpochValue, epochTimeout)
.contains(6.0, epochTimeout)
.should('be.visible');
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share(
'0',
'6.00',
'100.00%'
);
});
it('Able to stake against multiple validators', function () {
cy.staking_page_associate_tokens('5');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
5.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.parent()
.should('contain', 2.0, txTimeout);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(1);
cy.staking_validator_page_add_stake('1');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('have.length', 2, txTimeout)
.eq(0)
.should('contain', 2.0, txTimeout);
cy.get(vegaWalletStakedBalances, txTimeout)
.eq(1)
.should('contain', 1.0, txTimeout);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.get(`[row-id="${0}"]`).within(() => {
cy.get(stakeValidatorListTotalStake)
.should('have.text', '2.00')
.and('be.visible');
cy.get(stakeValidatorListTotalShare)
.should('have.text', '66.67%')
.and('be.visible');
cy.get(stakeValidatorListValidatorStake)
.scrollIntoView()
.should('have.text', '2.00')
.and('be.visible');
});
cy.get(`[row-id="${1}"]`).within(() => {
cy.get(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
cy.get(stakeValidatorListTotalShare)
.should('have.text', '33.33%')
.and('be.visible');
cy.get(stakeValidatorListValidatorStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
});
});
// 2001-STKE-041
it(
'Able to remove part of a stake against a validator',
{ tags: '@smoke' },
function () {
cy.staking_page_associate_tokens('4');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
4.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('3');
cy.get(stakeNextEpochValue, epochTimeout)
.contains(3.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
// 2001-STKE-040
cy.click_on_validator_from_list(0);
// 2001-STKE-044, 2001-STKE-048
cy.staking_validator_page_remove_stake('1');
// 2001-STKE-049
cy.get(stakeNextEpochValue, epochTimeout).contains(2.0, epochTimeout);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.get(stakeNextEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(stakeThisEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(totalStake, epochTimeout).should('contain.text', '2');
cy.get(stakeShare, epochTimeout).should('have.text', '100%');
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share(
'0',
'2.00',
'100.00%'
);
}
);
// 2001-STKE-045
it('Able to remove a full stake against a validator', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('1');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list('0');
cy.staking_validator_page_remove_stake('1');
cy.get(stakeNextEpochValue, epochTimeout)
.contains(0.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get(stakeNextEpochValue, epochTimeout)
.contains(0.0, epochTimeout)
.should('be.visible');
cy.get(stakeThisEpochValue, epochTimeout)
.contains(0.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '0.00', '0.00%');
});
it('Unable to remove a stake with a negative value for a validator', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(stakeNextEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
cy.get(stakeRemoveStakeRadioButton, txTimeout).click();
cy.get(stakeTokenAmountInputBox).type('-0.1');
cy.contains('Waiting for next epoch to start', epochTimeout);
cy.get(stakeTokenSubmitButton)
.should('be.disabled', epochTimeout)
.and('contain', `Remove -0.1 $VEGA tokens at the end of epoch`)
.and('be.visible');
});
it('Unable to remove a stake greater than staked amount next epoch for a validator', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(stakeNextEpochValue, epochTimeout)
.contains(2.0, epochTimeout)
.should('be.visible');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.close_staking_dialog();
cy.navigate_to('validators');
cy.click_on_validator_from_list(0);
cy.get(stakeRemoveStakeRadioButton).click();
cy.get(stakeTokenAmountInputBox).type(4);
cy.contains('Waiting for next epoch to start', epochTimeout);
cy.get(stakeTokenSubmitButton)
.should('be.disabled', epochTimeout)
.and('contain', `Remove 4 $VEGA tokens at the end of epoch`)
.and('be.visible');
});
it('Disassociating all wallet tokens max - removes all staked tokens', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list('1');
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_disassociate_all_tokens('wallet');
cy.get(ethWalletContainer).within(() => {
cy.contains(vegaWalletPublicKeyShort, txTimeout).should('not.exist');
});
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('0.0', txTimeout)
.should('be.visible');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.00'
);
});
cy.get(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '0.00', '0.00%');
});
it('Disassociating all vesting contract tokens max - removes all staked tokens', function () {
cy.staking_page_associate_tokens('3', { type: 'contract' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list('1');
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_disassociate_all_tokens('contract');
cy.get(ethWalletContainer).within(() => {
cy.contains(vegaWalletPublicKeyShort, txTimeout).should('not.exist');
});
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('0.0', txTimeout)
.should('be.visible');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.00'
);
});
cy.get(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share('0', '0.00', '0.00%');
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
2.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_disassociate_tokens('1');
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('2.0', txTimeout)
.should('be.visible');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'2.00'
);
});
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', 2.0, txTimeout)
.and('contain', partValidatorId);
cy.navigate_to('validators');
cy.validate_validator_list_total_stake_and_share(
'0',
'2.00',
'100.00%'
);
});
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 2001-STKE-004
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('3');
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_associate_tokens('4');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
0.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
7.0,
txTimeout
);
});
it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 2001-STKE-004
cy.staking_page_associate_tokens('3', { type: 'contract' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('3');
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_associate_tokens('4', { type: 'contract' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
0.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
7.0,
txTimeout
);
});
it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () {
// 2001-STKE-004
cy.staking_page_associate_tokens('3', { type: 'wallet' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('3');
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_associate_tokens('4', { type: 'contract' });
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
0.0,
txTimeout
);
cy.get(vegaWalletStakedBalances, txTimeout).should(
'contain',
7.0,
txTimeout
);
});
it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () {
// 2001-STKE-004
cy.staking_page_associate_tokens('6');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
6.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
0.0,
txTimeout
);
cy.close_staking_dialog();
cy.click_on_validator_from_list(1);
cy.staking_validator_page_add_stake('4');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
0.0,
txTimeout
);
cy.close_staking_dialog();
cy.staking_page_associate_tokens('6');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'12.00'
);
});
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', '4.0', txTimeout)
.and('contain', partValidatorId);
cy.get(vegaWalletStakedBalances, txTimeout)
.should('contain', '8.0')
.and('contain', partValidatorId);
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
0.0,
txTimeout
);
});
it('Selecting use maximum where tokens are already staked - suggests the unstaked token amount', function () {
cy.staking_page_associate_tokens('3');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
3.0,
txTimeout
);
cy.get('button').contains('Select a validator to nominate').click();
cy.click_on_validator_from_list(0);
cy.staking_validator_page_add_stake('2');
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
1.0,
txTimeout
);
cy.close_staking_dialog();
cy.click_on_validator_from_list(0);
cy.get(stakeAddStakeRadioButton).click();
cy.get(stakeMaximumTokens, { timeout: 60000 }).click();
cy.get(stakeTokenSubmitButton).should('contain', 'Add 1 $VEGA tokens');
});
after('teardown wallet', function () {
cy.vega_wallet_teardown();
});
});
}
);
@@ -0,0 +1,501 @@
/// <reference types="cypress" />
import {
verifyUnstakedBalance,
verifyStakedBalance,
verifyEthWalletTotalAssociatedBalance,
verifyEthWalletAssociatedBalance,
waitForSpinner,
navigateTo,
navigation,
} from '../../support/common.functions';
import {
clickOnValidatorFromList,
closeStakingDialog,
ensureSpecifiedUnstakedTokensAreAssociated,
stakingPageAssociateTokens,
stakingPageDisassociateAllTokens,
stakingPageDisassociateTokens,
stakingValidatorPageAddStake,
stakingValidatorPageRemoveStake,
validateValidatorListTotalStakeAndShare,
waitForBeginningOfEpoch,
} from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-teardown.functions';
const stakeValidatorListTotalStake = 'total-stake';
const stakeValidatorListTotalShare = 'total-stake-share';
const stakeValidatorListStakePercentage = 'stake-percentage';
const userStakeBtn = 'my-stake-btn';
const userStake = 'user-stake';
const userStakeShare = 'user-stake-share';
const viewAllValidatorsToggle = 'validators-view-toggle-all';
const viewStakedByMeToggle = 'validators-view-toggle-myStake';
const stakeRemoveStakeRadioButton = 'remove-stake-radio';
const stakeTokenAmountInputBox = 'token-amount-input';
const stakeTokenSubmitButton = 'token-input-submit-button';
const stakeAddStakeRadioButton = 'add-stake-radio';
const stakeMaximumTokens = 'token-amount-use-maximum';
const vegaWalletAssociatedBalance = 'currency-value';
const vegaWalletStakedBalances = 'vega-wallet-balance-staked-validators';
const ethWalletContainer = 'ethereum-wallet';
const vegaWallet = 'vega-wallet';
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
context(
'Staking Tab - with eth and vega wallets connected',
{ tags: '@slow' },
function () {
// 2001-STKE-002, 2001-STKE-032
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
ethereumWalletConnect();
// this is a workaround for #2422 which can be removed once issue is resolved
cy.associateTokensToVegaWallet('4');
vegaWalletSetSpecifiedApprovalAmount('1000');
});
describe('Eth wallet - contains VEGA tokens', function () {
beforeEach(
'teardown wallet & drill into a specific validator',
function () {
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
ethereumWalletConnect();
navigateTo(navigation.validators);
}
);
it('Able to stake against a validator - using vega from wallet', function () {
ensureSpecifiedUnstakedTokensAreAssociated('3');
verifyUnstakedBalance(3.0);
verifyEthWalletTotalAssociatedBalance('3.0');
verifyEthWalletAssociatedBalance('3.0');
cy.get('button').contains('Select a validator to nominate').click();
// 2001-STKE-031
clickOnValidatorFromList(0);
// 2001-STKE-033, 2001-STKE-034, 2001-STKE-037
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0);
// 2001-STKE-039
verifyStakedBalance(2.0);
verifyNextEpochValue(2.0); // 2001-STKE-016 2001-STKE-038
verifyThisEpochValue(2.0); // 2001-STKE-013
closeStakingDialog();
navigateTo(navigation.validators);
// 2002-SINC-007
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Able to view validators staked by me', function () {
ensureSpecifiedUnstakedTokensAreAssociated('4');
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
closeStakingDialog();
navigateTo(navigation.validators);
cy.getByTestId(userStake, epochTimeout)
.first()
.should('have.text', '2.00');
cy.getByTestId('total-stake').first().realHover();
cy.getByTestId('staked-by-user-tooltip')
.first()
.should('have.text', 'Staked by me: 2.00');
cy.getByTestId('total-pending-stake').first().realHover();
cy.getByTestId('pending-user-stake-tooltip')
.first()
.should('have.text', 'My pending stake: 0.00');
cy.getByTestId(userStakeShare).invoke('text').should('not.be.empty'); // Adjust when #3286 is resolved
cy.getByTestId(userStakeBtn).should('exist').click();
verifyThisEpochValue(2.0);
navigateTo(navigation.validators);
cy.getByTestId(viewStakedByMeToggle).click();
cy.getByTestId(userStakeBtn).should('have.length', 1);
cy.getByTestId(viewAllValidatorsToggle).click();
clickOnValidatorFromList(1);
stakingValidatorPageAddStake('2');
closeStakingDialog();
navigateTo(navigation.validators);
cy.getByTestId(viewStakedByMeToggle).click();
cy.getByTestId(userStakeBtn).should('have.length', 2);
});
it('Able to stake against a validator - using vega from vesting contract', function () {
stakingPageAssociateTokens('3', { type: 'contract' });
verifyUnstakedBalance(3.0);
verifyEthWalletTotalAssociatedBalance('3.0');
verifyEthWalletAssociatedBalance('3.0');
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0);
verifyStakedBalance(2.0);
verifyNextEpochValue(2.0);
verifyThisEpochValue(2.0);
closeStakingDialog();
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Able to stake against a validator - using vega from both wallet and vesting contract', function () {
vegaWalletTeardown();
stakingPageAssociateTokens('3', { type: 'contract' });
navigateTo(navigation.validators);
stakingPageAssociateTokens('4', { type: 'wallet' });
verifyUnstakedBalance(7.0);
verifyEthWalletTotalAssociatedBalance('3.0');
verifyEthWalletTotalAssociatedBalance('4.0');
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('6');
verifyUnstakedBalance(1.0);
verifyStakedBalance(6.0);
verifyNextEpochValue(6.0);
verifyThisEpochValue(6.0);
closeStakingDialog();
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '6.00', '100.00%');
});
it('Able to stake against multiple validators', function () {
stakingPageAssociateTokens('5');
verifyUnstakedBalance(5.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(3.0);
cy.getByTestId(vegaWalletStakedBalances, txTimeout)
.parent()
.should('contain', 2.0, txTimeout);
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(1);
stakingValidatorPageAddStake('1');
verifyUnstakedBalance(2.0);
cy.getByTestId(vegaWalletStakedBalances, txTimeout)
.should('have.length', 4, txTimeout)
.eq(0)
.should('contain', 2.0, txTimeout);
cy.getByTestId(vegaWalletStakedBalances, txTimeout)
.eq(1)
.should('contain', 1.0, txTimeout);
closeStakingDialog();
navigateTo(navigation.validators);
cy.get(`[row-id="${0}"]`).within(() => {
cy.getByTestId(stakeValidatorListTotalStake)
.should('have.text', '2.00')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '66.67%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '2.00')
.and('be.visible');
});
cy.get(`[row-id="${1}"]`).within(() => {
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalShare)
.should('have.text', '33.33%')
.and('be.visible');
cy.getByTestId(stakeValidatorListTotalStake)
.scrollIntoView()
.should('have.text', '1.00')
.and('be.visible');
});
});
// 2001-STKE-041
it(
'Able to remove part of a stake against a validator',
{ tags: '@smoke' },
function () {
ensureSpecifiedUnstakedTokensAreAssociated('4');
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3');
verifyNextEpochValue(3.0);
verifyUnstakedBalance(1.0);
closeStakingDialog();
navigateTo(navigation.validators);
// 2001-STKE-040
clickOnValidatorFromList(0);
// 2001-STKE-044, 2001-STKE-048
stakingValidatorPageRemoveStake('1');
// 2001-STKE-049
verifyNextEpochValue(2.0);
verifyUnstakedBalance(2.0);
verifyStakedBalance(2.0);
verifyNextEpochValue(2.0);
verifyThisEpochValue(2.0);
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
'contain.text',
'2'
);
waitForBeginningOfEpoch();
cy.getByTestId(stakeValidatorListStakePercentage).should(
'have.text',
'100%'
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
}
);
// 2001-STKE-045
it('Able to remove a full stake against a validator', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('1');
verifyUnstakedBalance(2.0);
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
stakingValidatorPageRemoveStake('1');
verifyNextEpochValue(0.0);
verifyUnstakedBalance(3.0);
verifyNextEpochValue(0.0);
verifyThisEpochValue(0.0);
cy.getByTestId(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
cy.getByTestId(userStakeBtn).should('not.exist');
cy.getByTestId(userStake).should('not.exist');
cy.getByTestId(userStakeShare).should('not.exist');
});
it('Unable to remove a stake with a negative value for a validator', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyNextEpochValue(2.0);
verifyUnstakedBalance(1.0);
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
cy.getByTestId(stakeRemoveStakeRadioButton, txTimeout).click();
cy.getByTestId(stakeTokenAmountInputBox).type('-0.1');
cy.contains('Waiting for next epoch to start', epochTimeout);
cy.getByTestId(stakeTokenSubmitButton)
.should('be.disabled', epochTimeout)
.and('contain', `Remove -0.1 $VEGA tokens at the end of epoch`)
.and('be.visible');
});
it('Unable to remove a stake greater than staked amount next epoch for a validator', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyNextEpochValue(2.0);
verifyUnstakedBalance(3.0);
closeStakingDialog();
navigateTo(navigation.validators);
clickOnValidatorFromList(0);
cy.getByTestId(stakeRemoveStakeRadioButton).click();
cy.getByTestId(stakeTokenAmountInputBox).type('4');
cy.contains('Waiting for next epoch to start', epochTimeout);
cy.getByTestId(stakeTokenSubmitButton)
.should('be.disabled', epochTimeout)
.and('contain', `Remove 4 $VEGA tokens at the end of epoch`)
.and('be.visible');
});
it('Disassociating all wallet tokens max - removes all staked tokens', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(1);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(3.0);
verifyStakedBalance(2.0);
closeStakingDialog();
stakingPageDisassociateAllTokens();
cy.getByTestId(ethWalletContainer).within(() => {
cy.contains(vegaWalletPublicKeyShort, txTimeout).should('not.exist');
});
verifyEthWalletTotalAssociatedBalance('0.0');
cy.getByTestId(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.00'
);
});
cy.getByTestId(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
});
it('Disassociating all vesting contract tokens max - removes all staked tokens', function () {
stakingPageAssociateTokens('3', { type: 'contract' });
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(1);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0);
verifyStakedBalance(2.0);
closeStakingDialog();
stakingPageDisassociateAllTokens('contract');
cy.getByTestId(ethWalletContainer).within(() => {
cy.contains(vegaWalletPublicKeyShort, txTimeout).should('not.exist');
});
verifyEthWalletTotalAssociatedBalance('0.0');
cy.getByTestId(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.00'
);
});
cy.getByTestId(vegaWalletStakedBalances, txTimeout).should(
'not.exist',
txTimeout
);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '0.00', '0.00%');
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0);
verifyStakedBalance(2.0);
closeStakingDialog();
stakingPageDisassociateTokens('1');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.getByTestId(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'2.00'
);
});
verifyStakedBalance(2.0);
navigateTo(navigation.validators);
validateValidatorListTotalStakeAndShare('0', '2.00', '100.00%');
});
it('Associating wallet tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 2001-STKE-004
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3');
verifyStakedBalance(3.0);
closeStakingDialog();
stakingPageAssociateTokens('4');
verifyUnstakedBalance(0.0);
verifyStakedBalance(7.0);
});
it('Associating vesting contract tokens - when some already staked - auto stakes tokens to staked validator', function () {
// 2001-STKE-004
stakingPageAssociateTokens('3', { type: 'contract' });
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3');
verifyStakedBalance(3.0);
closeStakingDialog();
stakingPageAssociateTokens('4', { type: 'contract' });
verifyUnstakedBalance(0.0);
verifyStakedBalance(7.0);
});
it('Associating vesting contract tokens - when wallet tokens already staked - auto stakes tokens to staked validator', function () {
// 2001-STKE-004
stakingPageAssociateTokens('3', { type: 'wallet' });
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('3');
verifyStakedBalance(3.0);
closeStakingDialog();
stakingPageAssociateTokens('4', { type: 'contract' });
verifyUnstakedBalance(0.0);
verifyStakedBalance(7.0);
});
it('Associating tokens - with multiple validators already staked - auto stakes to staked validators - abiding by existing stake ratio', function () {
// 2001-STKE-004
stakingPageAssociateTokens('6');
verifyUnstakedBalance(6.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(0.0);
closeStakingDialog();
clickOnValidatorFromList(1);
stakingValidatorPageAddStake('4');
verifyUnstakedBalance(0.0);
closeStakingDialog();
stakingPageAssociateTokens('6');
cy.getByTestId(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'12.00'
);
});
verifyStakedBalance(4.0);
verifyStakedBalance(8.0);
verifyUnstakedBalance(0.0);
});
it('Selecting use maximum where tokens are already staked - suggests the unstaked token amount', function () {
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
clickOnValidatorFromList(0);
stakingValidatorPageAddStake('2');
verifyUnstakedBalance(1.0);
closeStakingDialog();
clickOnValidatorFromList(0);
cy.getByTestId(stakeAddStakeRadioButton).click();
cy.getByTestId(stakeMaximumTokens, { timeout: 60000 }).click();
cy.getByTestId(stakeTokenSubmitButton).should(
'contain',
'Add 1 $VEGA tokens'
);
});
afterEach('Teardown Wallet', function () {
vegaWalletTeardown();
});
function verifyNextEpochValue(amount: number) {
cy.getByTestId('stake-next-epoch', epochTimeout)
.contains(amount, epochTimeout)
.should('be.visible');
}
function verifyThisEpochValue(amount: number) {
cy.getByTestId('stake-this-epoch', epochTimeout) // 2001-STKE-013
.contains(amount, epochTimeout)
.should('be.visible');
}
});
}
);
@@ -1,13 +1,31 @@
import {
verifyEthWalletTotalAssociatedBalance,
verifyEthWalletAssociatedBalance,
waitForSpinner,
navigateTo,
navigation,
} from '../../support/common.functions';
import {
stakingPageAssociateTokens,
stakingPageDisassociateAllTokens,
stakingPageDisassociateTokens,
validateWalletCurrency,
} from '../../support/staking.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
vegaWalletAssociate,
vegaWalletDisassociate,
vegaWalletSetSpecifiedApprovalAmount,
vegaWalletTeardown,
} from '../../support/wallet-teardown.functions';
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const ethWalletAssociatedBalances =
'[data-testid="eth-wallet-associated-balances"]';
const ethWalletTotalAssociatedBalance = '[data-testid="currency-locked"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const txTimeout = Cypress.env('txTimeout');
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const ethWalletAssociateButton = '[href="/token/associate"]';
const ethWalletAssociateButton = '[data-testid="associate-btn"]';
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
@@ -28,7 +46,7 @@ context(
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
// 0005-ETXN-001
cy.vega_wallet_set_specified_approval_amount('1000');
vegaWalletSetSpecifiedApprovalAmount('1000');
});
describe('Eth wallet - contains VEGA tokens', function () {
@@ -36,11 +54,10 @@ context(
'teardown wallet & drill into a specific validator',
function () {
cy.reload();
cy.wait_for_spinner();
cy.ethereum_wallet_connect();
waitForSpinner();
cy.connectVegaWallet();
cy.vega_wallet_teardown();
cy.navigate_to('validators');
ethereumWalletConnect();
vegaWalletTeardown();
}
);
@@ -57,26 +74,21 @@ context(
//0005-ETXN-006
//0005-ETXN-003
//0005-ETXN-005
cy.staking_page_associate_tokens('2', { skipConfirmation: true });
stakingPageAssociateTokens('2', { skipConfirmation: true });
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
3
);
cy.validate_wallet_currency('Associated', '0.00');
cy.validate_wallet_currency('Pending association', '2.00');
cy.validate_wallet_currency('Total associated after pending', '2.00');
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
// 0005-ETXN-002
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent(txTimeout)
.should('contain', 2.0);
verifyEthWalletAssociatedBalance('2.0');
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('2.0', txTimeout)
.should('be.visible');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
@@ -92,50 +104,30 @@ context(
// 1004-ASSO-029
// 1004-ASSO-031
cy.staking_page_associate_tokens('2');
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent(txTimeout)
.should('contain', 2.0);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('2.0', txTimeout)
.should('be.visible');
stakingPageAssociateTokens('2');
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_tokens('2');
stakingPageDisassociateTokens('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
3
);
cy.validate_wallet_currency('Associated', '2.00');
cy.validate_wallet_currency('Pending association', '2.00');
cy.validate_wallet_currency('Total associated after pending', '0.00');
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
cy.get(ethWalletAssociatedBalances, txTimeout).should('not.exist');
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('0.00', txTimeout)
.should('be.visible');
cy.getByTestId('eth-wallet-associated-balances', txTimeout).should(
'not.exist'
);
verifyEthWalletTotalAssociatedBalance('0.00');
});
it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () {
//1004-ASSO-011
cy.staking_page_associate_tokens('1001', { approve: true });
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent()
.should('contain', '1,001.00', txTimeout);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('1,001.00', txTimeout)
.should('be.visible');
stakingPageAssociateTokens('1001', { approve: true });
verifyEthWalletAssociatedBalance('1,001.00');
verifyEthWalletTotalAssociatedBalance('1,001.00');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
@@ -145,26 +137,14 @@ context(
});
it('Able to disassociate a partial amount of tokens currently associated', function () {
cy.staking_page_associate_tokens('2');
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_tokens('1');
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent(txTimeout)
.should('contain', 1.0);
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent(txTimeout)
.should('contain', 1.0);
stakingPageDisassociateTokens('1');
verifyEthWalletAssociatedBalance('1.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
});
@@ -174,32 +154,24 @@ context(
// 1004-ASSO-026
const warningText =
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
cy.staking_page_associate_tokens('2');
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get('button').contains('Select a validator to nominate').click();
cy.get(ethWalletDissociateButton).click();
cy.get(disassociationWarning).should('contain', warningText);
cy.staking_page_disassociate_all_tokens();
stakingPageDisassociateAllTokens();
cy.get(ethWalletContainer).within(() => {
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
'not.exist'
);
});
cy.get(ethWalletContainer).within(() => {
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
'not.exist'
);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
});
@@ -211,8 +183,9 @@ context(
// 1004-ASSO-018
// 1004-ASSO-024
// 1004-ASSO-023
// 1004-ASSO-032
cy.staking_page_associate_tokens('2', {
stakingPageAssociateTokens('2', {
type: 'contract',
skipConfirmation: true,
});
@@ -221,47 +194,30 @@ context(
'have.length.above',
3
);
cy.validate_wallet_currency('Associated', '0.00');
cy.validate_wallet_currency('Pending association', '2.00');
cy.validate_wallet_currency('Total associated after pending', '2.00');
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent(txTimeout)
.should('contain', 2.0);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('2.0', txTimeout)
.should('be.visible');
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
cy.staking_page_disassociate_tokens('1', {
stakingPageDisassociateTokens('1', {
type: 'contract',
skipConfirmation: true,
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
3
);
cy.validate_wallet_currency('Associated', '2.00');
cy.validate_wallet_currency('Pending association', '1.00');
cy.validate_wallet_currency('Total associated after pending', '1.00');
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '1.00');
validateWalletCurrency('Total associated after pending', '1.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
cy.get(ethWalletAssociatedBalances, txTimeout)
.contains(vegaWalletPublicKeyShort)
.parent(txTimeout)
.should('contain', 1.0);
cy.get(ethWalletTotalAssociatedBalance, txTimeout)
.contains('1.0', txTimeout)
.should('be.visible');
verifyEthWalletAssociatedBalance('1.0');
verifyEthWalletTotalAssociatedBalance('1.0');
});
it('Able to associate & disassociate both wallet and vesting contract tokens', function () {
@@ -269,11 +225,9 @@ context(
// 1004-ASSO-020
// 1004-ASSO-021
// 1004-ASSO-022
cy.staking_page_associate_tokens('21', { type: 'wallet' });
stakingPageAssociateTokens('21', { type: 'wallet' });
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_associate_tokens('37', { type: 'contract' });
stakingPageAssociateTokens('37', { type: 'contract' });
cy.get(vestingContractSection).within(() => {
cy.get(associatedKey).should(
'contain',
@@ -281,7 +235,6 @@ context(
);
cy.get(associatedAmount, txTimeout).should('contain', 37);
});
cy.get(vegaInWalletSection).within(() => {
cy.get(associatedKey).should(
'contain',
@@ -289,27 +242,21 @@ context(
);
cy.get(associatedAmount, txTimeout).should('contain', 21);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
});
cy.staking_page_disassociate_tokens('6', { type: 'contract' });
stakingPageDisassociateTokens('6', { type: 'contract' });
cy.get(vestingContractSection).within(() => {
cy.get(associatedAmount, txTimeout).should('contain', 31);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
});
cy.navigate_to('validators');
cy.staking_page_disassociate_tokens('9', { type: 'wallet' });
navigateTo(navigation.validators);
stakingPageDisassociateTokens('9', { type: 'wallet' });
cy.get(vegaInWalletSection).within(() => {
cy.get(associatedAmount, txTimeout).should('contain', 12);
});
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
});
@@ -319,42 +266,41 @@ context(
// 1004-ASSO-008
// 1004-ASSO-010
// No warning visible as described in AC, but the button is disabled
cy.get(ethWalletAssociateButton).first().click();
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
cy.get(tokenAmountInputBox, { timeout: 10000 }).type(6500000);
cy.get(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
cy.get(tokenSubmitButton, txTimeout).should('be.disabled');
});
// 1004-ASSO-004
it('Pending association outside of app is shown', function () {
cy.vega_wallet_associate('2');
vegaWalletAssociate('2');
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
3
);
cy.validate_wallet_currency('Associated', '0.00');
cy.validate_wallet_currency('Pending association', '2.00');
cy.validate_wallet_currency('Total associated after pending', '2.00');
validateWalletCurrency('Associated', '0.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '2.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
cy.validate_wallet_currency('Associated', '2.00');
validateWalletCurrency('Associated', '2.00');
});
it('Disassociation outside of app is shown', function () {
cy.staking_page_associate_tokens('2');
cy.validate_wallet_currency('Associated', '2.00');
cy.vega_wallet_disassociate('2');
stakingPageAssociateTokens('2');
cy.wrap(validateWalletCurrency('Associated', '2.00')).then(() => {
vegaWalletDisassociate('2');
});
cy.getByTestId('currency-title', txTimeout).should(
'have.length.above',
3
);
cy.validate_wallet_currency('Associated', '2.00');
cy.validate_wallet_currency('Pending association', '2.00');
cy.validate_wallet_currency('Total associated after pending', '0.00');
validateWalletCurrency('Associated', '2.00');
validateWalletCurrency('Pending association', '2.00');
validateWalletCurrency('Total associated after pending', '0.00');
cy.getByTestId('currency-title', txTimeout).should('have.length', 3);
cy.validate_wallet_currency('Associated', '0.00');
validateWalletCurrency('Associated', '0.00');
});
it('Able to associate tokens to different public key of connected vega wallet', function () {
@@ -371,7 +317,7 @@ context(
'have.text',
Cypress.env('vegaWalletPublicKey2')
);
cy.staking_page_associate_tokens('2');
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
@@ -381,7 +327,7 @@ context(
'vegaWalletPublicKey2Short'
)} can now participate in governance and nominate a validator with your associated $VEGA.`
);
cy.staking_page_disassociate_all_tokens();
stakingPageDisassociateAllTokens();
});
});
}
@@ -1,5 +1,16 @@
import {
navigateTo,
navigation,
waitForSpinner,
} from '../../support/common.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import {
depositAsset,
vegaWalletTeardown,
} from '../../support/wallet-teardown.functions';
const withdraw = 'withdraw';
const selectAsset = 'select-asset';
const withdrawalForm = 'withdraw-form';
const ethAddressInput = 'eth-address-input';
const amountInput = 'amount-input';
const balanceAvailable = 'BALANCE_AVAILABLE_value';
@@ -17,6 +28,8 @@ const completeWithdrawalButton = 'complete-withdrawal';
const usdtName = 'USDC (local)';
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
const usdcSymbol = 'tUSDC';
const usdtSelectValue =
'993ed98f4f770d91a796faab1738551193ba45c62341d20597df70fea6704ede';
const truncatedWithdrawalEthAddress = '0xEe7D…22d94F';
const formValidationError = 'input-error-text';
const txTimeout = Cypress.env('txTimeout');
@@ -26,60 +39,72 @@ context(
{ tags: '@slow' },
function () {
before('visit withdrawals and connect vega wallet', function () {
cy.updateCapsuleMultiSig(); // When running tests locally, will fail if run without restarting capsule
cy.deposit_asset(usdcEthAddress, '100000000000000000000');
cy.visit('/');
// When running tests locally, will fail if run without restarting capsule
cy.updateCapsuleMultiSig().then(() => {
depositAsset(usdcEthAddress, '100');
});
});
beforeEach('Navigate to withdrawal page', function () {
cy.reload();
cy.visit('/');
cy.wait_for_spinner();
cy.navigate_to('withdraw');
waitForSpinner();
navigateTo(navigation.withdraw);
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.vega_wallet_teardown();
ethereumWalletConnect();
vegaWalletTeardown();
});
it('Able to open withdrawal form with vega wallet connected', function () {
// needs to reload page for withdrawal form to be displayed in ci - not reproducible outside of ci
cy.reload();
waitForSpinner();
ethereumWalletConnect();
cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(selectAsset)
.find('option')
.should('have.length.at.least', 2);
cy.getByTestId(ethAddressInput).should('be.visible');
cy.getByTestId(amountInput).should('be.visible');
cy.getByTestId(withdrawalForm).within(() => {
cy.get('select').find('option').should('have.length.at.least', 2);
cy.getByTestId(ethAddressInput).should('be.visible');
cy.getByTestId(amountInput).should('be.visible');
});
});
it('Unable to submit withdrawal with invalid fields', function () {
cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(selectAsset).select(usdtName);
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should('have.length', 1);
cy.getByTestId(amountInput).clear().click().type('0.0000001');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should(
'have.text',
'Value is below minimum'
);
cy.getByTestId(amountInput).clear().click().type('10');
cy.getByTestId(ethAddressInput).click().type('123');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should(
'have.text',
'Invalid Ethereum address'
);
cy.getByTestId(withdrawalForm).within(() => {
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should('have.length', 1);
cy.getByTestId(amountInput).clear().click().type('0.0000001');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should(
'have.text',
'Value is below minimum'
);
cy.getByTestId(amountInput).clear().click().type('10');
cy.getByTestId(ethAddressInput).click().type('123');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(formValidationError).should(
'have.text',
'Invalid Ethereum address'
);
});
});
it('Able to withdraw asset: -eth wallet connected -withdraw funds button', function () {
// fill in withdrawal form
cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(selectAsset).select(usdtName);
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should('have.text', '100,000.00000T');
cy.getByTestId(delayTime).should('have.text', 'None');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(withdrawalForm).within(() => {
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(withdrawalThreshold).should(
'have.text',
'100,000.00000T'
);
cy.getByTestId(delayTime).should('have.text', 'None');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request
@@ -136,15 +161,17 @@ context(
waitForAssetsDisplayed(usdtName);
// fill in withdrawal form
cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(selectAsset).select(usdtName);
cy.getByTestId(ethAddressInput).should('be.empty');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(withdrawalForm).within(() => {
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(ethAddressInput).should('be.empty');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
// Need eth address to submit withdrawal
cy.getByTestId(formValidationError).should('have.length', 1);
cy.getByTestId(ethAddressInput).click().type(ethWalletAddress);
cy.getByTestId(submitWithdrawalButton).click();
// Need eth address to submit withdrawal
cy.getByTestId(formValidationError).should('have.length', 1);
cy.getByTestId(ethAddressInput).click().type(ethWalletAddress);
cy.getByTestId(submitWithdrawalButton).click();
});
cy.contains('Awaiting network confirmation').should('be.visible');
// assert withdrawal request
@@ -180,10 +207,12 @@ context(
cy.connectPublicKey(vegaWalletPubKey);
cy.getByTestId(withdraw).should('be.visible').click();
cy.getByTestId(selectAsset).select(usdtName);
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
cy.getByTestId(withdrawalForm).within(() => {
cy.get('select').select(usdtSelectValue, { force: true });
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
cy.getByTestId(amountInput).click().type('100');
cy.getByTestId(submitWithdrawalButton).click();
});
cy.getByTestId('dialog-content').within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
@@ -191,7 +220,7 @@ context(
});
});
function waitForAssetsDisplayed(expectedAsset) {
function waitForAssetsDisplayed(expectedAsset: string) {
cy.contains(expectedAsset, txTimeout).should('be.visible');
}
}
@@ -1,111 +0,0 @@
context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
before('visit token home page', function () {
cy.visit('/');
cy.get('nav', { timeout: 10000 }).should('be.visible');
});
describe('with wallets disconnected', function () {
describe('Links and buttons', function () {
it('should have link for proposal page', function () {
cy.getByTestId('home-proposals').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Browse, vote, and propose');
});
});
it('should show open or enacted proposals with proposal summary', function () {
cy.get('body').then(($body) => {
if (!$body.find('[data-testid="proposals-list-item"]').length) {
cy.createMarket();
cy.reload();
cy.wait_for_spinner();
}
});
cy.getByTestId('proposals-list-item')
.should('have.length.at.least', 1)
.first()
.within(() => {
cy.getByTestId('proposal-title')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-type')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-description')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-status')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('vote-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('view-proposal-btn').should('be.visible');
});
});
it('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contain', 'https://vega.xyz/governance');
});
});
it('should have link for validator page', function () {
cy.getByTestId('home-validators').within(() => {
cy.get('[href="/validators"]')
.first()
.should('exist')
.and('have.text', 'Browse, and stake');
});
});
it('should have external link for validators', function () {
cy.getByTestId('home-validators').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and(
'contain',
'https://community.vega.xyz/c/mainnet-validator-candidates'
);
});
});
it('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '2')
.and('contain.text', 'active nodes');
});
it('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '2')
.and('contain.text', 'consensus nodes');
});
it('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '2')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
});
it('should have link for rewards page', function () {
cy.getByTestId('home-rewards').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'See rewards');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('home-vega-token').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Manage tokens');
});
});
});
});
});
@@ -0,0 +1,208 @@
import { waitForSpinner } from '../../support/common.functions';
context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
before('visit token home page', function () {
cy.visit('/');
cy.get('nav', { timeout: 10000 }).should('be.visible');
});
describe('Links and buttons', function () {
it('should have link for proposal page', function () {
cy.getByTestId('home-proposals').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Browse, vote, and propose');
});
});
it('should display announcement banner', function () {
cy.getByTestId('app-announcement')
.should('be.visible')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
cy.getByTestId('app-announcement-close').should('be.visible').click();
cy.getByTestId('app-announcement').should('not.exist');
});
it('should show open or enacted proposals with proposal summary', function () {
cy.get('body').then(($body) => {
if (!$body.find('[data-testid="proposals-list-item"]').length) {
cy.createMarket();
cy.reload();
waitForSpinner();
}
});
cy.getByTestId('proposals-list-item')
.should('have.length.at.least', 1)
.first()
.within(() => {
cy.getByTestId('proposal-title')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-type').invoke('text').should('not.be.empty');
cy.getByTestId('proposal-description')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-details')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('proposal-status')
.invoke('text')
.should('not.be.empty');
cy.getByTestId('vote-details').invoke('text').should('not.be.empty');
cy.getByTestId('view-proposal-btn').should('be.visible');
});
});
it('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and('contain', 'https://vega.xyz/governance');
});
});
it('should have link for validator page', function () {
cy.getByTestId('home-validators').within(() => {
cy.get('[href="/validators"]')
.first()
.should('exist')
.and('have.text', 'Browse, and stake');
});
});
it('should have external link for validators', function () {
cy.getByTestId('home-validators').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
.and(
'contain',
'https://community.vega.xyz/c/mainnet-validator-candidates'
);
});
});
it('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '2')
.and('contain.text', 'active nodes');
});
it('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '2')
.and('contain.text', 'consensus nodes');
});
it('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '2')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
});
it('should have link for rewards page', function () {
cy.getByTestId('home-rewards').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'See rewards');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('home-vega-token').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Manage tokens');
});
});
it('should display network data', function () {
cy.getByTestId('git-network-data')
.should('contain.text', 'Reading network data from')
.within(() => {
cy.get('span')
.first()
.should('have.text', 'http://localhost:3028/query');
cy.getByTestId('link').should('exist');
});
});
it('should display eth data', function () {
cy.getByTestId('git-eth-data')
.should('contain.text', 'Reading Ethereum data from')
.within(() => {
cy.get('span').should('have.text', 'http://localhost:8545');
});
});
it('should contain link for known issues on Github', function () {
cy.getByTestId('git-info').within(() => {
cy.contains('Known issues and feedback on')
.find('[data-testid="link"]')
.should(
'have.attr',
'href',
'https://github.com/vegaprotocol/feedback/discussions'
);
});
});
});
describe('Mobile view - navigation bar', function () {
before('Change to mobile resolution', function () {
cy.viewport('iphone-xr');
});
it('should have burger button', () => {
cy.getByTestId('button-menu-drawer').should('be.visible').click();
cy.getByTestId('menu-drawer').should('be.visible');
});
it('should have link for proposal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
.and('have.text', 'Proposals');
});
});
it('should have link for validator page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/validators"]')
.first()
.should('exist')
.and('have.text', 'Validators');
});
});
it('should have link for rewards page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/rewards"]')
.first()
.should('exist')
.and('have.text', 'Rewards');
});
});
it('should have link for withdrawal page', function () {
cy.getByTestId('menu-drawer').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
.should('exist')
.and('have.text', 'Withdraw');
});
});
after(function () {
cy.viewport(
Cypress.config('viewportWidth'),
Cypress.config('viewportHeight')
);
});
});
});
@@ -1,284 +0,0 @@
context(
'Landing pages - verifies required elements',
{ tags: '@smoke' },
() => {
const navbar = 'nav .navbar';
const mobileNav = '[data-testid="menu-drawer"]';
const topLevelLinks = [
{
name: 'Proposals',
selector: '[href="/proposals"]',
tests: () => {
it('should be able to see a working link for - find out more about Vega governance', function () {
const governanceDocsUrl = 'https://vega.xyz/governance';
const proposalDocumentationLink =
'[data-testid="proposal-documentation-link"]';
// 3001-VOTE-001
cy.get(proposalDocumentationLink)
.should('be.visible')
.and('have.text', 'Find out more about Vega governance')
.and('have.attr', 'href')
.and('equal', governanceDocsUrl);
// 3002-PROP-001
cy.request(governanceDocsUrl)
.its('body')
.then((body) => {
if (!body.includes('Govern the network')) {
assert.include(
body,
'Govern the network',
`Checking that governance link destination includes 'Govern the network' text`
);
}
});
});
it('should be able to see button for - new proposal', function () {
// 3001-VOTE-002
const newProposalLink = '[data-testid="new-proposal-link"]';
cy.get(newProposalLink)
.should('be.visible')
.and('have.text', 'New proposal')
.and('have.attr', 'href')
.and('equal', '/proposals/propose');
});
},
},
{
name: 'Validators',
selector: '[href="/validators"]',
tests: () => {
it('Should have Staking Guide link visible', function () {
// 2001-STKE-003
cy.get('[data-testid="staking-guide-link"]')
.should('be.visible')
.and('have.text', 'Read more about staking on Vega')
.and(
'have.attr',
'href',
'https://docs.vega.xyz/mainnet/concepts/vega-chain/#staking-on-vega'
);
});
},
},
{
name: 'Rewards',
selector: '[href="/rewards"]',
header: 'Rewards and fees',
tests: () => {
it('should have epoch warning', () => {
cy.get('[data-testid="callout"]')
.should('be.visible')
.and(
'have.text',
'Rewards are credited less than a minute after the epoch ends.This delay is set by a network parameter'
);
});
it('should have toggle for seeing total vs individual rewards', () => {
cy.get('[data-testid="epoch-reward-view-toggle-total"]').should(
'be.visible'
);
});
},
},
];
const secondLevelLinks = [
{
trigger: true,
name: 'Token',
selector: '[data-testid="state-trigger"]',
},
{
name: 'Token',
selector: '[href="/token"]',
header: 'The $VEGA token',
},
{
name: 'Supply & Vesting',
selector: '[href="/token/tranches"]',
header: 'Vesting tranches',
},
{
name: 'Withdraw',
selector: '[href="/token/withdraw"]',
header: 'Withdrawals',
tests: () => {
it('should have connect Vega wallet button', function () {
cy.get('[data-testid="connect-to-vega-wallet-btn"]')
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});
},
},
{
name: 'Redeem',
selector: '[href="/token/redeem"]',
header: 'Vesting',
tests: () => {
// 1005-VEST-018
it('should have connect Eth wallet button', function () {
cy.get('[data-testid="connect-to-eth-btn"]')
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet');
});
},
},
{
name: 'Associate',
selector: '[href="/token/associate"]',
header: 'Associate $VEGA tokens with Vega Key',
},
{
name: 'Disassociate',
selector: '[href="/token/disassociate"]',
header: 'Disassociate $VEGA tokens from a Vega key',
},
];
const expand = () => {
const trigger = secondLevelLinks.find((l) => l.trigger).selector;
cy.get(trigger).then((el) => {
if (el.attr('aria-expanded') === 'false') {
el.trigger('click');
}
});
};
const collapse = () => {
const trigger = secondLevelLinks.find((l) => l.trigger).selector;
cy.get(trigger).then((el) => {
if (el.attr('aria-expanded') === 'true') {
el.trigger('click');
}
});
};
const ensureHeader = (text) => {
cy.get('main header h1').should('have.text', text);
};
before(() => {
// goes to HOME
cy.visit('/');
// and waits for it to load
cy.get(navbar, { timeout: 10000 }).should('be.visible');
});
describe('Navigation (desktop)', () => {
for (const { name, selector } of topLevelLinks) {
it(`should have ${name} nav link`, () => {
cy.get(navbar).within(() => {
cy.get(selector).should('be.visible');
cy.get(selector).should('have.text', name);
});
});
}
for (const { name, selector, trigger } of secondLevelLinks) {
it(`should have ${name} ${
trigger ? 'as trigger button' : ''
} second level nav link`, () => {
cy.get(navbar).within(() => {
cy.get(selector).should('be.visible');
cy.get(selector).should('have.text', name);
if (trigger) cy.get(selector).click();
});
});
}
after(() => {
collapse();
});
});
describe('Navigation (mobile)', () => {
beforeEach(() => {
// iphone xr
cy.viewport(414, 896);
});
it('should have burger button', () => {
cy.get('[data-testid="button-menu-drawer"]').should('be.visible');
cy.get('[data-testid="button-menu-drawer"]').click();
cy.get(mobileNav).should('be.visible');
});
for (const { name, selector } of topLevelLinks) {
it(`should have ${name} nav link`, () => {
cy.get(mobileNav).within(() => {
cy.get(selector).should('be.visible');
cy.get(selector).should('have.text', name);
});
});
}
for (const { name, selector, trigger } of secondLevelLinks) {
it(`should have ${name} ${
trigger ? 'as trigger button' : ''
} second level nav link`, () => {
cy.get(mobileNav).within(() => {
cy.get(selector).should('be.visible');
cy.get(selector).should('have.text', name);
});
});
}
after(() => {
cy.get('[data-testid="button-menu-drawer"]').click();
cy.viewport(
Cypress.config('viewportWidth'),
Cypress.config('viewportHeight')
);
});
});
describe('Elements', () => {
for (const { name, selector, header, tests } of topLevelLinks) {
describe(`${name} page`, () => {
it(`navigates to ${name}`, () => {
cy.get(navbar).within(() => {
cy.log(`goes to ${name}`);
cy.get(selector).click();
cy.log(`ensures ${name} is highlighted`);
cy.get(selector).should('have.attr', 'aria-current');
});
});
it('displays header', () => {
ensureHeader(header || name);
});
if (tests) tests.apply(this);
});
}
for (const { name, selector, header, tests } of secondLevelLinks.filter(
(l) => !l.trigger
)) {
describe(`${name} page`, () => {
it(`navigates to ${name}`, () => {
cy.get(navbar).within(() => {
expand();
cy.log(`goes to ${name}`);
cy.get(selector).click();
expand();
cy.log(`ensures ${name} is highlighted`);
cy.get(selector).should('have.attr', 'aria-current');
});
});
it('displays header', () => {
ensureHeader(header || name);
});
if (tests) tests.apply(this);
});
}
after(() => {
collapse();
});
});
}
);
@@ -0,0 +1,77 @@
import {
navigateTo,
navigation,
verifyPageHeader,
verifyTabHighlighted,
waitForSpinner,
} from '../../support/common.functions';
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
const newProposalButton = '[data-testid="new-proposal-link"]';
const newProposalLink = '[data-testid="new-proposal-link"]';
const governanceDocsUrl = 'https://vega.xyz/governance';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
context(
'Governance Page - verify elements on page',
{ tags: '@smoke' },
function () {
before('navigate to governance page', function () {
cy.visit('/');
navigateTo(navigation.proposals);
});
describe('with no network change proposals', function () {
it('should have governance tab highlighted', function () {
verifyTabHighlighted(navigation.proposals);
});
it('should have GOVERNANCE header visible', function () {
verifyPageHeader('Proposals');
});
it('should be able to see a working link for - find out more about Vega governance', function () {
// 3001-VOTE-001
cy.get(proposalDocumentationLink)
.should('be.visible')
.and('have.text', 'Find out more about Vega governance')
.and('have.attr', 'href')
.and('equal', governanceDocsUrl);
// 3002-PROP-001
cy.request(governanceDocsUrl)
.its('body')
.then((body) => {
if (!body.includes('Govern the network')) {
assert.include(
body,
'Govern the network',
`Checking that governance link destination includes 'Govern the network' text`
);
}
});
});
it('should be able to see button for - new proposal', function () {
// 3001-VOTE-002
cy.get(newProposalLink)
.should('be.visible')
.and('have.text', 'New proposal')
.and('have.attr', 'href')
.and('equal', '/proposals/propose');
});
// Skipping this test for now, the new proposal button no longer takes a user directly
// to a proposal form, instead it takes them to a page where they can select a proposal type.
// Keeping this test here for now as it can be repurposed to test the new proposal forms.
it.skip('should be able to see a connect wallet button - if vega wallet disconnected and new proposal button selected', function () {
cy.get(newProposalButton).should('be.visible').click();
cy.get(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
navigateTo(navigation.proposals);
waitForSpinner();
});
});
}
);
@@ -1,43 +1,50 @@
/// <reference types="cypress" />
import {
navigateTo,
navigation,
waitForSpinner,
} from '../../support/common.functions';
import {
enterUniqueFreeFormProposalBody,
goToMakeNewProposal,
} from '../../support/governance.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey2');
const vegaPubkeyTruncated = Cypress.env('vegaWalletPublicKey2Short');
const banner = 'view-banner';
context('View functionality with public key', { tags: '@smoke' }, function () {
before('send asset to wallet', function () {
cy.vega_wallet_faucet_assets_without_check(
'fUSDC',
'1000000',
vegaWalletPubKey
);
vegaWalletFaucetAssetsWithoutCheck('fUSDC', '1000000', vegaWalletPubKey);
});
beforeEach('visit home page', function () {
cy.visit('/');
cy.wait_for_spinner();
waitForSpinner();
cy.connectPublicKey(vegaWalletPubKey);
});
it('Able to connect public key via wallet', function () {
verifyConnectedToPubKey();
cy.getByTestId('currency-title', Cypress.env('epochTimeout')).should(
'contain.text',
'USDC (fake)'
);
});
it('Able to connect public key using url', function () {
cy.getByTestId('exit-view').click();
cy.visit(`/?address=${vegaWalletPubKey}`);
verifyConnectedToPubKey();
});
it('Able to connect public key via wallet and view assets in wallet', function () {
verifyConnectedToPubKey();
cy.getByTestId('currency-title', { timeout: 10000 })
.should('have.length.at.least', 4)
.and('contain.text', 'USDC (fake)');
});
it('Unable to submit proposal with public key', function () {
const expectedErrorTxt = `You are connected in a view only state for public key: ${vegaWalletPubKey}. In order to send transactions you must connect to a real wallet.`;
cy.navigate_to('proposals');
cy.go_to_make_new_proposal('Freeform');
cy.enter_unique_freeform_proposal_body('50', 'pub key proposal test');
navigateTo(navigation.proposals);
goToMakeNewProposal('Freeform');
enterUniqueFreeFormProposalBody('50', 'pub key proposal test');
cy.getByTestId('dialog-content').within(() => {
cy.get('h1').should('have.text', 'Transaction failed');
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
@@ -0,0 +1,42 @@
import {
navigateTo,
navigation,
verifyPageHeader,
} from '../../support/common.functions';
const viewToggle = '[data-testid="epoch-reward-view-toggle-total"]';
const warning = '[data-testid="callout"]';
context(
'Rewards Page - verify elements on page',
{ tags: '@regression' },
function () {
before('navigate to rewards page', function () {
cy.visit('/');
navigateTo(navigation.rewards);
});
describe('with wallets disconnected', function () {
it('should have REWARDS tab highlighted', function () {
verifyPageHeader('Rewards and fees');
});
it('should have rewards header visible', function () {
verifyPageHeader('Rewards and fees');
});
it('should have epoch warning', function () {
cy.get(warning)
.should('be.visible')
.and(
'have.text',
'Rewards are credited less than a minute after the epoch ends.This delay is set by a network parameter'
);
});
it('should have toggle for seeing total vs individual rewards', function () {
cy.get(viewToggle).should('be.visible');
});
});
}
);
@@ -1,3 +1,5 @@
import { navigateTo, navigation } from '../../support/common.functions';
const tokenDetailsTable = '.token-details';
const address = '[data-testid="token-address"]';
const contract = '[data-testid="token-contract"]';
@@ -17,7 +19,8 @@ const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
context('Verify elements on Token page', { tags: '@smoke' }, function () {
before('Visit token page', function () {
cy.visit('/token');
cy.visit('/');
navigateTo(navigation.token);
});
describe('THE $VEGA TOKEN table', function () {
it('should have TOKEN ADDRESS', function () {
@@ -1,4 +1,5 @@
import { trancheData } from '../../fixtures/mocks/tranches';
import { navigateTo, navigation } from '../../support/common.functions';
const tranches = trancheData;
@@ -8,7 +9,13 @@ context(
function () {
before('visit homepage', function () {
cy.intercept('GET', '**/tranches/stats', { tranches });
cy.visit('/token/tranches');
cy.visit('/');
});
it('Able to navigate to tranches page', function () {
navigateTo(navigation.supply);
cy.url().should('include', '/token/tranches');
cy.get('h1').should('contain.text', 'Vesting tranches');
});
// 1005-VEST-001
@@ -62,7 +69,7 @@ context(
});
it('Able to view tranches with less than 10 vega', function () {
cy.navigate_to('supply');
navigateTo(navigation.supply);
cy.getByTestId('show-all-tranches').click();
cy.getByTestId('tranche-item')
.should('have.length', 8)
@@ -1,210 +0,0 @@
/// <reference types="cypress" />
const validatorTitle = '[data-testid="validator-node-title"]';
const validatorId = '[data-testid="validator-id"]';
const validatorPubKey = '[data-testid="validator-public-key"]';
const ethAddressLink = '[data-testid="link"]';
const validatorStatus = '[data-testid="validator-status"]';
const totalStake = '[data-testid="total-stake"]';
const pendingStake = '[data-testid="pending-stake"]';
const stakedByOperator = '[data-testid="staked-by-operator"]';
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
const stakeShare = '[data-testid="stake-percentage"]';
const stakedByOperatorToolTip = "[data-testid='staked-operator-tooltip']";
const stakedByDelegatesToolTip = "[data-testid='staked-delegates-tooltip']";
const totalStakedToolTip = "[data-testid='total-staked-tooltip']";
const unnormalisedVotingPowerToolTip =
"[data-testid='unnormalised-voting-power-tooltip']";
const normalisedVotingPowerToolTip =
"[data-testid='normalised-voting-power-tooltip']";
const performancePenaltyToolTip = "[data-testid='performance-penalty-tooltip']";
const overstakedPenaltyToolTip = "[data-testid='overstaked-penalty-tooltip']";
const totalPenaltyToolTip = "[data-testid='total-penalty-tooltip']";
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d*\.?\d*$/;
context('Staking Page - verify elements on page', function () {
before('navigate to staking page', function () {
cy.visit('/validators');
});
describe(
'Should be able to see validator list from the staking page',
{ tags: '@regression' },
function () {
// 2001-STKE-050
it('Should be able to see validator names', function () {
cy.get('[col-id="validator"] > div > span')
.should('have.length.at.least', 1)
.each(($name) => {
cy.wrap($name).should('not.be.empty');
});
});
it('Should be able to see validator stake', function () {
cy.get('[col-id="stake"] > div > span > span')
.should('have.length.at.least', 1)
.each(($stake) => {
cy.wrap($stake).should('not.be.empty');
});
});
it('Should be able to see validator stake tooltip', function () {
cy.get('[col-id="stake"] > div > span > span').first().realHover();
cy.get(stakedByOperatorToolTip)
.invoke('text')
.should('contain', 'Staked by operator: 0.00');
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip)
.invoke('text')
.should('contain', 'Total stake: 0.00');
});
it('Should be able to see validator normalised voting power', function () {
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
.should('have.length.at.least', 1)
.each(($vPower) => {
cy.wrap($vPower).should('not.be.empty');
});
});
it('Should be able to see validator voting power tooltip', function () {
cy.get('[col-id="normalisedVotingPower"] > div > span > span')
.first()
.realHover();
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Unnormalised voting power: 0.00%');
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Normalised voting power: 0.10%');
});
// 2002-SINC-018
it('Should be able to see validator total penalties', function () {
cy.get('[col-id="totalPenalties"] > div > span > span')
.should('have.length.at.least', 1)
.each(($penalties) => {
cy.wrap($penalties).should('contain.text', '0%');
});
});
it('Should be able to see validator penalties tooltip', function () {
cy.get('[col-id="totalPenalties"] > div > span > span').realHover();
cy.get(performancePenaltyToolTip)
.invoke('text')
.should('contain', 'Performance penalty: 100.00%');
cy.get(overstakedPenaltyToolTip)
.invoke('text')
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 0.00%');
});
it('Should be able to see validator pending stake', function () {
cy.get('[col-id="pendingStake"] > div > span')
.should('have.length.at.least', 1)
.each(($pendingStake) => {
cy.wrap($pendingStake).should('contain.text', '0.00');
});
});
}
);
// 2001-STKE-050
describe(
'Should be able to see static information about a validator',
{ tags: '@smoke' },
function () {
before('connect wallets and click on validator', function () {
cy.connectVegaWallet();
cy.click_on_validator_from_list(0);
});
// 2001-STKE-006
it('Should be able to see validator name', function () {
cy.get(validatorTitle).should('not.be.empty');
});
// 2001-STKE-007
it('Should be able to see validator id', function () {
cy.get(validatorId).should('not.be.empty');
});
// 2001-STKE-008
it('Should be able to see validator public key', function () {
cy.get(validatorPubKey).should('not.be.empty');
});
// 2001-STKE-010
it('Should be able to see Ethereum address', function () {
cy.get(ethAddressLink).should('not.be.empty').and('have.attr', 'href');
});
// TODO validators missing url for more information about them 2001-STKE-09
it('Should be able to see validator status', function () {
cy.get(validatorStatus).should('have.text', 'Consensus');
});
// 2001-STKE-012
it('Should be able to see total stake', function () {
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see pending stake', function () {
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see staked by operator', function () {
cy.get(stakedByOperator)
.invoke('text')
.should('match', stakeNumberRegex);
});
it('Should be able to see staked by delegates', function () {
cy.get(stakedByDelegates)
.invoke('text')
.should('match', stakeNumberRegex);
});
// 2001-STKE-051
it('Should be able to see stake share in percentage', function () {
cy.get(stakeShare)
.invoke('text')
.then(($stakePercentage) => {
// The pattern must start at a word boundary (\b).
// The pattern cannot be immediately preceded by a dot ((?<!\.)).
// The pattern can be one of the following:
// A percentage value of zero (0%), or
// A non-zero percentage value that can be:
// A single digit (\d) between 0 and 9, or
// A two-digit number between 0 and 99 (\d{1,2}), or
// The number 100.
// The pattern can optionally include a decimal point and one or more digits after the decimal point ((?:(?<!100)\.\d+)?). However, if the number is 100, it cannot have a decimal point.
// The pattern must end with a percentage sign (%).
cy.wrap($stakePercentage).should(
'match',
/\b(?<!\.)(?:0+(?:\.0+)?%|(?:\d|\d{1,2}|100)(?:(?<!100)\.\d+)?)%/
);
});
});
// 2001-STKE-011 2002-SINC-001 2002-SINC-002
it('Should be able to see epoch information', function () {
const epochTitle = 'h3';
const nextEpochInfo = 'p';
cy.get(epochCountDown).within(() => {
cy.get(epochTitle).should('not.be.empty');
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
});
});
}
);
});
@@ -0,0 +1,244 @@
/// <reference types="cypress" />
import {
navigateTo,
navigation,
verifyPageHeader,
verifyTabHighlighted,
} from '../../support/common.functions';
import { clickOnValidatorFromList } from '../../support/staking.functions';
const guideLink = '[data-testid="staking-guide-link"]';
const validatorTitle = '[data-testid="validator-node-title"]';
const validatorId = '[data-testid="validator-id"]';
const validatorPubKey = '[data-testid="validator-public-key"]';
const ethAddressLink = '[data-testid="link"]';
const validatorStatus = '[data-testid="validator-status"]';
const totalStake = '[data-testid="total-stake"]';
const pendingStake = '[data-testid="pending-stake"]';
const stakedByOperator = '[data-testid="staked-by-operator"]';
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
const stakeShare = '[data-testid="stake-percentage"]';
const stakedByOperatorToolTip = '[data-testid="staked-operator-tooltip"]';
const stakedByDelegatesToolTip = '[data-testid="staked-delegates-tooltip"]';
const totalStakedToolTip = '[data-testid="total-staked-tooltip"]';
const unnormalisedVotingPowerToolTip =
'[data-testid="unnormalised-voting-power-tooltip"]';
const normalisedVotingPowerToolTip =
'[data-testid="normalised-voting-power-tooltip"]';
const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const totalPenaltyToolTip = '[data-testid="total-penalty-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d*\.?\d*$/;
context('Validators Page - verify elements on page', function () {
before('navigate to validators page', function () {
cy.visit('/');
navigateTo(navigation.validators);
});
describe('with wallets disconnected', { tags: '@smoke' }, function () {
describe('description section', function () {
it('Should have validators tab highlighted', function () {
verifyTabHighlighted(navigation.validators);
});
it('Should have validators ON VEGA header visible', function () {
verifyPageHeader('Validators');
});
it('Should have Staking Guide link visible', function () {
// 2001-STKE-003
cy.get(guideLink)
.should('be.visible')
.and('have.text', 'Read more about staking on Vega')
.and(
'have.attr',
'href',
'https://docs.vega.xyz/mainnet/concepts/vega-chain/#staking-on-vega'
);
});
});
describe(
'Should be able to see validator list from the staking page',
{ tags: '@regression' },
function () {
// 2001-STKE-050
it('Should be able to see validator names', function () {
cy.get('[col-id="validator"] > div > span')
.should('have.length.at.least', 1)
.each(($name) => {
cy.wrap($name).should('not.be.empty');
});
});
it('Should be able to see validator stake', function () {
cy.getByTestId('total-stake')
.should('have.length.at.least', 1)
.each(($stake) => {
cy.wrap($stake).should('not.be.empty');
});
});
it('Should be able to see validator stake tooltip', function () {
cy.getByTestId('total-stake').first().realHover();
cy.get(stakedByOperatorToolTip)
.invoke('text')
.should('contain', 'Staked by operator: 0.00');
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.get(totalStakedToolTip)
.invoke('text')
.should('contain', 'Total stake: 0.00');
});
it('Should be able to see validator normalised voting power', function () {
cy.getByTestId('normalised-voting-power')
.should('have.length.at.least', 1)
.each(($vPower) => {
cy.wrap($vPower).should('not.be.empty');
});
});
it('Should be able to see validator normalised voting power tooltip', function () {
cy.getByTestId('normalised-voting-power').first().realHover();
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Unnormalised voting power: 0.00%');
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Normalised voting power: 0.10%');
});
// 2002-SINC-018
it('Should be able to see validator total penalties', function () {
cy.getByTestId('total-penalty')
.should('have.length.at.least', 1)
.each(($penalties) => {
cy.wrap($penalties).should('contain.text', '0%');
});
});
it('Should be able to see validator penalties tooltip', function () {
cy.getByTestId('total-penalty').realHover();
cy.get(performancePenaltyToolTip)
.invoke('text')
.should('contain', 'Performance penalty: 100.00%');
cy.get(overstakedPenaltyToolTip)
.invoke('text')
.should('contain', 'Overstaked penalty:'); // value not asserted due to #2886
cy.get(totalPenaltyToolTip)
.invoke('text')
.should('contain', 'Total penalties: 0.00%');
});
it('Should be able to see validator pending stake', function () {
cy.getByTestId('total-pending-stake')
.should('have.length.at.least', 1)
.each(($pendingStake) => {
cy.wrap($pendingStake).should('contain.text', '0.00');
});
});
}
);
// 2001-STKE-050
describe(
'Should be able to see static information about a validator',
{ tags: '@smoke' },
function () {
before('connect wallets and click on validator', function () {
cy.connectVegaWallet();
clickOnValidatorFromList(0);
});
// 2001-STKE-006
it('Should be able to see validator name', function () {
cy.get(validatorTitle).should('not.be.empty');
});
// 2001-STKE-007
it('Should be able to see validator id', function () {
cy.get(validatorId).should('not.be.empty');
});
// 2001-STKE-008
it('Should be able to see validator public key', function () {
cy.get(validatorPubKey).should('not.be.empty');
});
// 2001-STKE-010
it('Should be able to see Ethereum address', function () {
cy.get(ethAddressLink)
.should('not.be.empty')
.and('have.attr', 'href');
});
// TODO validators missing url for more information about them 2001-STKE-09
it('Should be able to see validator status', function () {
cy.get(validatorStatus).should('have.text', 'Consensus');
});
// 2001-STKE-012
it('Should be able to see total stake', function () {
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see pending stake', function () {
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see staked by operator', function () {
cy.get(stakedByOperator)
.invoke('text')
.should('match', stakeNumberRegex);
});
it('Should be able to see staked by delegates', function () {
cy.get(stakedByDelegates)
.invoke('text')
.should('match', stakeNumberRegex);
});
// 2001-STKE-051
it('Should be able to see stake share in percentage', function () {
cy.get(stakeShare)
.invoke('text')
.then(($stakePercentage) => {
// The pattern must start at a word boundary (\b).
// The pattern cannot be immediately preceded by a dot ((?<!\.)).
// The pattern can be one of the following:
// A percentage value of zero (0%), or
// A non-zero percentage value that can be:
// A single digit (\d) between 0 and 9, or
// A two-digit number between 0 and 99 (\d{1,2}), or
// The number 100.
// The pattern can optionally include a decimal point and one or more digits after the decimal point ((?:(?<!100)\.\d+)?). However, if the number is 100, it cannot have a decimal point.
// The pattern must end with a percentage sign (%).
cy.wrap($stakePercentage).should(
'match',
/\b(?<!\.)(?:0+(?:\.0+)?%|(?:\d|\d{1,2}|100)(?:(?<!100)\.\d+)?)%/
);
});
});
// 2001-STKE-011 2002-SINC-001 2002-SINC-002
it('Should be able to see epoch information', function () {
const epochTitle = 'h3';
const nextEpochInfo = 'p';
cy.get(epochCountDown).within(() => {
cy.get(epochTitle).should('not.be.empty');
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
});
});
}
);
});
});
@@ -1,3 +1,12 @@
import {
navigateTo,
navigation,
verifyPageHeader,
verifyTabHighlighted,
} from '../../support/common.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
const connectButton = '[data-testid="connect-to-eth-btn"]';
const lockedTokensInVestingContract = '6,499,972.30';
context(
@@ -5,12 +14,30 @@ context(
{ tags: '@smoke' },
function () {
before('navigate to vesting page', function () {
cy.visit('/token/redeem');
cy.visit('/');
navigateTo(navigation.vesting);
});
describe('with wallets disconnected', function () {
it('should have vesting tab highlighted', function () {
verifyTabHighlighted(navigation.vesting);
});
it('should have VESTING header visible', function () {
verifyPageHeader('Vesting');
});
// 1005-VEST-018
it('should have connect Eth wallet button', function () {
cy.get(connectButton)
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet');
});
});
describe('With Eth wallet connected', function () {
before('connect eth wallet', function () {
cy.ethereum_wallet_connect();
ethereumWalletConnect();
cy.getByTestId('view-connected-eth-btn').click();
});
@@ -105,7 +132,6 @@ context(
'All the tokens in this tranche are locked and can not be redeemed yet.'
);
});
cy.connectVegaWallet();
});
});
}
@@ -1,3 +1,6 @@
import { convertTokenValueToNumber } from '../../support/common.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
const walletContainer = 'aside [data-testid="ethereum-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
const connectToEthButton =
@@ -87,7 +90,7 @@ context(
// 0004-EWAL-001 0004-EWAL-002
describe('when Ethereum wallet connected', function () {
before('connect to Ethereum wallet', function () {
cy.ethereum_wallet_connect();
ethereumWalletConnect();
});
it('should have ETHEREUM KEY header visible', function () {
@@ -178,22 +181,25 @@ context(
.within(() => {
cy.get(currencyValue)
.invoke('text')
.convert_token_value_to_number()
.as('value');
.then((currencyValueTxt) => {
convertTokenValueToNumber(currencyValueTxt).as('value');
});
cy.get(currencyLocked)
.invoke('text')
.convert_token_value_to_number()
.as('locked');
.then((currencyLockedTxt) => {
convertTokenValueToNumber(currencyLockedTxt).as('locked');
});
cy.get(currencyUnlocked)
.invoke('text')
.convert_token_value_to_number()
.as('unlocked');
.then((currencyUnlockedTxt) => {
convertTokenValueToNumber(currencyUnlockedTxt).as('unlocked');
});
})
.then(function () {
expect(parseFloat(this.value).toFixed(1)).to.equal(
parseFloat(
Math.round((this.locked + this.unlocked) * 100) / 100
).toFixed(1)
(Math.round((this.locked + this.unlocked) * 100) / 100).toFixed(
1
)
);
});
});
@@ -247,16 +253,19 @@ context(
.within(() => {
cy.get(currencyValue)
.invoke('text')
.convert_token_value_to_number()
.as('value');
.then((currencyValueTxt) => {
convertTokenValueToNumber(currencyValueTxt).as('value');
});
cy.get(currencyLocked)
.invoke('text')
.convert_token_value_to_number()
.as('locked');
.then((currencyLockedTxt) => {
convertTokenValueToNumber(currencyLockedTxt).as('locked');
});
cy.get(currencyUnlocked)
.invoke('text')
.convert_token_value_to_number()
.as('unlocked');
.then((currencyUnlockedTxt) => {
convertTokenValueToNumber(currencyUnlockedTxt).as('unlocked');
});
})
.then(function () {
expect(this.value).to.equal(this.locked + this.unlocked);
@@ -1,4 +1,7 @@
import { truncateByChars } from '@vegaprotocol/utils';
import { waitForSpinner } from '../../support/common.functions';
import { vegaWalletTeardown } from '../../support/wallet-teardown.functions';
import { vegaWalletFaucetAssetsWithoutCheck } from '../../support/wallet-vega.functions';
const walletContainer = 'aside [data-testid="vega-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
@@ -129,7 +132,7 @@ context(
before('connect vega wallet', function () {
cy.visit('/');
cy.connectVegaWallet();
cy.vega_wallet_teardown();
vegaWalletTeardown();
});
// 0002-WCON-007
@@ -158,7 +161,7 @@ context(
cy.get(walletContainer).within(() => {
cy.get(currencyTitle)
.should('be.visible')
.and('have.text', `VEGAAssociated`);
.and('contain.text', `VEGAAssociated`);
});
});
@@ -167,14 +170,19 @@ context(
{ tags: '@smoke' },
function () {
cy.get(walletContainer).within(() => {
cy.get(currencyValue).should('be.visible').and('have.text', `0.00`);
cy.get(currencyValue)
.should('be.visible')
.and('contain.text', `0.00`);
});
}
);
it('should have Unstaked value visible', { tags: '@smoke' }, function () {
cy.get(walletContainer).within(() => {
cy.get(vegaUnstaked).should('be.visible').and('have.text', `0.00`);
cy.get(vegaUnstaked)
.should('be.visible')
.invoke('text')
.and('not.be.empty');
});
});
@@ -300,16 +308,17 @@ context(
before('faucet assets to connected vega wallet', function () {
for (const { id, amount } of assets) {
cy.vega_wallet_faucet_assets_without_check(
id,
amount,
vegaWalletPublicKey
);
vegaWalletFaucetAssetsWithoutCheck(id, amount, vegaWalletPublicKey);
}
cy.reload();
cy.wait_for_spinner();
waitForSpinner();
cy.connectVegaWallet();
cy.ethereum_wallet_connect();
cy.get(walletContainer).within(() => {
cy.getByTestId('currency-title', txTimeout).should(
'have.length.at.least',
5
);
});
});
for (const { id, name, expectedAmount } of assets) {
@@ -323,8 +332,9 @@ context(
.contains(id)
.parent()
.siblings()
.within((el) => {
const value = parseFloat(el.text());
.invoke('text')
.then((el) => {
const value = parseFloat(el);
cy.wrap(value).should('be.gte', parseFloat(expectedAmount));
});
@@ -0,0 +1,35 @@
import {
navigateTo,
navigation,
verifyPageHeader,
verifyTabHighlighted,
} from '../../support/common.functions';
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
context(
'Withdraw Page - verify elements on page',
{ tags: '@smoke' },
function () {
before('navigate to withdrawals page', function () {
cy.visit('/');
navigateTo(navigation.withdraw);
});
describe('with wallets disconnected', function () {
it('should have withdraw tab highlighted', function () {
verifyTabHighlighted(navigation.withdraw);
});
it('should have WITHDRAW header visible', function () {
verifyPageHeader('Withdrawals');
});
it('should have connect Vega wallet button', function () {
cy.get(connectToVegaBtn)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});
});
}
);
@@ -0,0 +1,128 @@
export interface testFreeformProposal {
rationale: {
title: string;
description: string;
};
terms: {
closingTimestamp: number;
};
}
export interface networkParameters {
'blockchains.ethereumConfig': string;
'governance.proposal.asset.maxClose': string;
'governance.proposal.asset.maxEnact': string;
'governance.proposal.asset.minClose': string;
'governance.proposal.asset.minEnact': string;
'governance.proposal.asset.minProposerBalance': string;
'governance.proposal.asset.minVoterBalance': string;
'governance.proposal.asset.requiredMajority': string;
'governance.proposal.asset.requiredParticipation': string;
'governance.proposal.freeform.maxClose': string;
'governance.proposal.freeform.minClose': string;
'governance.proposal.freeform.minProposerBalance': string;
'governance.proposal.freeform.minVoterBalance': string;
'governance.proposal.freeform.requiredMajority': string;
'governance.proposal.freeform.requiredParticipation': string;
'governance.proposal.market.maxClose': string;
'governance.proposal.market.maxEnact': string;
'governance.proposal.market.minClose': string;
'governance.proposal.market.minEnact': string;
'governance.proposal.market.minProposerBalance': string;
'governance.proposal.market.minVoterBalance': string;
'governance.proposal.market.requiredMajority': string;
'governance.proposal.market.requiredParticipation': string;
'governance.proposal.updateAsset.maxClose': string;
'governance.proposal.updateAsset.maxEnact': string;
'governance.proposal.updateAsset.minClose': string;
'governance.proposal.updateAsset.minEnact': string;
'governance.proposal.updateAsset.minProposerBalance': string;
'governance.proposal.updateAsset.minVoterBalance': string;
'governance.proposal.updateAsset.requiredMajority': string;
'governance.proposal.updateAsset.requiredParticipation': string;
'governance.proposal.updateMarket.maxClose': string;
'governance.proposal.updateMarket.maxEnact': string;
'governance.proposal.updateMarket.minClose': string;
'governance.proposal.updateMarket.minEnact': string;
'governance.proposal.updateMarket.minProposerBalance': string;
'governance.proposal.updateMarket.minProposerEquityLikeShare': string;
'governance.proposal.updateMarket.minVoterBalance': string;
'governance.proposal.updateMarket.requiredMajority': string;
'governance.proposal.updateMarket.requiredMajorityLP': string;
'governance.proposal.updateMarket.requiredParticipation': string;
'governance.proposal.updateMarket.requiredParticipationLP': string;
'governance.proposal.updateNetParam.maxClose': string;
'governance.proposal.updateNetParam.maxEnact': string;
'governance.proposal.updateNetParam.minClose': string;
'governance.proposal.updateNetParam.minEnact': string;
'governance.proposal.updateNetParam.minProposerBalance': string;
'governance.proposal.updateNetParam.minVoterBalance': string;
'governance.proposal.updateNetParam.requiredMajority': string;
'governance.proposal.updateNetParam.requiredParticipation': string;
'limits.assets.proposeEnabledFrom': string;
'limits.markets.maxPeggedOrders': string;
'limits.markets.proposeEnabledFrom': string;
'market.auction.maximumDuration': string;
'market.auction.minimumDuration': string;
'market.fee.factors.infrastructureFee': string;
'market.fee.factors.makerFee': string;
'market.liquidity.bondPenaltyParameter': string;
'market.liquidity.maximumLiquidityFeeFactorLevel': string;
'market.liquidity.minimum.probabilityOfTrading.lpOrders': string;
'market.liquidity.probabilityOfTrading.tau.scaling': string;
'market.liquidity.providers.fee.distributionTimeStep': string;
'market.liquidity.stakeToCcyVolume': string;
'market.liquidity.targetstake.triggering.ratio': string;
'market.liquidityProvision.minLpStakeQuantumMultiple': string;
'market.liquidityProvision.shapes.maxSize': string;
'market.margin.scalingFactors': string;
'market.monitor.price.defaultParameters': string;
'market.stake.target.scalingFactor': string;
'market.stake.target.timeWindow': string;
'market.value.windowLength': string;
'network.checkpoint.timeElapsedBetweenCheckpoints': string;
'network.floatingPointUpdates.delay': string;
'network.markPriceUpdateMaximumFrequency': string;
'network.transaction.defaultgas': string;
'network.transactions.maxgasperblock': string;
'network.transactions.minBlockCapacity': string;
'network.validators.ersatz.multipleOfTendermintValidators': string;
'network.validators.ersatz.rewardFactor': string;
'network.validators.incumbentBonus': string;
'network.validators.minimumEthereumEventsForNewValidator': string;
'network.validators.multisig.numberOfSigners': string;
'network.validators.tendermint.number': string;
'reward.asset': string;
'reward.staking.delegation.competitionLevel': string;
'reward.staking.delegation.delegatorShare': string;
'reward.staking.delegation.maxPayoutPerEpoch': string;
'reward.staking.delegation.maxPayoutPerParticipant': string;
'reward.staking.delegation.minValidators': string;
'reward.staking.delegation.minimumValidatorStake': string;
'reward.staking.delegation.optimalStakeMultiplier': string;
'reward.staking.delegation.payoutDelay': string;
'reward.staking.delegation.payoutFraction': string;
'rewards.marketCreationQuantumMultiple': string;
'snapshot.interval.length': string;
'spam.pow.difficulty': string;
'spam.pow.hashFunction': string;
'spam.pow.increaseDifficulty': string;
'spam.pow.numberOfPastBlocks': string;
'spam.pow.numberOfTxPerBlock': string;
'spam.protection.delegation.min.tokens': string;
'spam.protection.max.batchSize': string;
'spam.protection.max.delegations': string;
'spam.protection.max.proposals': string;
'spam.protection.max.votes': string;
'spam.protection.maxUserTransfersPerEpoch': string;
'spam.protection.minMultisigUpdates': string;
'spam.protection.minimumWithdrawalQuantumMultiple': string;
'spam.protection.proposal.min.tokens': string;
'spam.protection.voting.min.tokens': string;
'transfer.fee.factor': string;
'transfer.minTransferQuantumMultiple': string;
'validator.performance.scaling.factor': string;
'validators.delegation.minAmount': string;
'validators.epoch.length': string;
'validators.vote.required': string;
}
@@ -1,87 +0,0 @@
const epochTimeout = Cypress.env('epochTimeout');
const txTimeout = Cypress.env('txTimeout');
Cypress.Commands.add(
'convert_token_value_to_number',
{ prevSubject: true },
(subject) => {
return parseFloat(subject.replace(/,/g, ''));
}
);
const navigation = {
section: '[data-testid="navigation"]',
vesting: '[href="/token/redeem"]',
validators: '[href="/validators"]',
rewards: '[href="/rewards"]',
withdraw: '[href="/token/withdraw"]',
proposals: '[href="/proposals"]',
pageSpinner: '[data-testid="splash-loader"]',
supply: '[href="/token/tranches"]',
token: '[href="/token"]',
};
const topLevelRoutes = ['proposals', 'validators', 'rewards'];
const tokenDropDown = 'state-trigger';
Cypress.Commands.add('navigate_to', (page) => {
if (!topLevelRoutes.includes(page)) {
// FIXME: Timeout madness
cy.getByTestId(tokenDropDown, { timeout: 60000 }).eq(0).click();
cy.get('[data-testid="token-dropdown"]:visible').within(() => {
cy.get(navigation[page]).eq(0).click();
});
} else {
return cy.get(navigation.section, { timeout: 10000 }).within(() => {
cy.get(navigation[page]).eq(0).click();
});
}
});
Cypress.Commands.add('verify_tab_highlighted', (page) => {
return cy.get(navigation.section).within(() => {
if (!topLevelRoutes.includes(page)) {
cy.getByTestId(tokenDropDown, { timeout: 10000 }).eq(0).click();
cy.get('[data-testid="token-dropdown"]:visible').within(() => {
cy.get(navigation[page]).should('have.attr', 'aria-current');
});
} else {
cy.get(navigation[page]).should('have.attr', 'aria-current');
}
});
});
Cypress.Commands.add('verify_page_header', (text) => {
return cy.get('header h1').should('be.visible').and('have.text', text);
});
Cypress.Commands.add('wait_for_spinner', () => {
cy.get(navigation.pageSpinner, Cypress.env('epochTimeout')).should('exist');
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
});
// This is a workaround function to begin tests with associating tokens without failing
// Should be removed when eth transaction bug is fixed
export function associateTokenStartOfTests() {
cy.highlight(`Associating tokens for first time`);
cy.ethereum_wallet_connect();
cy.connectVegaWallet();
cy.get('[href="/token/associate"]:visible').first().click();
cy.getByTestId('associate-radio-wallet', { timeout: 30000 }).click();
cy.getByTestId('token-amount-input', epochTimeout).type('1');
cy.getByTestId('token-input-submit-button', txTimeout)
.should('be.enabled')
.click();
cy.contains(
`Associating with Vega key. Waiting for ${Cypress.env(
'blockConfirmations'
)} more confirmations..`,
txTimeout
).should('be.visible');
cy.getByTestId('associated-amount', txTimeout).should('contain.text', '1');
// Wait is needed to allow time for transaction to complete
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(10000);
cy.vega_wallet_teardown();
cy.clearLocalStorage();
}
@@ -0,0 +1,90 @@
const tokenDropDown = 'state-trigger';
const txTimeout = Cypress.env('txTimeout');
export enum navigation {
section = 'nav',
vesting = '[href="/token/redeem"]',
validators = '[href="/validators"]',
rewards = '[href="/rewards"]',
withdraw = '[href="/token/withdraw"]',
proposals = '[href="/proposals"]',
pageSpinner = '[data-testid="splash-loader"]',
supply = '[href="/token/tranches"]',
token = '[href="/token"]',
}
export function convertTokenValueToNumber(subject: string) {
return cy.wrap(parseFloat(subject.replace(/,/g, '')));
}
const topLevelRoutes = [
navigation.proposals,
navigation.validators,
navigation.rewards,
];
export function navigateTo(page: navigation) {
if (!topLevelRoutes.includes(page)) {
cy.getByTestId(tokenDropDown, { timeout: 10000 }).eq(0).click();
cy.getByTestId('token-dropdown').within(() => {
cy.get(page).eq(0).click();
});
} else {
return cy.get(navigation.section, { timeout: 10000 }).within(() => {
cy.get(page).eq(0).click();
});
}
}
export function verifyTabHighlighted(page: navigation) {
return cy.get(navigation.section).within(() => {
if (!topLevelRoutes.includes(page)) {
cy.getByTestId(tokenDropDown, { timeout: 10000 }).eq(0).click();
cy.get('[data-testid="token-dropdown"]:visible').within(() => {
cy.get(page).should('have.attr', 'aria-current');
});
} else {
cy.get(page).should('have.attr', 'aria-current');
}
});
}
export function verifyPageHeader(text: string) {
return cy.get('header h1').should('be.visible').and('have.text', text);
}
export function waitForSpinner() {
cy.get(navigation.pageSpinner, Cypress.env('epochTimeout')).should('exist');
cy.get(navigation.pageSpinner, { timeout: 20000 }).should('not.exist');
}
export function verifyUnstakedBalance(amount: number) {
cy.getByTestId('vega-wallet-balance-unstaked', txTimeout).should(
'contain',
amount,
txTimeout
);
}
export function verifyStakedBalance(amount: number) {
cy.getByTestId('vega-wallet-balance-staked-validators', txTimeout)
.should('contain', amount, txTimeout)
.and('contain', '…');
}
export function verifyEthWalletTotalAssociatedBalance(amount: string) {
cy.getByTestId('currency-locked', txTimeout)
.should('contain', amount)
.and('be.visible');
}
export function verifyEthWalletAssociatedBalance(amount: string) {
cy.getByTestId('eth-wallet-associated-balances', txTimeout)
.contains(Cypress.env('vegaWalletPublicKeyShort'), txTimeout)
.parent(txTimeout)
.should('contain', amount, txTimeout);
}
export function closeDialog() {
cy.getByTestId('dialog-close').click();
}
@@ -1,228 +0,0 @@
const newProposalButton = '[data-testid="new-proposal-link"]';
const proposalInformationTableRows = '[data-testid="key-value-table-row"]';
const proposalListItem = '[data-testid="proposals-list-item"]';
const newProposalTitle = '[data-testid="proposal-title"]';
const newProposalDescription = '[data-testid="proposal-description"]';
const proposalDetails = '[data-testid="proposal-details"]';
const rawProposalData = '[data-testid="proposal-data"]';
const voteButtons = '[data-testid="vote-buttons"]';
const dialogTitle = '[data-testid="dialog-title"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const dialogCloseButton = '[data-testid="dialog-close"]';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
Cypress.Commands.add(
'convert_unix_timestamp_to_governance_data_table_date_format',
(unixTimestamp, monthTextLength = 'longMonth') => {
let dateSupplied = new Date(unixTimestamp * 1000),
year = dateSupplied.getFullYear(),
months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
month = months[dateSupplied.getMonth()],
shortMonth = months[dateSupplied.getMonth()].substring(0, 3),
date = dateSupplied.getDate();
if (monthTextLength === 'longMonth') return `${date} ${month} ${year}`;
else return `${date} ${shortMonth} ${year}`;
}
);
Cypress.Commands.add(
'create_ten_digit_unix_timestamp_for_specified_days',
(durationDays) => {
let today = new Date();
let timestamp = today.setDate(today.getDate() + parseInt(durationDays));
timestamp = Math.floor(timestamp / 1000);
return timestamp;
}
);
Cypress.Commands.add('enter_raw_proposal_body', (timestamp) => {
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp = timestamp;
rawProposal.rationale.title += timestamp;
let proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal);
});
});
Cypress.Commands.add(
'enter_unique_freeform_proposal_body',
(timestamp, proposalTitle) => {
cy.get(newProposalTitle).type(proposalTitle);
cy.get(newProposalDescription).type(
'this is a e2e freeform proposal description'
);
cy.get(proposalVoteDeadline).clear().click().type(timestamp);
cy.getByTestId('proposal-submit').should('be.visible').click();
}
);
Cypress.Commands.add(
'get_submitted_proposal_from_proposal_list',
(proposalTitle) => {
cy.get_proposal_id_from_list(proposalTitle).then(() => {
cy.get('@proposalIdText').then((proposalId) => {
return cy.get(`#${proposalId}`);
});
});
}
);
Cypress.Commands.add('get_proposal_id_from_list', (proposalTitle) => {
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.within(() => {
cy.get(proposalDetails)
.invoke('text')
.then((proposalIdText) => {
let newProposalId;
if (proposalIdText.includes('Freeform proposal')) {
newProposalId = proposalIdText.replace('Freeform proposal: ', '');
}
cy.wrap(newProposalId).as('proposalIdText');
});
});
});
Cypress.Commands.add(
'get_governance_proposal_date_format_for_specified_days',
(days, shortOrLong) => {
cy.create_ten_digit_unix_timestamp_for_specified_days(days).then((date) => {
cy.convert_unix_timestamp_to_governance_data_table_date_format(
date,
shortOrLong
).then((convertedDate) => {
return convertedDate;
});
});
}
);
Cypress.Commands.add('get_proposal_information_from_table', (heading) => {
cy.get(proposalInformationTableRows).contains(heading).siblings();
});
Cypress.Commands.add('vote_for_proposal', (vote) => {
cy.contains('Vote breakdown').should('be.visible', { timeout: 10000 });
cy.get(voteButtons).contains(vote).click();
cy.get(dialogTitle).should('have.text', 'Transaction complete');
cy.get(dialogCloseButton).click();
});
Cypress.Commands.add('wait_for_proposal_sync', () => {
// This is a workaround function required because after posting a proposal
// and waiting for the ProposalEvent network call to respond there can still be a few seconds
// before proposal appears in the list - so rather than hard coded wait - we just wait on the
// delegation checks that are performed on the governance page.
cy.intercept('POST', '/query', (req) => {
if (req.body.operationName === 'Delegations') {
req.alias = 'proposalDelegationsCompletion';
}
});
// waiting for two network calls
cy.wait(['@proposalDelegationsCompletion', '@proposalDelegationsCompletion']);
// Turn off this intercept from here on in
cy.intercept('POST', '/query', (req) => {
if (req.body.operationName === 'Delegations') {
req.continue();
}
});
});
Cypress.Commands.add('navigate_to_page_if_not_already_loaded', (section) => {
cy.url().then((url) => {
if (url != `http://localhost:4210/${section}`) {
cy.navigate_to(section);
}
});
});
Cypress.Commands.add('get_sort_order_of_supplied_array', (suppliedArray) => {
const tempArray = [];
for (let index = 1; index < suppliedArray.length; index++) {
tempArray.push(
suppliedArray[index - 1].localeCompare(suppliedArray[index])
);
}
if (tempArray.every((n) => n <= 0)) return 'ascending';
else if (tempArray.every((n) => n >= 0)) return 'descending';
else return 'unsorted';
});
Cypress.Commands.add('go_to_make_new_proposal', (proposalType) => {
cy.navigate_to_page_if_not_already_loaded('proposals');
cy.get(newProposalButton).should('be.visible').click();
cy.url().should('include', '/proposals/propose');
cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click();
});
Cypress.Commands.add('wait_for_proposal_submitted', () => {
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal submitted', proposalTimeout).should('be.visible');
cy.get(dialogCloseButton).click();
});
export function createRawProposal(proposerBalance) {
if (proposerBalance)
cy.ensure_specified_unstaked_tokens_are_associated(proposerBalance);
cy.go_to_make_new_proposal('raw proposal');
cy.create_ten_digit_unix_timestamp_for_specified_days('8').then(
(closingDateTimestamp) => {
cy.enter_raw_proposal_body(closingDateTimestamp).as('rawProposal');
}
);
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.navigate_to('proposals');
}
export function generateFreeFormProposalTitle() {
const randomNum = Math.floor(Math.random() * 1000) + 1;
return randomNum + ': Freeform e2e proposal';
}
export function createFreeformProposal(proposalTitle) {
cy.go_to_make_new_proposal(governanceProposalType.FREEFORM);
cy.enter_unique_freeform_proposal_body('50', proposalTitle);
cy.wait_for_proposal_submitted();
cy.wait_for_proposal_sync();
cy.getByTestId('proposal-title').invoke('text').as('proposalTitle');
cy.navigate_to('proposals');
}
export const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
@@ -0,0 +1,214 @@
import { closeDialog, navigateTo, navigation } from './common.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
const newProposalButton = '[data-testid="new-proposal-link"]';
const proposalInformationTableRows = '[data-testid="key-value-table-row"]';
const proposalListItem = '[data-testid="proposals-list-item"]';
const newProposalTitle = '[data-testid="proposal-title"]';
const newProposalDescription = '[data-testid="proposal-description"]';
const proposalDetails = '[data-testid="proposal-details"]';
const rawProposalData = '[data-testid="proposal-data"]';
const voteButtons = '[data-testid="vote-buttons"]';
const dialogTitle = '[data-testid="dialog-title"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
export function convertUnixTimestampToDateformat(
unixTimestamp: number,
monthTextLength = 'longMonth'
) {
const dateSupplied = new Date(unixTimestamp * 1000);
const year = dateSupplied.getFullYear();
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
const month = months[dateSupplied.getMonth()];
const shortMonth = months[dateSupplied.getMonth()].substring(0, 3),
date = dateSupplied.getDate();
if (monthTextLength === 'longMonth') {
return cy.wrap(`${date} ${month} ${year}`);
} else return cy.wrap(`${date} ${shortMonth} ${year}`);
}
export function createTenDigitUnixTimeStampForSpecifiedDays(
durationDays: number
) {
const today = new Date();
let timestamp = today.setDate(today.getDate() + durationDays);
return (timestamp = Math.floor(timestamp / 1000));
}
export function enterRawProposalBody(timestamp: number) {
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp = timestamp;
rawProposal.rationale.title += timestamp;
const proposalPayload = JSON.stringify(rawProposal);
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.wrap(rawProposal).as('rawProposal');
});
}
export function enterUniqueFreeFormProposalBody(
timestamp: string,
proposalTitle: string
) {
cy.get(newProposalTitle).type(proposalTitle);
cy.get(newProposalDescription).type(
'this is a e2e freeform proposal description'
);
cy.get(proposalVoteDeadline).clear().click().type(timestamp);
cy.getByTestId('proposal-submit').should('be.visible').click();
}
export function getSubmittedProposalFromProposalList(proposalTitle: string) {
getProposalIdFromList(proposalTitle);
cy.get('@proposalIdText').then((proposalId) => {
cy.get(`#${proposalId}`).as('submittedProposal');
});
return cy.get('@submittedProposal');
}
export function getProposalIdFromList(proposalTitle: string) {
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.within(() => {
cy.get(proposalDetails)
.invoke('text')
.then((proposalIdText) => {
let newProposalId;
if (proposalIdText.includes('Freeform proposal')) {
newProposalId = proposalIdText.replace('Freeform proposal: ', '');
}
cy.wrap(newProposalId).as('proposalIdText');
});
});
}
export function getGovernanceProposalDateFormatForSpecifiedDays(
days: number,
shortOrLong?: string
) {
return convertUnixTimestampToDateformat(
createTenDigitUnixTimeStampForSpecifiedDays(days),
shortOrLong
);
}
export function getProposalInformationFromTable(heading: string) {
return cy.get(proposalInformationTableRows).contains(heading).siblings();
}
export function voteForProposal(vote: string) {
cy.contains('Vote breakdown').should('be.visible', { timeout: 10000 });
cy.get(voteButtons).contains(vote).click();
cy.get(dialogTitle, proposalTimeout).should(
'have.text',
'Transaction complete'
);
closeDialog();
}
export function waitForProposalSync() {
// This is a workaround function required because after posting a proposal
// and waiting for the ProposalEvent network call to respond there can still be a few seconds
// before proposal appears in the list - so rather than hard coded wait - we just wait on the
// delegation checks that are performed on the governance page.
cy.intercept('POST', '/query', (req) => {
if (req.body.operationName === 'Delegations') {
req.alias = 'proposalDelegationsCompletion';
}
});
// waiting for two network calls
cy.wait(['@proposalDelegationsCompletion', '@proposalDelegationsCompletion']);
// Turn off this intercept from here on in
cy.intercept('POST', '/query', (req) => {
if (req.body.operationName === 'Delegations') {
req.continue();
}
});
}
export function getSortOrderOfSuppliedArray(suppliedArray: string[]) {
const tempArray = [];
for (let index = 1; index < suppliedArray.length; index++) {
tempArray.push(
suppliedArray[index - 1].localeCompare(suppliedArray[index])
);
}
if (tempArray.every((n) => n <= 0)) return 'ascending';
else if (tempArray.every((n) => n >= 0)) return 'descending';
else return 'unsorted';
}
export function goToMakeNewProposal(proposalType: string) {
navigateTo(navigation.proposals);
cy.get(newProposalButton).should('be.visible').click();
cy.url().should('include', '/proposals/propose');
cy.get('li').should('contain.text', proposalType).and('be.visible');
cy.get('li').contains(proposalType).click();
}
export function waitForProposalSubmitted() {
cy.contains('Awaiting network confirmation', epochTimeout).should(
'be.visible'
);
cy.contains('Proposal submitted', proposalTimeout).should('be.visible');
closeDialog();
}
export function createRawProposal(proposerBalance?: string) {
if (proposerBalance) {
ensureSpecifiedUnstakedTokensAreAssociated(proposerBalance);
}
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
waitForProposalSubmitted();
waitForProposalSync();
navigateTo(navigation.proposals);
}
export function generateFreeFormProposalTitle() {
const randomNum = Math.floor(Math.random() * 1000) + 1;
return randomNum + ': Freeform e2e proposal';
}
export function createFreeformProposal(proposalTitle: string) {
goToMakeNewProposal(governanceProposalType.FREEFORM);
enterUniqueFreeFormProposalBody('50', proposalTitle);
waitForProposalSubmitted();
waitForProposalSync();
cy.getByTestId('proposal-title').invoke('text').as('proposalTitle');
navigateTo(navigation.proposals);
}
export const governanceProposalType = {
NETWORK_PARAMETER: 'Network parameter',
NEW_MARKET: 'New market',
UPDATE_MARKET: 'Update market',
NEW_ASSET: 'New asset',
FREEFORM: 'Freeform',
RAW: 'raw proposal',
};
+20
View File
@@ -0,0 +1,20 @@
/// <reference types="cypress" />
declare namespace Cypress {
// specify additional properties in the TestConfig object
// in our case we will add "tags" property
interface SuiteConfigOverrides {
/**
* List of tags for this test
* @example a single tag
* it('logs in', { tags: '@smoke' }, () => { ... })
* @example multiple tags
* it('works', { tags: ['@smoke', '@slow'] }, () => { ... })
*/
tags?: string | string[];
}
interface Cypress {
grep?: (grep?: string, tags?: string, burn?: string) => void;
}
}
+6 -6
View File
@@ -1,12 +1,12 @@
import '@vegaprotocol/cypress';
import 'cypress-real-events/support';
import './common.functions.js';
import './staking.functions.js';
import './governance.functions.js';
import './wallet-eth.functions.js';
import './wallet-teardown.functions.js';
import './wallet-vega.functions.js';
import './common.functions.ts';
import './staking.functions.ts';
import './governance.functions.ts';
import './wallet-eth.functions.ts';
import './wallet-teardown.functions.ts';
import './wallet-vega.functions.ts';
import './proposal.functions.ts';
import registerCypressGrep from '@cypress/grep';
import { aliasGQLQuery } from '@vegaprotocol/cypress';
@@ -1,3 +1,6 @@
import { closeDialog } from './common.functions';
import { vegaWalletTeardown } from './wallet-teardown.functions';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const tokenInputApprove = '[data-testid="token-input-approve-button"]';
@@ -12,53 +15,55 @@ const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const associateContractRadioButton = '[data-testid="associate-radio-contract"]';
const stakeMaximumTokens = '[data-testid="token-amount-use-maximum"]';
const stakeValidatorListPendingStake = '[col-id="pendingStake"]';
const stakeValidatorListTotalStake = '[col-id="stake"] > div > span';
const stakeValidatorListTotalShare = '[col-id="stakeShare"] > div > span';
const stakeValidatorListTotalStake = 'total-stake';
const stakeValidatorListTotalShare = 'total-stake-share';
const stakeValidatorListName = '[col-id="validator"]';
const vegaKeySelector = '#vega-key-selector';
const dialogCloseButton = '[data-testid="dialog-close"]';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
Cypress.Commands.add('wait_for_beginning_of_epoch', () => {
export function waitForBeginningOfEpoch() {
cy.contains('Waiting for next epoch to start', epochTimeout).should(
'not.exist'
);
cy.contains('Waiting for next epoch to start', epochTimeout).should(
'be.visible'
);
});
}
Cypress.Commands.add('staking_validator_page_add_stake', (stake) => {
export function stakingValidatorPageAddStake(stake: string) {
cy.highlight(`Adding a stake of ${stake}`);
cy.get(addStakeRadioButton, epochTimeout).click({ force: true });
cy.get(tokenAmountInputBox).type(stake);
cy.wait_for_beginning_of_epoch();
waitForBeginningOfEpoch();
cy.get(tokenSubmitButton, epochTimeout)
.should('be.enabled')
.and('contain', `Add ${stake} $VEGA tokens`)
.and('be.visible')
.click();
});
}
Cypress.Commands.add('staking_validator_page_remove_stake', (stake) => {
export function stakingValidatorPageRemoveStake(stake: string) {
cy.highlight(`Removing a stake of ${stake}`);
cy.get(removeStakeRadioButton, epochTimeout).click();
cy.get(tokenAmountInputBox).type(stake);
cy.wait_for_beginning_of_epoch();
waitForBeginningOfEpoch();
cy.get(tokenSubmitButton)
.should('be.enabled', epochTimeout)
.and('contain', `Remove ${stake} $VEGA tokens at the end of epoch`)
.and('be.visible')
.click();
cy.get(dialogCloseButton).click();
});
closeDialog();
}
Cypress.Commands.add('staking_page_associate_tokens', (amount, options) => {
let approve = options && options.approve ? options.approve : false;
let type = options && options.type ? options.type : 'wallet';
let skipConfirmation =
export function stakingPageAssociateTokens(
amount: string,
options?: associateOptions
) {
const approve = options && options.approve ? options.approve : false;
const type = options && options.type ? options.type : 'wallet';
const skipConfirmation =
options && options.skipConfirmation ? options.skipConfirmation : false;
cy.highlight(`Associating ${amount} tokens from ${type}`);
@@ -94,10 +99,13 @@ Cypress.Commands.add('staking_page_associate_tokens', (amount, options) => {
txTimeout
).should('be.visible');
}
});
}
Cypress.Commands.add('staking_page_disassociate_tokens', (amount, options) => {
let type = options && options.type ? options.type : 'wallet';
export function stakingPageDisassociateTokens(
amount: string,
options?: associateOptions
) {
const type = options && options.type ? options.type : 'wallet';
cy.highlight(
`Disassociating ${amount} tokens via Staking Page back to ${type}`
);
@@ -127,92 +135,89 @@ Cypress.Commands.add('staking_page_disassociate_tokens', (amount, options) => {
txTimeout
).should('be.visible');
}
});
}
Cypress.Commands.add(
'staking_page_disassociate_all_tokens',
(type = 'wallet') => {
cy.highlight(`Disassociating all tokens via Staking Page`);
cy.get(ethWalletDissociateButton).first().click();
cy.get(stakeMaximumTokens, epochTimeout).click();
cy.get(tokenSubmitButton, epochTimeout).click();
if (type === 'wallet') {
cy.contains(
`$VEGA tokens have been returned to Ethereum wallet`,
txTimeout
).should('be.visible');
} else if (type === 'contract') {
cy.contains(
`$VEGA tokens have been returned to Vesting contract`,
txTimeout
).should('be.visible');
}
export function stakingPageDisassociateAllTokens(type = 'wallet') {
cy.highlight(`Disassociating all tokens via Staking Page`);
cy.get(ethWalletDissociateButton).first().click();
cy.get(stakeMaximumTokens, epochTimeout).click();
cy.get(tokenSubmitButton, epochTimeout).click();
if (type === 'wallet') {
cy.contains(
`$VEGA tokens have been returned to Ethereum wallet`,
txTimeout
).should('be.visible');
} else if (type === 'contract') {
cy.contains(
`$VEGA tokens have been returned to Vesting contract`,
txTimeout
).should('be.visible');
}
);
}
Cypress.Commands.add(
'click_on_validator_from_list',
(validatorNumber, validatorName = null) => {
cy.contains('Loading...', epochTimeout).should('not.exist');
cy.wait_for_beginning_of_epoch();
// below is to ensure validator list is shown
cy.get(stakeValidatorListName, { timeout: 10000 }).should('exist');
cy.get(stakeValidatorListPendingStake, txTimeout).should(
'not.contain',
'2,000,000,000,000,000,000.00' // number due to bug #936
export function clickOnValidatorFromList(
validatorNumber: number,
validatorName = null
) {
cy.contains('Loading...', epochTimeout).should('not.exist');
waitForBeginningOfEpoch();
// below is to ensure validator list is shown
cy.get(stakeValidatorListName, { timeout: 10000 }).should('exist');
cy.get(stakeValidatorListPendingStake, txTimeout).should(
'not.contain',
'2,000,000,000,000,000,000.00' // number due to bug #936
);
if (validatorName) {
cy.contains(validatorName).click();
} else {
cy.get(`[row-id="${validatorNumber}"]`)
.should('be.visible')
.find(stakeValidatorListName)
.click();
}
}
export function validateValidatorListTotalStakeAndShare(
positionOnList: string,
expectedTotalStake: string,
expectedTotalShare: string
) {
cy.contains('Loading...', epochTimeout).should('not.exist');
waitForBeginningOfEpoch();
cy.get(`[row-id="${positionOnList}"]`).within(() => {
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
'have.text',
expectedTotalStake
);
if (validatorName) {
cy.contains(validatorName).click();
} else {
cy.get(`[row-id="${validatorNumber}"]`)
.should('be.visible')
.find(stakeValidatorListName)
.click();
}
}
);
cy.getByTestId(stakeValidatorListTotalShare, epochTimeout).should(
'have.text',
expectedTotalShare
);
});
}
Cypress.Commands.add(
'validate_validator_list_total_stake_and_share',
(positionOnList, expectedTotalStake, expectedTotalShare) => {
cy.contains('Loading...', epochTimeout).should('not.exist');
cy.wait_for_beginning_of_epoch();
cy.get(`[row-id="${positionOnList}"]`).within(() => {
cy.get(stakeValidatorListTotalStake, epochTimeout).should(
'have.text',
expectedTotalStake
);
cy.get(stakeValidatorListTotalShare, epochTimeout).should(
'have.text',
expectedTotalShare
);
export function ensureSpecifiedUnstakedTokensAreAssociated(
tokenAmount: string
) {
cy.highlight(`Ensuring ${tokenAmount} token(s) associated`);
cy.get(vegaWalletUnstakedBalance)
.children()
.children()
.eq(1)
.invoke('text')
.then((unstakedBalance) => {
if (parseFloat(unstakedBalance) != parseFloat(tokenAmount)) {
vegaWalletTeardown();
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
txTimeout
);
stakingPageAssociateTokens(tokenAmount);
}
});
}
);
}
Cypress.Commands.add(
'ensure_specified_unstaked_tokens_are_associated',
(tokenAmount) => {
cy.highlight(`Ensuring ${tokenAmount} token(s) associated`);
cy.get(vegaWalletUnstakedBalance)
.children()
.children()
.eq(1)
.invoke('text')
.then((unstakedBalance) => {
if (parseFloat(unstakedBalance) != parseFloat(tokenAmount)) {
cy.vega_wallet_teardown();
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
txTimeout
);
cy.staking_page_associate_tokens(tokenAmount);
}
});
}
);
Cypress.Commands.add('close_staking_dialog', () => {
export function closeStakingDialog() {
cy.getByTestId('dialog-title').should(
'contain.text',
'At the beginning of the next epoch'
@@ -220,20 +225,26 @@ Cypress.Commands.add('close_staking_dialog', () => {
cy.getByTestId('dialog-content').within(() => {
cy.get('a').should('have.text', 'Back to Staking').click();
});
});
}
Cypress.Commands.add(
'validate_wallet_currency',
(currencyTitle, expectedAmount) => {
cy.get("[data-testid='currency-title']")
.contains(currencyTitle)
.parent()
.parent()
.within(() => {
cy.getByTestId('currency-value', txTimeout).should(
'have.text',
expectedAmount
);
});
}
);
export function validateWalletCurrency(
currencyTitle: string,
expectedAmount: string
) {
cy.get("[data-testid='currency-title']")
.contains(currencyTitle)
.parent()
.parent()
.within(() => {
cy.getByTestId('currency-value', txTimeout).should(
'have.text',
expectedAmount
);
});
}
interface associateOptions {
approve?: boolean;
type?: string;
skipConfirmation?: boolean;
}
@@ -3,7 +3,7 @@ const connectToEthButton =
'[data-testid="connect-to-eth-wallet-button"]:visible';
const capsuleWalletConnectButton = '[data-testid="web3-connector-Unknown"]';
Cypress.Commands.add('ethereum_wallet_connect', () => {
export function ethereumWalletConnect() {
cy.highlight('Connecting Eth Wallet');
cy.get(connectToEthButton).within(() => {
cy.contains('Connect Ethereum wallet to associate $VEGA')
@@ -20,4 +20,4 @@ Cypress.Commands.add('ethereum_wallet_connect', () => {
// It needs a few seconds before becoming operational
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(4000);
});
}
@@ -1,197 +0,0 @@
import {
StakingBridge,
Token,
TokenVesting,
TokenFaucetable,
CollateralBridge,
} from '@vegaprotocol/smart-contracts';
import { ethers, Wallet } from 'ethers';
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]';
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
const vegaTokenAddress = Cypress.env('vegaTokenAddress');
const ethWalletPubKey = Cypress.env('ethWalletPublicKey');
const ethStakingBridgeContractAddress = Cypress.env(
'ethStakingBridgeContractAddress'
);
const ethProviderUrl = Cypress.env('ethProviderUrl');
const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`;
const transactionTimeout = '90000';
const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54';
before('Vega wallet teardown prep', function () {
cy.wrap(new ethers.providers.JsonRpcProvider({ url: ethProviderUrl }), {
log: false,
}).as('provider');
cy.wrap(Wallet.fromMnemonic(vegaWalletMnemonic, getAccount(0)).privateKey, {
log: false,
}).then((privateKey) => {
cy.wrap(new Wallet(privateKey, this.provider), { log: false }).as('signer');
});
cy.get('@signer', { log: false }).then((signer) => {
cy.wrap(new StakingBridge(ethStakingBridgeContractAddress, signer), {
log: false,
}).as('stakingBridgeContract');
cy.wrap(new TokenVesting(vegaTokenContractAddress, signer), {
log: false,
}).as('vestingContract');
});
});
beforeEach(function () {
cy.wrap(this.stakingBridgeContract).as('stakingBridgeContract');
cy.wrap(this.vestingContract).as('vestingContract');
});
Cypress.Commands.add('deposit_asset', function (assetEthAddress, amount) {
cy.highlight('Depositing asset into vegawallet');
cy.get('@signer', { log: false }).then((signer) => {
// Approve asset
cy.wrap(
new TokenFaucetable(assetEthAddress, signer).approve(
Erc20BridgeAddress,
amount + '0'.repeat(19)
)
)
.then((tx) => {
cy.wait_for_transaction(tx);
})
.then(() => {
cy.wrap(new CollateralBridge(Erc20BridgeAddress, signer), {
log: false,
}).then((bridge) => {
// Deposit asset into vega wallet
cy.wrap(
bridge.deposit_asset(
assetEthAddress,
amount + '0'.repeat(18),
'0x' + vegaWalletPubKey
),
{ timeout: transactionTimeout, log: false }
).then((tx) => {
cy.wait_for_transaction(tx);
});
});
});
});
});
Cypress.Commands.add('faucet_asset', function (assetEthAddress) {
cy.get('@signer', { log: false }).then((signer) => {
cy.wrap(new TokenFaucetable(assetEthAddress, signer), { log: false }).then(
(token) => {
cy.wrap(token.faucet(), {
timeout: transactionTimeout,
log: false,
}).then((tx) => {
cy.wait_for_transaction(tx);
});
}
);
});
});
Cypress.Commands.add('vega_wallet_teardown', function () {
cy.get('aside [data-testid="associated-amount"]')
.should('be.visible')
.invoke('text')
.as('associatedAmount');
cy.get('body').then(($body) => {
if (
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
this.associatedAmount != '0.00'
) {
cy.vega_wallet_teardown_vesting(this.vestingContract);
cy.vega_wallet_teardown_staking(this.stakingBridgeContract);
}
});
cy.get(vegaWalletContainer).within(() => {
cy.get('[data-testid="vega-wallet-balance-unstaked"]', {
timeout: 30000,
}).should('contain.text', '0.00');
});
});
Cypress.Commands.add(
'vega_wallet_set_specified_approval_amount',
function (resetAmount) {
cy.highlight(`Setting token approval amount to ${resetAmount}`);
cy.wrap(new Token(vegaTokenAddress, this.signer), { log: false }).then(
(token) => {
cy.wrap(
token.approve(
ethStakingBridgeContractAddress,
resetAmount.concat('000000000000000000')
),
{ timeout: transactionTimeout, log: false }
).then((tx) => {
cy.wait_for_transaction(tx);
});
}
);
}
);
Cypress.Commands.add(
'vega_wallet_teardown_staking',
(stakingBridgeContract) => {
cy.highlight('Tearing down staking tokens from vega wallet if present');
cy.wrap(
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
{
timeout: transactionTimeout,
log: false,
}
).then((stake_amount) => {
if (String(stake_amount) != '0') {
cy.wrap(
stakingBridgeContract.remove_stake(stake_amount, vegaWalletPubKey),
{ timeout: transactionTimeout, log: false }
).then((tx) => {
cy.wait_for_transaction(tx);
});
}
});
}
);
Cypress.Commands.add('vega_wallet_teardown_vesting', (vestingContract) => {
cy.highlight('Tearing down vesting tokens from vega wallet if present');
cy.wrap(vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), {
timeout: transactionTimeout,
log: false,
}).then((vesting_amount) => {
if (String(vesting_amount) != '0') {
cy.wrap(vestingContract.remove_stake(vesting_amount, vegaWalletPubKey), {
timeout: transactionTimeout,
log: false,
}).then((tx) => {
cy.wait_for_transaction(tx);
});
}
});
});
Cypress.Commands.add('vega_wallet_associate', (amount) => {
amount = amount + '0'.repeat(18);
cy.highlight('Associating tokens');
cy.get('@stakingBridgeContract').then((stakingBridgeContract) => {
stakingBridgeContract.stake(amount, vegaWalletPubKey);
});
});
Cypress.Commands.add('vega_wallet_disassociate', (amount) => {
amount = amount + '0'.repeat(18);
cy.highlight('Disassociating tokens');
cy.get('@stakingBridgeContract').then((stakingBridgeContract) => {
stakingBridgeContract.remove_stake(amount, vegaWalletPubKey);
});
});
Cypress.Commands.add('wait_for_transaction', (tx) => {
cy.wrap(tx.wait(1).catch(cy.log), { timeout: transactionTimeout });
});
@@ -0,0 +1,146 @@
import { promiseWithTimeout } from '@vegaprotocol/cypress';
import {
StakingBridge,
Token,
TokenVesting,
TokenFaucetable,
CollateralBridge,
} from '@vegaprotocol/smart-contracts';
import { ethers, Wallet } from 'ethers';
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]';
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
const vegaTokenAddress = Cypress.env('vegaTokenAddress');
const ethWalletPubKey = Cypress.env('ethWalletPublicKey');
const ethStakingBridgeContractAddress = Cypress.env(
'ethStakingBridgeContractAddress'
);
const ethProviderUrl = Cypress.env('ethProviderUrl');
const getAccount = (number = 0) => `m/44'/60'/0'/0/${number}`;
const transactionTimeout = 100000;
const Erc20BridgeAddress = '0x9708FF7510D4A7B9541e1699d15b53Ecb1AFDc54';
const provider = new ethers.providers.JsonRpcProvider({ url: ethProviderUrl });
const privateKey = Wallet.fromMnemonic(
vegaWalletMnemonic,
getAccount(0)
).privateKey;
const signer = new Wallet(privateKey, provider);
const stakingBridgeContract = new StakingBridge(
ethStakingBridgeContractAddress,
signer
);
const vestingContract = new TokenVesting(vegaTokenContractAddress, signer);
export async function depositAsset(assetEthAddress: string, amount: string) {
// Approve asset
const faucet = new TokenFaucetable(assetEthAddress, signer);
cy.wrap(faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(19)), {
timeout: transactionTimeout,
log: false,
}).then(() => {
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
cy.wrap(
collateralBridge.deposit_asset(
assetEthAddress,
amount + '0'.repeat(18),
'0x' + vegaWalletPubKey
),
{ timeout: transactionTimeout, log: false }
);
});
}
export async function faucetAsset(assetEthAddress: string) {
const faucet = new TokenFaucetable(assetEthAddress, signer);
await promiseWithTimeout(faucet.faucet(), 10 * 60 * 1000, 'faucet asset');
}
export async function vegaWalletTeardown() {
cy.get('[data-testid="associated-amount"]')
.should('be.visible')
.invoke('text')
.then((associatedAmount) => {
cy.get('body').then(($body) => {
if (
$body.find('[data-testid="eth-wallet-associated-balances"]').length ||
associatedAmount != '0.00'
) {
vegaWalletTeardownVesting(vestingContract);
vegaWalletTeardownStaking(stakingBridgeContract);
}
});
cy.get(vegaWalletContainer).within(() => {
cy.getByTestId('associated-amount', {
timeout: transactionTimeout,
}).contains('0.00', {
timeout: transactionTimeout,
});
});
});
}
export async function vegaWalletSetSpecifiedApprovalAmount(
resetAmount: string
) {
cy.highlight(`Setting token approval amount to ${resetAmount}`);
const token = new Token(vegaTokenAddress, signer);
await promiseWithTimeout(
token.approve(
ethStakingBridgeContractAddress,
resetAmount.concat('000000000000000000')
),
10 * 60 * 1000,
'set approval amount'
);
}
async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
cy.highlight('Tearing down staking tokens from vega wallet if present');
cy.wrap(
stakingBridgeContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
{ timeout: transactionTimeout, log: false }
).then((stakeBalance) => {
if (Number(stakeBalance) != 0) {
cy.wrap(
stakingBridgeContract.remove_stake(
String(stakeBalance),
vegaWalletPubKey
),
{ timeout: transactionTimeout, log: false }
);
}
});
}
async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
cy.highlight('Tearing down vesting tokens from vega wallet if present');
cy.wrap(vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey), {
timeout: transactionTimeout,
log: false,
}).then((vestingAmount) => {
if (Number(vestingAmount) != 0) {
// Wait needed to allow time for ganache to process tx for stakingBridgeContract.remove_stake
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000);
cy.wrap(
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
{ timeout: transactionTimeout, log: false }
);
}
});
}
export async function vegaWalletAssociate(amount: string) {
cy.highlight('Associating tokens');
amount = amount + '0'.repeat(18);
stakingBridgeContract.stake(amount, vegaWalletPubKey);
}
export async function vegaWalletDisassociate(amount: string) {
cy.highlight('Disassociating tokens');
amount = amount + '0'.repeat(18);
stakingBridgeContract.remove_stake(amount, vegaWalletPubKey);
}
@@ -1,17 +0,0 @@
Cypress.Commands.add(
'vega_wallet_faucet_assets_without_check',
function (asset, amount, vegaWalletPublicKey) {
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
cy.exec(
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
)
.its('stdout')
.then((response) => {
assert.include(
response,
`"success":true`,
'Ensuring curl command was successfully undertaken'
);
});
}
);
@@ -0,0 +1,18 @@
export function vegaWalletFaucetAssetsWithoutCheck(
asset: string,
amount: string,
vegaWalletPublicKey: string
) {
cy.highlight(`Topping up vega wallet with ${asset}, amount: ${amount}`);
cy.exec(
`curl -X POST -d '{"amount": "${amount}", "asset": "${asset}", "party": "${vegaWalletPublicKey}"}' http://localhost:1790/api/v1/mint`
)
.its('stdout')
.then((response) => {
assert.include(
response,
`"success":true`,
'Ensuring curl command was successfully undertaken'
);
});
}
+2
View File
@@ -1,6 +1,8 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"target": "es5",
"lib": ["es5", "dom", "es2021"],
"strict": true,
"jsx": "react-jsx",
"sourceMap": false,
+1
View File
@@ -13,6 +13,7 @@ NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/main/announcements.json
#Test configuration variables
CYPRESS_FAIRGROUND=false
+1
View File
@@ -5,6 +5,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_FAIRGROUND=false
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_CONFIG_URL=''
NX_VEGA_URL=http://localhost:3028/query
+1
View File
@@ -10,3 +10,4 @@ NX_VEGA_EXPLORER_URL=https://dev.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-devnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -11,3 +11,4 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_SENTRY_DSN=https://4b8c8a8ba07742648aa4dfe1b8d17e40:87edc2605e544f888305d7fc4a9141bd@o286262.ingest.sentry.io/5882996
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-mainnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
+1
View File
@@ -9,3 +9,4 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_EXPLORER_URL=https://mirror.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/mainnet
NX_DELEGATIONS_PAGINATION=50
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
+1
View File
@@ -6,3 +6,4 @@ NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-inter
NX_VEGA_EXPLORER_URL=https://sandbox.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -7,3 +7,4 @@ NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet1-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -8,3 +8,4 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-stagnet3-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
+1
View File
@@ -11,3 +11,4 @@ NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_DELEGATIONS_PAGINATION=50
NX_TRANCHES_SERVICE_URL=https://tranches-testnet-k8s.ops.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
@@ -165,7 +165,7 @@ const ConnectedKey = () => {
</section>
<WalletCardActions>
<Link className="flex-1" to={Routes.ASSOCIATE}>
<Button size="sm" fill={true}>
<Button data-testid="associate-btn" size="sm" fill={true}>
{t('associate')}
</Button>
</Link>
@@ -1,9 +1,6 @@
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import {
ViewingAsBanner,
AnnouncementBanner,
ExternalLink,
} from '@vegaprotocol/ui-toolkit';
import { ViewingAsBanner } from '@vegaprotocol/ui-toolkit';
import { AnnouncementBanner } from '@vegaprotocol/announcements';
import { useVegaWallet } from '@vegaprotocol/wallet';
import React from 'react';
@@ -15,18 +12,16 @@ export interface TemplateSidebarProps {
}
export function TemplateSidebar({ children, sidebar }: TemplateSidebarProps) {
const { VEGA_ENV } = useEnvironment();
const { VEGA_ENV, ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
const { isReadOnly, pubKey, disconnect } = useVegaWallet();
return (
<>
<AnnouncementBanner>
<div className="font-alpha calt uppercase text-center text-lg text-white">
<span className="pr-4">Wait no longer, SIM3 is here!</span>
<ExternalLink href="https://fairground.wtf/sim3">
Learn more
</ExternalLink>
</div>
</AnnouncementBanner>
{ANNOUNCEMENTS_CONFIG_URL && (
<AnnouncementBanner
app="governance"
configUrl={ANNOUNCEMENTS_CONFIG_URL}
/>
)}
<Nav theme={VEGA_ENV === Networks.TESTNET ? 'yellow' : 'dark'} />
{isReadOnly ? (
<ViewingAsBanner pubKey={pubKey} disconnect={disconnect} />
@@ -221,8 +221,9 @@
"votePending": "Casting vote",
"voteError": "Something went wrong, and your vote was not seen by the network",
"back": "back",
"byTokenVote": "by Token vote",
"byLiquidityVote": "by Liquidity vote",
"byTokenVote": "by token vote",
"byLiquidityVote": "by liquidity vote",
"byLPVote": "by LP vote",
"youDidNotVote": "Voting has ended. You did not vote",
"voteState_Yes": "For",
"voteState_No": "Against",
@@ -601,11 +602,14 @@
"noValidators": "No validators",
"validator": "Validator",
"stake": "Stake",
"myStake": "My stake",
"stakeShare": "Stake share",
"stakedByOperator": "Staked by operator",
"stakedByDelegates": "Staked by delegates",
"stakedByMe": "Staked by me",
"totalStake": "Total stake",
"pendingStake": "Pending stake",
"myPendingStake": "My pending stake",
"totalPenalties": "Total penalties",
"noPenaltyDataFromLastEpoch": "No penalty data from last epoch",
"stakeNeededForPromotion": "Stake needed for promotion",
+2 -2
View File
@@ -12,14 +12,14 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
import Routes from '../routes';
import { ExternalLinks, removePaginationWrapper } from '@vegaprotocol/utils';
import { useNodesQuery } from '../staking/home/__generated___/Nodes';
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
import { getNotRejectedProposals } from '../proposals/proposals/proposals-container';
import { Heading } from '../../components/heading';
import * as Schema from '@vegaprotocol/types';
import type { RouteChildProps } from '..';
import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals';
import type { NodesFragmentFragment } from '../staking/home/__generated___/Nodes';
import type { NodesFragmentFragment } from '../staking/home/__generated__/Nodes';
const nodesToShow = 6;
@@ -86,7 +86,7 @@ describe('Proposal Votes Table', () => {
it('displays if an update market proposal will pass by token vote', () => {
renderComponent(updateMarketProposal, updateMarketProposalType);
expect(screen.getByText('👍 by Token vote')).toBeInTheDocument();
expect(screen.getByText('👍 by token vote')).toBeInTheDocument();
});
it('displays if an update market proposal will pass by LP vote', () => {
@@ -110,6 +110,6 @@ describe('Proposal Votes Table', () => {
}),
updateMarketProposalType
);
expect(screen.getByText('👍 by Liquidity vote')).toBeInTheDocument();
expect(screen.getByText('👍 by liquidity vote')).toBeInTheDocument();
});
});
@@ -136,6 +136,82 @@ describe('Proposals list item details', () => {
);
});
it('Renders proposal state: Update market proposal - set to pass by LP vote', () => {
renderComponent(
generateProposal({
state: ProposalState.STATE_OPEN,
terms: {
change: {
__typename: 'UpdateMarket',
},
},
votes: {
yes: {
...generateYesVotes(0),
totalEquityLikeShareWeight: '1000',
},
no: {
...generateNoVotes(0),
totalEquityLikeShareWeight: '0',
},
},
})
);
expect(screen.getByTestId('vote-status')).toHaveTextContent(
'Set to pass by LP vote'
);
});
it('Renders proposal state: Update market proposal - set to pass by token vote', () => {
renderComponent(
generateProposal({
state: ProposalState.STATE_OPEN,
terms: {
change: {
__typename: 'UpdateMarket',
},
},
votes: {
yes: {
...generateYesVotes(1000, 1000),
totalEquityLikeShareWeight: '0',
},
no: {
...generateNoVotes(0),
totalEquityLikeShareWeight: '0',
},
},
})
);
expect(screen.getByTestId('vote-status')).toHaveTextContent(
'Set to pass by token vote'
);
});
it('Renders proposal state: Update market proposal - set to fail', () => {
renderComponent(
generateProposal({
state: ProposalState.STATE_OPEN,
terms: {
change: {
__typename: 'UpdateMarket',
},
},
votes: {
yes: {
...generateYesVotes(0),
totalEquityLikeShareWeight: '0',
},
no: {
...generateNoVotes(0),
totalEquityLikeShareWeight: '0',
},
},
})
);
expect(screen.getByTestId('vote-status')).toHaveTextContent('Set to fail');
});
it('Renders proposal state: Open - 5 minutes left to vote', () => {
renderComponent(
generateProposal({
@@ -41,12 +41,22 @@ export const ProposalsListItemDetails = ({
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
}) => {
const state = proposal?.state;
const { willPassByTokenVote, majorityMet, participationMet } =
useVoteInformation({
proposal,
});
const {
willPassByTokenVote,
willPassByLPVote,
majorityMet,
participationMet,
} = useVoteInformation({
proposal,
});
const { t } = useTranslation();
const { voteState } = useUserVote(proposal?.id);
const isUpdateMarket = proposal?.terms.change.__typename === 'UpdateMarket';
const updateMarketWillPass = willPassByTokenVote || willPassByLPVote;
const updateMarketVotePassMethod = willPassByTokenVote
? t('byTokenVote')
: t('byLPVote');
let proposalStatus: ReactNode;
let voteDetails: ReactNode;
let voteStatus: ReactNode;
@@ -128,6 +138,19 @@ export const ProposalsListItemDetails = ({
</>
);
voteStatus =
(isUpdateMarket &&
(updateMarketWillPass ? (
<>
{t('Set to')}{' '}
<StatusPass>
{t('pass')} {updateMarketVotePassMethod}
</StatusPass>
</>
) : (
<>
{t('Set to')} <StatusFail>{t('fail')}</StatusFail>
</>
))) ||
(!participationMet && <ParticipationNotReached />) ||
(!majorityMet && <MajorityNotReached />) ||
(willPassByTokenVote ? (
@@ -32,6 +32,7 @@ export const VoteDetails = ({
totalTokensPercentage,
participationMet,
totalTokensVoted,
totalLPTokensPercentage,
noPercentage,
noLPPercentage,
yesPercentage,
@@ -41,6 +42,8 @@ export const VoteDetails = ({
requiredMajorityPercentage,
requiredMajorityLPPercentage,
requiredParticipation,
requiredParticipationLP,
participationLPMet,
} = useVoteInformation({ proposal });
const { t } = useTranslation();
@@ -101,6 +104,25 @@ export const VoteDetails = ({
</tr>
</tbody>
</table>
<p className="mb-6">
{t('participation')}
{': '}
{participationLPMet ? (
<span className="text-vega-green mx-4">{t('met')}</span>
) : (
<span className="text-danger mx-4">{t('notMet')}</span>
)}{' '}
{formatNumber(totalLPTokensPercentage, defaultDecimals)}%
<span className="ml-4">
{requiredParticipationLP && (
<>
({formatNumber(requiredParticipationLP, defaultDecimals)}%{' '}
{t('governanceRequired')})
</>
)}
</span>
</p>
</section>
)}
<section data-testid="votes-table">
@@ -16,12 +16,8 @@ import orderBy from 'lodash/orderBy';
const orderByDate = (arr: ProposalFieldsFragment[]) =>
orderBy(
arr,
[
(p) => new Date(p?.terms?.enactmentDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered.
(p) => new Date(p?.terms?.closingDatetime).getTime(),
(p) => p.id,
],
['desc', 'desc', 'desc']
[(p) => new Date(p?.terms?.closingDatetime).getTime(), (p) => p.id],
['desc', 'desc']
);
export function getNotRejectedProposals<T extends ProposalFieldsFragment>(
@@ -21,6 +21,12 @@ export const EpochIndividualRewards = () => {
first: Number(delegationsPagination),
}
: undefined,
// we can use the same value for rewardsPagination as delegationsPagination
rewardsPagination: delegationsPagination
? {
first: Number(delegationsPagination),
}
: undefined,
},
skip: !pubKey,
});
@@ -21,10 +21,14 @@ fragment DelegationFields on Delegation {
epoch
}
query Rewards($partyId: ID!, $delegationsPagination: Pagination) {
query Rewards(
$partyId: ID!
$delegationsPagination: Pagination
$rewardsPagination: Pagination
) {
party(id: $partyId) {
id
rewardsConnection {
rewardsConnection(pagination: $rewardsPagination) {
edges {
node {
...RewardFields
@@ -10,6 +10,7 @@ export type DelegationFieldsFragment = { __typename?: 'Delegation', amount: stri
export type RewardsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
rewardsPagination?: Types.InputMaybe<Types.Pagination>;
}>;
@@ -75,10 +76,10 @@ export const EpochFieldsFragmentDoc = gql`
}
`;
export const RewardsDocument = gql`
query Rewards($partyId: ID!, $delegationsPagination: Pagination) {
query Rewards($partyId: ID!, $delegationsPagination: Pagination, $rewardsPagination: Pagination) {
party(id: $partyId) {
id
rewardsConnection {
rewardsConnection(pagination: $rewardsPagination) {
edges {
node {
...RewardFields
@@ -119,6 +120,7 @@ ${DelegationFieldsFragmentDoc}`;
* variables: {
* partyId: // value for 'partyId'
* delegationsPagination: // value for 'delegationsPagination'
* rewardsPagination: // value for 'rewardsPagination'
* },
* });
*/
@@ -104,7 +104,7 @@ export const RewardsPage = () => {
</section>
)}
<section className="grid xl:grid-cols-2 gap-12 items-center mb-8">
<section className="grid xl:grid-cols-[1fr_auto] gap-12 items-center mb-8">
<div>
<SubHeading title={t('rewardsAndFeesReceived')} />
<p>
@@ -114,7 +114,7 @@ export const RewardsPage = () => {
</p>
</div>
<div className="max-w-[600px]">
<div className="w-[440px]">
<Toggle
name="epoch-reward-view-toggle"
toggles={[
@@ -23,6 +23,17 @@ fragment StakingNodeFields on Node {
}
}
fragment StakingDelegationFields on Delegation {
amount
epoch
node {
id
}
party {
id
}
}
query Staking($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
id
@@ -32,11 +43,7 @@ query Staking($partyId: ID!, $delegationsPagination: Pagination) {
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
amount
epoch
node {
id
}
...StakingDelegationFields
}
}
}
@@ -5,13 +5,15 @@ import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type StakingNodeFieldsFragment = { __typename?: 'Node', id: string, name: string, pubkey: string, infoUrl: string, location: string, ethereumAddress: string, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null, rankingScore: { __typename?: 'RankingScore', rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string, status: Types.ValidatorStatus } };
export type StakingDelegationFieldsFragment = { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string }, party: { __typename?: 'Party', id: string } };
export type StakingQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
delegationsPagination?: Types.InputMaybe<Types.Pagination>;
}>;
export type StakingQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string } } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } }, nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, pubkey: string, infoUrl: string, location: string, ethereumAddress: string, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null, rankingScore: { __typename?: 'RankingScore', rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string, status: Types.ValidatorStatus } } } | null> | null }, nodeData?: { __typename?: 'NodeData', stakedTotal: string, totalNodes: number, inactiveNodes: number, uptime: number } | null };
export type StakingQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string }, party: { __typename?: 'Party', id: string } } } | null> | null } | null } | null, epoch: { __typename?: 'Epoch', id: string, timestamps: { __typename?: 'EpochTimestamps', start?: any | null, end?: any | null, expiry?: any | null } }, nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, pubkey: string, infoUrl: string, location: string, ethereumAddress: string, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null, rankingScore: { __typename?: 'RankingScore', rankingScore: string, stakeScore: string, performanceScore: string, votingPower: string, status: Types.ValidatorStatus } } } | null> | null }, nodeData?: { __typename?: 'NodeData', stakedTotal: string, totalNodes: number, inactiveNodes: number, uptime: number } | null };
export const StakingNodeFieldsFragmentDoc = gql`
fragment StakingNodeFields on Node {
@@ -39,6 +41,18 @@ export const StakingNodeFieldsFragmentDoc = gql`
}
}
`;
export const StakingDelegationFieldsFragmentDoc = gql`
fragment StakingDelegationFields on Delegation {
amount
epoch
node {
id
}
party {
id
}
}
`;
export const StakingDocument = gql`
query Staking($partyId: ID!, $delegationsPagination: Pagination) {
party(id: $partyId) {
@@ -49,11 +63,7 @@ export const StakingDocument = gql`
delegationsConnection(pagination: $delegationsPagination) {
edges {
node {
amount
epoch
node {
id
}
...StakingDelegationFields
}
}
}
@@ -80,7 +90,8 @@ export const StakingDocument = gql`
uptime
}
}
${StakingNodeFieldsFragmentDoc}`;
${StakingDelegationFieldsFragmentDoc}
${StakingNodeFieldsFragmentDoc}`;
/**
* __useStakingQuery__
@@ -1,106 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type PreviousEpochQueryVariables = Types.Exact<{
epochId?: Types.InputMaybe<Types.Scalars['ID']>;
}>;
export type PreviousEpochQuery = {
__typename?: 'Query';
epoch: {
__typename?: 'Epoch';
id: string;
validatorsConnection?: {
__typename?: 'NodesConnection';
edges?: Array<{
__typename?: 'NodeEdge';
node: {
__typename?: 'Node';
id: string;
rewardScore?: {
__typename?: 'RewardScore';
rawValidatorScore: string;
} | null;
rankingScore: {
__typename?: 'RankingScore';
performanceScore: string;
};
};
} | null> | null;
} | null;
};
};
export const PreviousEpochDocument = gql`
query PreviousEpoch($epochId: ID) {
epoch(id: $epochId) {
id
validatorsConnection {
edges {
node {
id
rewardScore {
rawValidatorScore
}
rankingScore {
performanceScore
}
}
}
}
}
}
`;
/**
* __usePreviousEpochQuery__
*
* To run a query within a React component, call `usePreviousEpochQuery` and pass it any options that fit your needs.
* When your component renders, `usePreviousEpochQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = usePreviousEpochQuery({
* variables: {
* epochId: // value for 'epochId'
* },
* });
*/
export function usePreviousEpochQuery(
baseOptions?: Apollo.QueryHookOptions<
PreviousEpochQuery,
PreviousEpochQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<PreviousEpochQuery, PreviousEpochQueryVariables>(
PreviousEpochDocument,
options
);
}
export function usePreviousEpochLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
PreviousEpochQuery,
PreviousEpochQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<PreviousEpochQuery, PreviousEpochQueryVariables>(
PreviousEpochDocument,
options
);
}
export type PreviousEpochQueryHookResult = ReturnType<
typeof usePreviousEpochQuery
>;
export type PreviousEpochLazyQueryHookResult = ReturnType<
typeof usePreviousEpochLazyQuery
>;
export type PreviousEpochQueryResult = Apollo.QueryResult<
PreviousEpochQuery,
PreviousEpochQueryVariables
>;
@@ -1,114 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type LinkingsFieldsFragment = {
__typename?: 'StakeLinking';
id: string;
txHash: string;
status: Types.StakeLinkingStatus;
};
export type PartyStakeLinkingsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
}>;
export type PartyStakeLinkingsQuery = {
__typename?: 'Query';
party?: {
__typename?: 'Party';
id: string;
stakingSummary: {
__typename?: 'StakingSummary';
linkings: {
__typename?: 'StakesConnection';
edges?: Array<{
__typename?: 'StakeLinkingEdge';
node: {
__typename?: 'StakeLinking';
id: string;
txHash: string;
status: Types.StakeLinkingStatus;
};
} | null> | null;
};
};
} | null;
};
export const LinkingsFieldsFragmentDoc = gql`
fragment LinkingsFields on StakeLinking {
id
txHash
status
}
`;
export const PartyStakeLinkingsDocument = gql`
query PartyStakeLinkings($partyId: ID!) {
party(id: $partyId) {
id
stakingSummary {
linkings {
edges {
node {
...LinkingsFields
}
}
}
}
}
}
${LinkingsFieldsFragmentDoc}
`;
/**
* __usePartyStakeLinkingsQuery__
*
* To run a query within a React component, call `usePartyStakeLinkingsQuery` and pass it any options that fit your needs.
* When your component renders, `usePartyStakeLinkingsQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = usePartyStakeLinkingsQuery({
* variables: {
* partyId: // value for 'partyId'
* },
* });
*/
export function usePartyStakeLinkingsQuery(
baseOptions: Apollo.QueryHookOptions<
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables
>(PartyStakeLinkingsDocument, options);
}
export function usePartyStakeLinkingsLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables
>(PartyStakeLinkingsDocument, options);
}
export type PartyStakeLinkingsQueryHookResult = ReturnType<
typeof usePartyStakeLinkingsQuery
>;
export type PartyStakeLinkingsLazyQueryHookResult = ReturnType<
typeof usePartyStakeLinkingsLazyQuery
>;
export type PartyStakeLinkingsQueryResult = Apollo.QueryResult<
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables
>;
@@ -15,7 +15,7 @@ import {
} from '../../../hooks/transaction-reducer';
import Routes from '../../routes';
import { truncateMiddle } from '../../../lib/truncate-middle';
import type { LinkingsFieldsFragment } from './__generated___/PartyStakeLinkings';
import type { LinkingsFieldsFragment } from './__generated__/PartyStakeLinkings';
export const AssociateTransaction = ({
amount,
@@ -15,8 +15,8 @@ import type {
LinkingsFieldsFragment,
PartyStakeLinkingsQuery,
PartyStakeLinkingsQueryVariables,
} from './__generated___/PartyStakeLinkings';
import { PartyStakeLinkingsDocument } from './__generated___/PartyStakeLinkings';
} from './__generated__/PartyStakeLinkings';
import { PartyStakeLinkingsDocument } from './__generated__/PartyStakeLinkings';
export const useAddStake = (
address: string,
@@ -1,149 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type NodesFragmentFragment = {
__typename?: 'Node';
avatarUrl?: string | null;
id: string;
name: string;
pubkey: string;
stakedByOperator: string;
stakedByDelegates: string;
stakedTotal: string;
pendingStake: string;
rankingScore: {
__typename?: 'RankingScore';
rankingScore: string;
stakeScore: string;
performanceScore: string;
votingPower: string;
status: Types.ValidatorStatus;
};
};
export type NodesQueryVariables = Types.Exact<{ [key: string]: never }>;
export type NodesQuery = {
__typename?: 'Query';
epoch: {
__typename?: 'Epoch';
id: string;
timestamps: {
__typename?: 'EpochTimestamps';
start?: any | null;
end?: any | null;
expiry?: any | null;
};
};
nodesConnection: {
__typename?: 'NodesConnection';
edges?: Array<{
__typename?: 'NodeEdge';
node: {
__typename?: 'Node';
avatarUrl?: string | null;
id: string;
name: string;
pubkey: string;
stakedByOperator: string;
stakedByDelegates: string;
stakedTotal: string;
pendingStake: string;
rankingScore: {
__typename?: 'RankingScore';
rankingScore: string;
stakeScore: string;
performanceScore: string;
votingPower: string;
status: Types.ValidatorStatus;
};
};
} | null> | null;
};
nodeData?: { __typename?: 'NodeData'; stakedTotal: string } | null;
};
export const NodesFragmentFragmentDoc = gql`
fragment NodesFragment on Node {
avatarUrl
id
name
pubkey
stakedByOperator
stakedByDelegates
stakedTotal
pendingStake
rankingScore {
rankingScore
stakeScore
performanceScore
votingPower
status
}
}
`;
export const NodesDocument = gql`
query Nodes {
epoch {
id
timestamps {
start
end
expiry
}
}
nodesConnection {
edges {
node {
...NodesFragment
}
}
}
nodeData {
stakedTotal
}
}
${NodesFragmentFragmentDoc}
`;
/**
* __useNodesQuery__
*
* To run a query within a React component, call `useNodesQuery` and pass it any options that fit your needs.
* When your component renders, `useNodesQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useNodesQuery({
* variables: {
* },
* });
*/
export function useNodesQuery(
baseOptions?: Apollo.QueryHookOptions<NodesQuery, NodesQueryVariables>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<NodesQuery, NodesQueryVariables>(
NodesDocument,
options
);
}
export function useNodesLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<NodesQuery, NodesQueryVariables>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<NodesQuery, NodesQueryVariables>(
NodesDocument,
options
);
}
export type NodesQueryHookResult = ReturnType<typeof useNodesQuery>;
export type NodesLazyQueryHookResult = ReturnType<typeof useNodesLazyQuery>;
export type NodesQueryResult = Apollo.QueryResult<
NodesQuery,
NodesQueryVariables
>;
@@ -1,36 +1,53 @@
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { EpochCountdown } from '../../../components/epoch-countdown';
import { useNodesQuery } from './__generated___/Nodes';
import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch';
import { useNodesQuery } from './__generated__/Nodes';
import { useStakingQuery } from '../__generated__/Staking';
import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
import { ValidatorTables } from './validator-tables';
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
import { useVegaWallet } from '@vegaprotocol/wallet';
export const EpochData = () => {
// errorPolicy due to vegaprotocol/vega issue 5898
const { data, error, loading, refetch } = useNodesQuery();
const { pubKey } = useVegaWallet();
const {
data: nodesData,
error: nodesError,
loading: nodesLoading,
refetch,
} = useNodesQuery();
const { data: userStakingData } = useStakingQuery({
variables: {
partyId: pubKey || '',
},
});
const { data: previousEpochData } = usePreviousEpochQuery({
variables: {
epochId: (Number(data?.epoch.id) - 1).toString(),
epochId: (Number(nodesData?.epoch.id) - 1).toString(),
},
skip: !data?.epoch.id,
skip: !nodesData?.epoch.id,
});
useRefreshAfterEpoch(data?.epoch.timestamps.expiry, refetch);
useRefreshAfterEpoch(nodesData?.epoch.timestamps.expiry, refetch);
return (
<AsyncRenderer loading={loading} error={error} data={data}>
{data?.epoch &&
data.epoch.timestamps.start &&
data?.epoch.timestamps.expiry && (
<AsyncRenderer loading={nodesLoading} error={nodesError} data={nodesData}>
{nodesData?.epoch &&
nodesData.epoch.timestamps.start &&
nodesData?.epoch.timestamps.expiry && (
<div className="mb-10">
<EpochCountdown
id={data.epoch.id}
startDate={new Date(data.epoch.timestamps.start)}
endDate={new Date(data.epoch.timestamps.expiry)}
id={nodesData.epoch.id}
startDate={new Date(nodesData.epoch.timestamps.start)}
endDate={new Date(nodesData.epoch.timestamps.expiry)}
/>
</div>
)}
<ValidatorTables data={data} previousEpochData={previousEpochData} />
<ValidatorTables
nodesData={nodesData}
userStakingData={userStakingData}
previousEpochData={previousEpochData}
/>
</AsyncRenderer>
);
};
@@ -3,14 +3,15 @@ import { act, fireEvent, render, screen } from '@testing-library/react';
import { ConsensusValidatorsTable } from './consensus-validators-table';
import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter } from 'react-router-dom';
import { NodesDocument } from '../__generated___/Nodes';
import { PreviousEpochDocument } from '../../__generated___/PreviousEpoch';
import { NodesDocument } from '../__generated__/Nodes';
import { PreviousEpochDocument } from '../../__generated__/PreviousEpoch';
import * as Schema from '@vegaprotocol/types';
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
import type { MockedResponse } from '@apollo/client/testing';
import type { PartialDeep } from 'type-fest';
import type { NodesFragmentFragment } from '../__generated___/Nodes';
import type { PreviousEpochQuery } from '../../__generated___/PreviousEpoch';
import type { NodesFragmentFragment } from '../__generated__/Nodes';
import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
import type { ValidatorsView } from './validator-tables';
const nodeFactory = (
overrides?: PartialDeep<NodesFragmentFragment>
@@ -142,7 +143,8 @@ const MOCK_TOTAL_STAKE = '28832590188747439203824';
const renderValidatorsTable = (
data = MOCK_NODES,
previousEpochData = MOCK_PREVIOUS_EPOCH
previousEpochData = MOCK_PREVIOUS_EPOCH,
validatorsView: ValidatorsView = 'all'
) => {
return render(
<AppStateProvider initialState={{ decimals: 18 }}>
@@ -152,6 +154,7 @@ const renderValidatorsTable = (
data={data}
previousEpochData={previousEpochData}
totalStake={MOCK_TOTAL_STAKE}
validatorsView={validatorsView}
/>
</MockedProvider>
</MemoryRouter>
@@ -19,7 +19,9 @@ import {
import {
defaultColDef,
NODE_LIST_GRID_STYLES,
PendingStakeRenderer,
stakedTotalPercentage,
StakeShareRenderer,
TotalPenaltiesRenderer,
TotalStakeRenderer,
ValidatorFields,
@@ -54,6 +56,9 @@ interface CanonisedConsensusNodeProps {
[ValidatorFields.OVERSTAKING_PENALTY]: string;
[ValidatorFields.TOTAL_PENALTIES]: string;
[ValidatorFields.PENDING_STAKE]: string;
[ValidatorFields.STAKED_BY_USER]: string | undefined;
[ValidatorFields.PENDING_USER_STAKE]: string | undefined;
[ValidatorFields.USER_STAKE_SHARE]: string | undefined;
}
const getRowHeight = (params: RowHeightParams) => {
@@ -61,7 +66,7 @@ const getRowHeight = (params: RowHeightParams) => {
// Note: this value will change if the height of the top third cell renderer changes
return 138;
}
return 52;
return 68;
};
const TopThirdCellRenderer = (
@@ -117,6 +122,7 @@ export const ConsensusValidatorsTable = ({
data,
previousEpochData,
totalStake,
validatorsView,
}: ValidatorsTableProps) => {
const { t } = useTranslation();
const {
@@ -129,7 +135,7 @@ export const ConsensusValidatorsTable = ({
const nodes = useMemo(() => {
if (!data || !previousEpochData) return [];
const canonisedNodes = data
let canonisedNodes = data
.sort((a, b) => {
const aVotingPower = new BigNumber(a.rankingScore.votingPower);
const bVotingPower = new BigNumber(b.rankingScore.votingPower);
@@ -154,6 +160,9 @@ export const ConsensusValidatorsTable = ({
rankingScore: { stakeScore, votingPower },
pendingStake,
votingPowerRanking,
stakedByUser,
pendingUserStake,
userStakeShare,
}) => {
const { rawValidatorScore, performanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
@@ -201,12 +210,28 @@ export const ConsensusValidatorsTable = ({
totalStake
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
decimals,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
? formatNumber(toBigNum(stakedByUser, decimals), 2)
: undefined,
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
? stakedTotalPercentage(userStakeShare)
: undefined,
};
}
);
if (canonisedNodes.length < 3 || !hideTopThird) {
if (validatorsView === 'myStake') {
canonisedNodes = canonisedNodes.filter(
(node) => node[ValidatorFields.STAKED_BY_USER] !== undefined
);
}
if (
canonisedNodes.length < 3 ||
!hideTopThird ||
validatorsView === 'myStake'
) {
return canonisedNodes;
}
@@ -291,7 +316,14 @@ export const ConsensusValidatorsTable = ({
},
...remaining,
];
}, [data, decimals, hideTopThird, previousEpochData, totalStake]);
}, [
data,
decimals,
hideTopThird,
previousEpochData,
totalStake,
validatorsView,
]);
const ConsensusTable = forwardRef<AgGridReact>((_, gridRef) => {
const colDefs = useMemo<ColDef[]>(
@@ -311,7 +343,7 @@ export const ConsensusValidatorsTable = ({
return a > b ? 1 : -1;
},
pinned: 'left',
width: 240,
width: 260,
},
{
field: ValidatorFields.STAKE,
@@ -320,6 +352,20 @@ export const ConsensusValidatorsTable = ({
cellRenderer: TotalStakeRenderer,
width: 120,
},
{
field: ValidatorFields.PENDING_STAKE,
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
headerTooltip: t('PendingStakeDescription').toString(),
cellRenderer: PendingStakeRenderer,
width: 120,
},
{
field: ValidatorFields.STAKE_SHARE,
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
headerTooltip: t('StakeShareDescription').toString(),
cellRenderer: StakeShareRenderer,
width: 120,
},
{
field: ValidatorFields.NORMALISED_VOTING_POWER,
headerName: t(ValidatorFields.NORMALISED_VOTING_POWER).toString(),
@@ -328,12 +374,6 @@ export const ConsensusValidatorsTable = ({
width: 200,
sort: 'desc',
},
{
field: ValidatorFields.STAKE_SHARE,
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
headerTooltip: t('StakeShareDescription').toString(),
width: 100,
},
{
field: ValidatorFields.TOTAL_PENALTIES,
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
@@ -341,14 +381,6 @@ export const ConsensusValidatorsTable = ({
cellRenderer: TotalPenaltiesRenderer,
width: 120,
},
{
field: ValidatorFields.PENDING_STAKE,
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
headerTooltip: t('PendingStakeDescription').toString(),
valueFormatter: ({ value }) =>
formatNumber(toBigNum(value, decimals), 2),
width: 110,
},
],
[]
);
@@ -1,70 +0,0 @@
import {
getLastEpochScoreAndPerformance,
getTotalPenalties,
} from '../../shared';
import { stakedTotalPercentage } from './shared';
const MOCK_PREVIOUS_EPOCH = {
epoch: {
id: '1',
validatorsConnection: {
edges: [
{
node: {
id: '0x123',
rewardScore: {
rawValidatorScore: '0.25',
},
rankingScore: {
performanceScore: '0.75',
},
},
},
],
},
},
};
describe('stakedTotalPercentage', () => {
it('should return the correct percentage as a string, 2dp', () => {
expect(stakedTotalPercentage('1.2345')).toBe('123.45%');
});
});
describe('totalPenalties', () => {
it('should return the correct penalty based on arbitrary values, test 1', () => {
expect(
getTotalPenalties(
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
.rawValidatorScore,
'0.1',
'5000',
'100000'
)
).toBe('50.00%');
});
it('should return the correct penalty based on lower performance score than first test', () => {
expect(
getTotalPenalties(
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
.rawValidatorScore,
'0.05',
'5000',
'100000'
)
).toBe('75.00%');
});
it('should return the correct penalty based on higher amount of stake than other tests (great penalty due to anti-whaling)', () => {
expect(
getTotalPenalties(
getLastEpochScoreAndPerformance(MOCK_PREVIOUS_EPOCH, '0x123')
.rawValidatorScore,
'0.1',
'5000',
'5500'
)
).toBe('97.25%');
});
});
@@ -10,9 +10,12 @@ import {
Tooltip,
TooltipCellComponent,
} from '@vegaprotocol/ui-toolkit';
import type { NodesFragmentFragment } from '../__generated___/Nodes';
import type { PreviousEpochQuery } from '../../__generated___/PreviousEpoch';
import { BigNumber } from '../../../../lib/bignumber';
import type { NodesFragmentFragment } from '../__generated__/Nodes';
import type { PreviousEpochQuery } from '../../__generated__/PreviousEpoch';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import type { StakingDelegationFieldsFragment } from '../../__generated__/Staking';
import type { ValidatorsView } from './validator-tables';
export enum ValidatorFields {
RANKING_INDEX = 'rankingIndex',
@@ -31,12 +34,49 @@ export enum ValidatorFields {
PERFORMANCE_PENALTY = 'performancePenalty',
OVERSTAKED_AMOUNT = 'overstakedAmount',
OVERSTAKING_PENALTY = 'overstakingPenalty',
// the following are additional fields added to the validator object displaying user data
STAKED_BY_USER = 'stakedByUser',
PENDING_USER_STAKE = 'pendingUserStake',
USER_STAKE_SHARE = 'userStakeShare',
}
export const addUserDataToValidator = (
validator: NodesFragmentFragment,
currentEpochUserStaking: StakingDelegationFieldsFragment | undefined,
nextEpochUserStaking: StakingDelegationFieldsFragment | undefined,
currentUserStakeAvailable: string
) => {
return {
...validator,
[ValidatorFields.STAKED_BY_USER]:
currentEpochUserStaking && Number(currentEpochUserStaking?.amount) > 0
? currentEpochUserStaking.amount
: undefined,
[ValidatorFields.PENDING_USER_STAKE]: nextEpochUserStaking
? new BigNumber(nextEpochUserStaking?.amount)
.minus(new BigNumber(currentEpochUserStaking?.amount || 0))
.toString()
: undefined,
[ValidatorFields.USER_STAKE_SHARE]:
currentEpochUserStaking && Number(currentEpochUserStaking.amount) > 0
? new BigNumber(currentEpochUserStaking.amount).dividedBy(
new BigNumber(currentUserStakeAvailable)
)
: undefined,
};
};
export type ValidatorWithUserData = NodesFragmentFragment & {
stakedByUser?: string;
pendingUserStake?: string;
userStakeShare?: string;
};
export interface ValidatorsTableProps {
data: NodesFragmentFragment[] | undefined;
data: ValidatorWithUserData[] | undefined;
previousEpochData: PreviousEpochQuery | undefined;
totalStake: string;
validatorsView: ValidatorsView;
}
// Custom styling to account for the scrollbar. This is needed because the
@@ -58,19 +98,23 @@ export const defaultColDef = {
resizable: true,
autoHeight: true,
comparator: (a: string, b: string) => parseFloat(a) - parseFloat(b),
cellStyle: { margin: '10px 0', padding: '0 12px' },
tooltipComponent: TooltipCellComponent,
cellStyle: { display: 'flex', alignItems: 'center', padding: '0 10px' },
};
interface ValidatorRendererProps {
data: { id: string; validator: { avatarUrl: string; name: string } };
data: {
id: string;
validator: { avatarUrl: string; name: string };
stakedByUser: string | undefined;
};
}
export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => {
const { t } = useTranslation();
const { avatarUrl, name } = data.validator;
return (
<div className="grid grid-cols-[1fr_auto] gap-2 items-center">
<div className="w-[238px] grid grid-cols-[1fr_auto] gap-2 items-center">
<span className="flex overflow-hidden">
{avatarUrl && (
<img
@@ -83,9 +127,19 @@ export const ValidatorRenderer = ({ data }: ValidatorRendererProps) => {
<span>{name}</span>
</span>
<Link to={data.id}>
<Button size="sm" fill={true}>
{t('Stake')}
</Button>
{data.stakedByUser ? (
<Button
data-testid="my-stake-btn"
size="sm"
className="text-vega-green border-vega-green"
>
{t('myStake')}
</Button>
) : (
<Button data-testid="stake-btn" size="sm" fill={true}>
{t('Stake')}
</Button>
)}
</Link>
</div>
);
@@ -102,8 +156,14 @@ export const StakeNeededForPromotionRenderer = ({
data,
}: StakeNeededForPromotionRendererProps) => {
return (
<Tooltip description={data.stakeNeededForPromotionDescription}>
<span>
<Tooltip
description={
<span data-testid="stake-needed-for-promotion-tooltip">
{data.stakeNeededForPromotionDescription}
</span>
}
>
<span data-testid="stake-needed-for-promotion">
{data.stakeNeededForPromotion &&
formatNumber(data.stakeNeededForPromotion, 2)}
</span>
@@ -134,7 +194,59 @@ export const VotingPowerRenderer = ({ data }: VotingPowerRendererProps) => {
</>
}
>
<span>{data.normalisedVotingPower}</span>
<span data-testid="normalised-voting-power">
{data.normalisedVotingPower}
</span>
</Tooltip>
);
};
interface PendingStakeRendererProps {
data: {
pendingStake: string;
pendingUserStake: string | undefined;
};
}
export const PendingStakeRenderer = ({ data }: PendingStakeRendererProps) => {
const { t } = useTranslation();
const {
appState: { decimals },
} = useAppState();
return (
<Tooltip
description={
<>
<div data-testid="pending-stake-tooltip">
{t('pendingStake')}:{' '}
{formatNumber(toBigNum(data.pendingStake, decimals), decimals)}
</div>
{data.pendingUserStake && (
<div
className="text-vega-green border-t border-t-vega-dark-200 mt-1.5 pt-1"
data-testid="pending-user-stake-tooltip"
>
{t('myPendingStake')}:{' '}
{formatNumber(
toBigNum(data.pendingUserStake, decimals),
decimals
)}
</div>
)}
</>
}
>
<div className="flex flex-col">
{data.pendingUserStake && data.pendingStake !== '0' && (
<span data-testid="pending-user-stake" className="text-vega-green">
{formatNumber(toBigNum(data.pendingUserStake, decimals), 2)}
</span>
)}
<span data-testid="total-pending-stake">
{formatNumber(toBigNum(data.pendingStake, decimals), 2)}
</span>
</div>
</Tooltip>
);
};
@@ -144,6 +256,7 @@ interface TotalStakeRendererProps {
stake: string;
stakedByDelegates: string;
stakedByOperator: string;
stakedByUser: string | undefined;
};
}
@@ -165,18 +278,52 @@ export const TotalStakeRenderer = ({ data }: TotalStakeRendererProps) => {
<div data-testid="staked-delegates-tooltip">
{t('stakedByDelegates')}: {data.stakedByDelegates.toString()}
</div>
<div data-testid="total-staked-tooltip">
{t('totalStake')}:{' '}
<span className="font-bold">{formattedStake}</span>
<div className="font-bold" data-testid="total-staked-tooltip">
{t('totalStake')}: {formattedStake}
</div>
{data.stakedByUser && (
<div
className="text-vega-green border-t border-t-vega-dark-200 mt-1.5 pt-1"
data-testid="staked-by-user-tooltip"
>
{t('stakedByMe')}: {data.stakedByUser}
</div>
)}
</>
}
>
<span>{formattedStake}</span>
<div className="flex flex-col">
{data.stakedByUser && (
<span data-testid="user-stake" className="text-vega-green">
{data.stakedByUser}
</span>
)}
<span data-testid="total-stake">{formattedStake}</span>
</div>
</Tooltip>
);
};
interface StakeShareRendererProps {
data: {
stakeShare: string;
userStakeShare: string | undefined;
};
}
export const StakeShareRenderer = ({ data }: StakeShareRendererProps) => {
return (
<div className="flex flex-col">
{data.userStakeShare && (
<span data-testid="user-stake-share" className="text-vega-green">
{data.userStakeShare}
</span>
)}
<span data-testid="total-stake-share">{data.stakeShare}</span>
</div>
);
};
interface TotalPenaltiesRendererProps {
data: {
performanceScore: string;
@@ -209,7 +356,7 @@ export const TotalPenaltiesRenderer = ({
</>
}
>
<span>{data.totalPenalties}</span>
<span data-testid="total-penalty">{data.totalPenalties}</span>
</Tooltip>
);
};
@@ -21,6 +21,8 @@ import {
ValidatorRenderer,
TotalPenaltiesRenderer,
TotalStakeRenderer,
StakeShareRenderer,
PendingStakeRenderer,
} from './shared';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community';
@@ -38,6 +40,7 @@ export const StandbyPendingValidatorsTable = ({
totalStake,
stakeNeededForPromotion,
stakeNeededForPromotionDescription,
validatorsView,
}: StandbyPendingValidatorsTableProps) => {
const { t } = useTranslation();
const {
@@ -47,7 +50,7 @@ export const StandbyPendingValidatorsTable = ({
const gridRef = useRef<AgGridReact | null>(null);
const nodes = useMemo(() => {
let nodes = useMemo(() => {
if (!data) return [];
return data
@@ -75,6 +78,9 @@ export const StandbyPendingValidatorsTable = ({
rankingScore: { stakeScore },
pendingStake,
votingPowerRanking,
stakedByUser,
pendingUserStake,
userStakeShare,
}) => {
const { rawValidatorScore, performanceScore } =
getLastEpochScoreAndPerformance(previousEpochData, id);
@@ -152,6 +158,13 @@ export const StandbyPendingValidatorsTable = ({
totalStake
),
[ValidatorFields.PENDING_STAKE]: pendingStake,
[ValidatorFields.STAKED_BY_USER]: stakedByUser
? formatNumber(toBigNum(stakedByUser, decimals), 2)
: undefined,
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
? stakedTotalPercentage(userStakeShare)
: undefined,
};
}
);
@@ -165,6 +178,12 @@ export const StandbyPendingValidatorsTable = ({
totalStake,
]);
if (validatorsView === 'myStake') {
nodes = nodes.filter(
(node) => node[ValidatorFields.STAKED_BY_USER] !== undefined
);
}
const StandbyPendingTable = forwardRef<AgGridReact>((_, gridRef) => {
const colDefs = useMemo<ColDef[]>(
() => [
@@ -180,7 +199,7 @@ export const StandbyPendingValidatorsTable = ({
cellRenderer: ValidatorRenderer,
comparator: ({ name: a }, { name: b }) => Math.sign(a - b),
pinned: 'left',
width: 240,
width: 260,
},
{
field: ValidatorFields.STAKE,
@@ -189,6 +208,20 @@ export const StandbyPendingValidatorsTable = ({
cellRenderer: TotalStakeRenderer,
width: 120,
},
{
field: ValidatorFields.PENDING_STAKE,
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
headerTooltip: t('PendingStakeDescription').toString(),
cellRenderer: PendingStakeRenderer,
width: 120,
},
{
field: ValidatorFields.STAKE_SHARE,
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
headerTooltip: t('StakeShareDescription').toString(),
cellRenderer: StakeShareRenderer,
width: 100,
},
{
field: ValidatorFields.STAKE_NEEDED_FOR_PROMOTION,
headerName: t(ValidatorFields.STAKE_NEEDED_FOR_PROMOTION).toString(),
@@ -199,12 +232,6 @@ export const StandbyPendingValidatorsTable = ({
width: 210,
sort: 'asc',
},
{
field: ValidatorFields.STAKE_SHARE,
headerName: t(ValidatorFields.STAKE_SHARE).toString(),
headerTooltip: t('StakeShareDescription').toString(),
width: 100,
},
{
field: ValidatorFields.TOTAL_PENALTIES,
headerName: t(ValidatorFields.TOTAL_PENALTIES).toString(),
@@ -212,14 +239,6 @@ export const StandbyPendingValidatorsTable = ({
cellRenderer: TotalPenaltiesRenderer,
width: 120,
},
{
field: ValidatorFields.PENDING_STAKE,
headerName: t(ValidatorFields.PENDING_STAKE).toString(),
headerTooltip: t('PendingStakeDescription').toString(),
valueFormatter: ({ value }) =>
formatNumber(toBigNum(value, decimals), 2),
width: 110,
},
],
[]
);
@@ -230,7 +249,7 @@ export const StandbyPendingValidatorsTable = ({
domLayout="autoHeight"
style={{ width: '100%' }}
customThemeParams={NODE_LIST_GRID_STYLES}
rowHeight={52}
rowHeight={68}
defaultColDef={defaultColDef}
tooltipShowDelay={0}
animateRows={true}

Some files were not shown because too many files have changed in this diff Show More