Compare commits
63
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6430c3930 | ||
|
|
f37a8720a2 | ||
|
|
f5fe3cc18d | ||
|
|
288f075cac | ||
|
|
ca46861fd8 | ||
|
|
2cb6ede492 | ||
|
|
eefcc96149 | ||
|
|
7a99ded8e9 | ||
|
|
4a4cdaa2b8 | ||
|
|
cd969549ab | ||
|
|
5a73b7e769 | ||
|
|
a4f41eabb6 | ||
|
|
351a20abad | ||
|
|
8d42481130 | ||
|
|
8a59722f09 | ||
|
|
de48ffa563 | ||
|
|
00c5252aea | ||
|
|
9acc083c11 | ||
|
|
77264e80f1 | ||
|
|
50378bc8d2 | ||
|
|
b88da51c68 | ||
|
|
2ae56bb113 | ||
|
|
752af9a983 | ||
|
|
a01df081eb | ||
|
|
c2d14d1abe | ||
|
|
a76ff44ffd | ||
|
|
ac2e262bf3 | ||
|
|
ab180875b1 | ||
|
|
66d3e89b9e | ||
|
|
c9cce87606 | ||
|
|
50232ab089 | ||
|
|
e86fb6d0b4 | ||
|
|
473ef3ae83 | ||
|
|
77ab719b37 | ||
|
|
a10f67d7ab | ||
|
|
66d2b83513 | ||
|
|
e4fd040933 | ||
|
|
30c1dbd019 | ||
|
|
5e558c4cd3 | ||
|
|
85060a0421 | ||
|
|
b336291b49 | ||
|
|
0f7bffd38b | ||
|
|
3665375b75 | ||
|
|
8572c0a570 | ||
|
|
6a29dc82ef | ||
|
|
c19bcc5e0c | ||
|
|
6593bbf37b | ||
|
|
1e5e57b978 | ||
|
|
5394b00349 | ||
|
|
32f5eaade5 | ||
|
|
b7a440132d | ||
|
|
d6ecdf80fa | ||
|
|
21e545d941 | ||
|
|
ef5fa7cba6 | ||
|
|
d1f465e2af | ||
|
|
2740df48a3 | ||
|
|
cf1f10e324 | ||
|
|
f09a1abcfa | ||
|
|
3de83c6eb8 | ||
|
|
e6545c13af | ||
|
|
613582ee00 | ||
|
|
77582a62a8 | ||
|
|
384c96376a |
@@ -50,7 +50,7 @@ jobs:
|
||||
secrets: inherit
|
||||
|
||||
lint-test-build:
|
||||
timeout-minutes: 35
|
||||
timeout-minutes: 60
|
||||
needs: node-modules
|
||||
runs-on: ubuntu-22.04
|
||||
name: '(CI) lint + unit test + build'
|
||||
@@ -105,19 +105,44 @@ jobs:
|
||||
affected="$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)"
|
||||
echo -n "Affected projects: $affected"
|
||||
|
||||
branch_slug="$(echo ${{ github.head_ref || github.ref_name }} | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g )"
|
||||
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
|
||||
preview_governance="not deployed"
|
||||
preview_trading="not deployed"
|
||||
preview_explorer="not deployed"
|
||||
if [[ -z "$projects_e2e" ]]; then
|
||||
projects_e2e+='"governance-e2e" "trading-e2e" "explorer-e2e" '
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
else
|
||||
if [[ $affected == *"governance"* ]]; then
|
||||
projects_e2e+='"governance-e2e" '
|
||||
preview_governance=$(printf "https://%s.%s.vega.rocks" "governance" "$branch_slug")
|
||||
fi
|
||||
if [[ $affected == *"trading"* ]]; then
|
||||
projects_e2e+='"trading-e2e" '
|
||||
preview_trading=$(printf "https://%s.%s.vega.rocks" "trading" "$branch_slug")
|
||||
fi
|
||||
if [[ $affected == *"explorer"* ]]; then
|
||||
projects_e2e+='"explorer-e2e" '
|
||||
preview_explorer=$(printf "https://%s.%s.vega.rocks" "explorer" "$branch_slug")
|
||||
fi
|
||||
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
|
||||
echo PREVIEW_GOVERNANCE=$preview_governance >> $GITHUB_ENV
|
||||
echo PREVIEW_TRADING=$preview_trading >> $GITHUB_ENV
|
||||
echo PREVIEW_EXPLORER=$preview_explorer >> $GITHUB_ENV
|
||||
|
||||
outputs:
|
||||
projects: ${{ env.PROJECTS }}
|
||||
projects-e2e: ${{ env.PROJECTS_E2E }}
|
||||
preview_governance: ${{ env.PREVIEW_GOVERNANCE }}
|
||||
preview_trading: ${{ env.PREVIEW_TRADING }}
|
||||
preview_explorer: ${{ env.PREVIEW_EXPLORER }}
|
||||
|
||||
cypress:
|
||||
needs: lint-test-build
|
||||
@@ -140,7 +165,9 @@ jobs:
|
||||
|
||||
dist-check:
|
||||
runs-on: ubuntu-latest
|
||||
needs: publish-dist
|
||||
needs:
|
||||
- publish-dist
|
||||
- lint-test-build
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
name: '(CD) comment preview links'
|
||||
steps:
|
||||
@@ -151,22 +178,16 @@ jobs:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body-includes: Previews
|
||||
|
||||
- name: Inject slug/short variables
|
||||
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||
uses: rlespinasse/github-slug-action@v4
|
||||
with:
|
||||
prefix: CI_
|
||||
|
||||
- name: Create comment
|
||||
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
if: ${{ steps.fc.outputs.comment-id == 0 }}
|
||||
with:
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
body: |
|
||||
Previews
|
||||
- explorer https://explorer.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||
- trading https://trading.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||
- governance https://governance.${{ env.CI_GITHUB_REF_NAME }}.vega.rocks
|
||||
Previews:
|
||||
* governance: ${{ needs.lint-test-build.outputs.preview_governance }}
|
||||
* explorer: ${{ needs.lint-test-build.outputs.preview_explorer }}
|
||||
* trading: ${{ needs.lint-test-build.outputs.preview_trading }}
|
||||
|
||||
cypress-check:
|
||||
name: '(CI) cypress - check'
|
||||
|
||||
Vendored
+1
-19
@@ -1,20 +1,2 @@
|
||||
@Library('vega-shared-library') _
|
||||
|
||||
def commitHash = 'UNKNOWN'
|
||||
|
||||
pipeline {
|
||||
agent any
|
||||
options {
|
||||
skipDefaultCheckout true
|
||||
parallelsAlwaysFailFast()
|
||||
}
|
||||
stages {
|
||||
stage('approbation') {
|
||||
steps {
|
||||
sh 'printenv'
|
||||
checkout scm
|
||||
runApprobation ignoreFailure: false, frontendBranch: env.BRANCH_NAME, type: 'frontend'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
runApprobation ignoreFailure: false, frontendBranch: env.BRANCH_NAME, type: 'frontend'
|
||||
|
||||
@@ -72,7 +72,7 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
|
||||
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
|
||||
|
||||
```bash
|
||||
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
|
||||
export GRAPHQL_SCHEMA_PATH=https://api.n07.testnet.vega.xyz/graphql
|
||||
yarn nx run types:generate
|
||||
```
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=http://localhost:26617
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_ENV=CUSTOM
|
||||
NX_VEGA_CONFIG_URL=
|
||||
|
||||
@@ -18,4 +18,4 @@ NX_EXPLORER_PARTIES=1
|
||||
NX_EXPLORER_VALIDATORS=1
|
||||
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
|
||||
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
|
||||
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ module.exports = defineConfig({
|
||||
},
|
||||
env: {
|
||||
environment: 'CUSTOM',
|
||||
networkQueryUrl: 'http://localhost:3028/query',
|
||||
networkQueryUrl: 'http://localhost:3008/graphql',
|
||||
ethUrl: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
|
||||
commitHash: 'dev',
|
||||
tsConfig: 'tsconfig.json',
|
||||
|
||||
@@ -9,21 +9,22 @@ context('Home Page', function () {
|
||||
it('should show connected environment stats', function () {
|
||||
const statTitles = {
|
||||
0: 'Status',
|
||||
1: 'Block height',
|
||||
2: 'Uptime',
|
||||
3: 'Total nodes',
|
||||
4: 'Total staked',
|
||||
5: 'Backlog',
|
||||
6: 'Trades / second',
|
||||
7: 'Orders / block',
|
||||
8: 'Orders / second',
|
||||
9: 'Transactions / block',
|
||||
10: 'Block time',
|
||||
11: 'Time',
|
||||
12: 'App',
|
||||
13: 'Tendermint',
|
||||
14: 'Up since',
|
||||
15: 'Chain ID',
|
||||
1: 'Epoch',
|
||||
2: 'Block height',
|
||||
3: 'Uptime',
|
||||
4: 'Total nodes',
|
||||
5: 'Total staked',
|
||||
6: 'Backlog',
|
||||
7: 'Trades / second',
|
||||
8: 'Orders / block',
|
||||
9: 'Orders / second',
|
||||
10: 'Transactions / block',
|
||||
11: 'Block time',
|
||||
12: 'Time',
|
||||
13: 'App',
|
||||
14: 'Tendermint',
|
||||
15: 'Up since',
|
||||
16: 'Chain ID',
|
||||
};
|
||||
|
||||
cy.get('[data-testid="stats-title"]')
|
||||
@@ -31,42 +32,13 @@ context('Home Page', function () {
|
||||
cy.wrap($list).should('contain.text', statTitles[index]);
|
||||
})
|
||||
.then(($list) => {
|
||||
cy.wrap($list).should('have.length', 16);
|
||||
cy.wrap($list).should('have.length', 17);
|
||||
});
|
||||
|
||||
cy.get(statsValue).eq(0).should('contain.text', 'CONNECTED');
|
||||
cy.get(statsValue).eq(1).should('not.be.empty');
|
||||
cy.get(statsValue)
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.should('match', /\d+d \d+h \d+m \d+s/i);
|
||||
cy.get(statsValue).eq(3).should('contain.text', '2');
|
||||
cy.get(statsValue)
|
||||
.eq(4)
|
||||
.invoke('text')
|
||||
.should('match', /\d+\.\d\d(?!\d)/i);
|
||||
cy.get(statsValue).eq(5).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(6).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(7).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(8).should('contain.text', '0');
|
||||
cy.get(statsValue).eq(9).should('not.be.empty');
|
||||
cy.get(statsValue).eq(10).should('not.be.empty');
|
||||
cy.get(statsValue).eq(11).should('not.be.empty');
|
||||
cy.get(statsValue)
|
||||
.eq(12)
|
||||
.invoke('text')
|
||||
.should('match', /v\d+\.\d+\.\d+/i);
|
||||
cy.get(statsValue)
|
||||
.eq(13)
|
||||
.invoke('text')
|
||||
.should('match', /\d+\.\d+\.\d+/i);
|
||||
cy.get(statsValue).eq(14).should('not.be.empty');
|
||||
cy.get(statsValue).eq(15).should('not.be.empty');
|
||||
});
|
||||
|
||||
it('Block height should be updating', function () {
|
||||
cy.get(statsValue)
|
||||
.eq(1)
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.then((blockHeightTxt) => {
|
||||
cy.get(statsValue)
|
||||
|
||||
@@ -6,7 +6,7 @@ const customNodeBtn = 'custom-node';
|
||||
context.skip('Node switcher', { tags: '@regression' }, function () {
|
||||
beforeEach('visit home page', function () {
|
||||
cy.intercept('GET', 'https://static.vega.xyz/assets/capsule-network.json', {
|
||||
hosts: ['http://localhost:3028/query'],
|
||||
hosts: ['http://localhost:3008/graphql'],
|
||||
}).as('nodeData');
|
||||
cy.visit('/');
|
||||
cy.wait('@nodeData');
|
||||
|
||||
@@ -219,7 +219,7 @@ context.skip('Parties page', { tags: '@regression' }, function () {
|
||||
balance type asset {id symbol decimals}}}}}}}}';
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `http://localhost:3028/query`,
|
||||
url: `http://localhost:3008/graphql`,
|
||||
body: {
|
||||
query: mutation,
|
||||
},
|
||||
|
||||
@@ -4,7 +4,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
|
||||
@@ -43,7 +43,7 @@ export const AssetLink = ({
|
||||
if (asDialog) {
|
||||
open(assetId, e.target as HTMLElement);
|
||||
} else {
|
||||
navigate(`${Routes.ASSETS}/${asset?.id}`);
|
||||
navigate(`/${Routes.ASSETS}/${asset?.id}`);
|
||||
}
|
||||
}}
|
||||
{...props}
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
SettlementAssetInfoPanel,
|
||||
} from '@vegaprotocol/market-info';
|
||||
import { MarketInfoTable } from '@vegaprotocol/market-info';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
if (!market) return null;
|
||||
@@ -97,22 +96,19 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Oracle'),
|
||||
title: t('Settlement Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel noBorder={false} market={market}>
|
||||
<Link
|
||||
className="text-xs hover:underline"
|
||||
to={`/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id}`}
|
||||
>
|
||||
{t('View settlement data oracle specification')}
|
||||
</Link>
|
||||
<Link
|
||||
className="text-xs hover:underline"
|
||||
to={`/oracles#${market.tradableInstrument.instrument.product.dataSourceSpecForTradingTermination.id}`}
|
||||
>
|
||||
{t('View termination oracle specification')}
|
||||
</Link>
|
||||
</OracleInfoPanel>
|
||||
<OracleInfoPanel
|
||||
noBorder={false}
|
||||
market={market}
|
||||
type="settlementData"
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Termination Oracle'),
|
||||
content: (
|
||||
<OracleInfoPanel noBorder={false} market={market} type="termination" />
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
@@ -3,7 +3,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueGetterParams,
|
||||
|
||||
@@ -9,7 +9,7 @@ import SizeInMarket from '../size-in-market/size-in-market';
|
||||
export interface DeterministicOrderDetailsProps {
|
||||
id: string;
|
||||
// Version to fetch, with 0 being 'latest' and 1 being 'first'. Defaults to 0
|
||||
version?: number;
|
||||
version?: number | null;
|
||||
}
|
||||
|
||||
export const wrapperClasses =
|
||||
@@ -28,7 +28,7 @@ export const wrapperClasses =
|
||||
*/
|
||||
const DeterministicOrderDetails = ({
|
||||
id,
|
||||
version = 0,
|
||||
version = null,
|
||||
}: DeterministicOrderDetailsProps) => {
|
||||
const { data, error } = useExplorerDeterministicOrderQuery({
|
||||
variables: { orderId: id, version },
|
||||
|
||||
@@ -3,7 +3,7 @@ import { VoteProgress } from '@vegaprotocol/proposals';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { AgGridColumn } from 'ag-grid-react';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
|
||||
@@ -71,7 +71,7 @@ export const PartyAccounts = ({ accounts }: PartyAccountsProps) => {
|
||||
/>
|
||||
</td>
|
||||
<td className="text-md">
|
||||
<AssetLink assetId={account.asset.id} />
|
||||
<AssetLink assetId={account.asset.id} asDialog={true} />
|
||||
</td>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
|
||||
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@@ -11,3 +15,43 @@
|
||||
.react-markdown-container a:before {
|
||||
content: '🔗 ';
|
||||
}
|
||||
|
||||
/* AG GRID - Do not edit without updating other global stylesheets for each app */
|
||||
|
||||
.vega-ag-grid .ag-root-wrapper {
|
||||
border: solid 0px;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-cell,
|
||||
.vega-ag-grid .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
/* Light variables */
|
||||
.ag-theme-balham {
|
||||
--ag-background-color: theme(colors.white);
|
||||
--ag-border-color: theme(colors.neutral[300]);
|
||||
--ag-header-background-color: theme(colors.white);
|
||||
--ag-odd-row-background-color: theme(colors.white);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[300]);
|
||||
--ag-row-border-color: theme(colors.white);
|
||||
--ag-row-hover-color: theme(colors.neutral[100]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: theme(colors.black);
|
||||
--ag-border-color: theme(colors.neutral[700]);
|
||||
--ag-header-background-color: theme(colors.black);
|
||||
--ag-odd-row-background-color: theme(colors.black);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
NX_FAIRGROUND=false
|
||||
NX_VEGA_NETWORKS={}
|
||||
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_ETHEREUM_CHAIN_ID=1440
|
||||
NX_ETH_URL_CONNECT=1
|
||||
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
@@ -20,7 +20,7 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
|
||||
#Test configuration variables
|
||||
CYPRESS_FAIRGROUND=false
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
CYPRESS_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
CYPRESS_ETHEREUM_PROVIDER_URL=http://localhost:8545
|
||||
CYPRESS_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
@@ -31,6 +31,5 @@ CYPRESS_VEGA_ENV=CUSTOM
|
||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
||||
CYPRESS_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"name": "Token test market",
|
||||
"code": "Token.24h",
|
||||
"future": {
|
||||
"settlementAsset": "fBTC",
|
||||
"settlementAsset": "73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0",
|
||||
"quoteName": "fBTC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"external": {
|
||||
|
||||
@@ -49,6 +49,7 @@ describe(
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -217,9 +218,9 @@ describe(
|
||||
vegaWalletSetSpecifiedApprovalAmount('1000');
|
||||
createRawProposal();
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
||||
.as('submittedProposal')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
getSubmittedProposalFromProposalList(
|
||||
rawProposal.rationale.title
|
||||
).within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
voteForProposal('for');
|
||||
// 3001-VOTE-079
|
||||
@@ -237,9 +238,9 @@ describe(
|
||||
);
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getSubmittedProposalFromProposalList(rawProposal.rationale.title)
|
||||
.as('submittedProposal')
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
getSubmittedProposalFromProposalList(
|
||||
rawProposal.rationale.title
|
||||
).within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
cy.get(proposalVoteProgressForPercentage)
|
||||
.contains('100.00%')
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-teardown.functions';
|
||||
|
||||
const proposalListItem = '[data-testid="proposals-list-item"]';
|
||||
const closedProposals = '[data-testid="closed-proposals"]';
|
||||
const proposalStatus = '[data-testid="proposal-status"]';
|
||||
const viewProposalButton = '[data-testid="view-proposal-btn"]';
|
||||
@@ -35,6 +36,7 @@ context(
|
||||
});
|
||||
|
||||
beforeEach('visit proposals', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -51,7 +53,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(closedProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalStatus).should('have.text', 'Enacted ');
|
||||
cy.get(viewProposalButton).click();
|
||||
@@ -78,7 +81,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
getProposalInformationFromTable('State')
|
||||
@@ -111,7 +115,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(openProposals, { timeout: 6000 }).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
getProposalInformationFromTable('State')
|
||||
@@ -133,7 +138,8 @@ context(
|
||||
waitForSpinner();
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil('[data-testid="proposals-list-item"]')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.get(viewProposalButton).click());
|
||||
});
|
||||
getProposalInformationFromTable('State')
|
||||
|
||||
@@ -84,6 +84,7 @@ context(
|
||||
});
|
||||
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
||||
@@ -50,6 +50,8 @@ const liquidityVoteStatus = 'liquidity-votes-status';
|
||||
const tokenVoteStatus = 'token-votes-status';
|
||||
const proposalTermsSection = 'proposal';
|
||||
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
|
||||
const fUSDCId =
|
||||
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc';
|
||||
const epochTimeout = Cypress.env('epochTimeout');
|
||||
const proposalTimeout = { timeout: 14000 };
|
||||
|
||||
@@ -74,6 +76,7 @@ context(
|
||||
});
|
||||
|
||||
beforeEach('visit governance tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -264,7 +267,7 @@ context(
|
||||
it('Unable to submit update market proposal without minimum amount of tokens', function () {
|
||||
vegaWalletTeardown();
|
||||
vegaWalletFaucetAssetsWithoutCheck(
|
||||
'fUSDC',
|
||||
fUSDCId,
|
||||
'1000000',
|
||||
vegaWalletPublicKey
|
||||
);
|
||||
@@ -290,7 +293,7 @@ context(
|
||||
// 3001-VOTE-092 3004-PMAC-001
|
||||
it('Able to submit update market proposal and vote for proposal', function () {
|
||||
vegaWalletFaucetAssetsWithoutCheck(
|
||||
'fUSDC',
|
||||
fUSDCId,
|
||||
'1000000',
|
||||
vegaWalletPublicKey
|
||||
);
|
||||
@@ -302,7 +305,10 @@ context(
|
||||
cy.get('dd').eq(0).should('have.text', 'Test market 1');
|
||||
cy.get('dd').eq(1).should('have.text', 'TEST.24h');
|
||||
cy.get('dd').eq(2).should('not.be.empty');
|
||||
cy.get('dd').eq(2).invoke('text').as('EnactedMarketId');
|
||||
cy.get('dd')
|
||||
.eq(2)
|
||||
.invoke('text')
|
||||
.as('EnactedMarketId', { type: 'static' });
|
||||
});
|
||||
cy.get('@EnactedMarketId').then((marketId) => {
|
||||
cy.VegaWalletSubmitLiquidityProvision(String(marketId), '1');
|
||||
@@ -320,6 +326,7 @@ context(
|
||||
cy.get('@EnactedMarketId').then((marketId) => {
|
||||
cy.contains(String(marketId))
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
@@ -373,16 +380,19 @@ context(
|
||||
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');
|
||||
cy.getByTestId('dialog-content').within(() => {
|
||||
cy.get('p').should(
|
||||
'have.text',
|
||||
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('p').should(
|
||||
'have.text',
|
||||
'PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE'
|
||||
);
|
||||
});
|
||||
closeDialog();
|
||||
navigateTo(navigation.proposals);
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
@@ -420,6 +430,7 @@ context(
|
||||
cy.get(proposalType)
|
||||
.contains('Update asset')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalDetails).should('contain.text', assetId); // 3001-VOTE-029
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
|
||||
@@ -35,6 +35,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
});
|
||||
|
||||
beforeEach('visit proposals tab', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -59,7 +60,10 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
cy.get(openProposals).within(() => {
|
||||
cy.get(proposalClosingDate).first().should('contain.text', 'year');
|
||||
cy.get(proposalClosingDate).should('contain.text', 'months');
|
||||
cy.get(proposalClosingDate).last().should('contain.text', 'days');
|
||||
cy.get(proposalClosingDate)
|
||||
.last()
|
||||
.invoke('text')
|
||||
.should('match', /days|minutes/);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,9 +25,10 @@ const rewardsTimeOut = { timeout: 60000 };
|
||||
|
||||
context('rewards - flow', { tags: '@slow' }, function () {
|
||||
before('set up environment to allow rewards', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
depositAsset(vegaAssetAddress, '1000');
|
||||
depositAsset(vegaAssetAddress, '1000', 18);
|
||||
cy.validatorsSelfDelegate();
|
||||
ethereumWalletConnect();
|
||||
cy.connectVegaWallet();
|
||||
@@ -56,7 +57,7 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
cy.getByTestId(rewardsTable)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('asset').should('have.text', 'Vega');
|
||||
cy.getByTestId('asset', rewardsTimeOut).should('have.text', 'Vega');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD').should('have.text', '1');
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE').should(
|
||||
'have.text',
|
||||
@@ -93,13 +94,13 @@ context('rewards - flow', { tags: '@slow' }, function () {
|
||||
.within(() => {
|
||||
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.1177')
|
||||
.and('contain.text', '(11.7733%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut)
|
||||
.should('contain.text', '0.4415')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')
|
||||
.should('contain.text', '0.0001')
|
||||
.and('contain.text', '(11.7733%)');
|
||||
cy.getByTestId('total').should('have.text', '0.1179');
|
||||
.should('contain.text', '0.0004')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('total').should('have.text', '0.4419');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,7 +55,6 @@ context(
|
||||
function () {
|
||||
// 2001-STKE-002, 2001-STKE-032
|
||||
before('visit staking tab and connect vega wallet', function () {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.visit('/');
|
||||
ethereumWalletConnect();
|
||||
// this is a workaround for #2422 which can be removed once issue is resolved
|
||||
@@ -67,6 +66,7 @@ context(
|
||||
beforeEach(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
|
||||
@@ -53,6 +53,7 @@ context(
|
||||
beforeEach(
|
||||
'teardown wallet & drill into a specific validator',
|
||||
function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.connectVegaWallet();
|
||||
@@ -90,9 +91,14 @@ context(
|
||||
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
@@ -128,26 +134,38 @@ context(
|
||||
stakingPageAssociateTokens('1001', { approve: true });
|
||||
verifyEthWalletAssociatedBalance('1,001.00');
|
||||
verifyEthWalletTotalAssociatedBalance('1,001.00');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
'1,001.00'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate a partial amount of tokens currently associated', function () {
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
stakingPageDisassociateTokens('1');
|
||||
verifyEthWalletAssociatedBalance('1.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
1.0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to disassociate all tokens - using max', function () {
|
||||
@@ -155,26 +173,40 @@ context(
|
||||
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.';
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.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);
|
||||
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);
|
||||
});
|
||||
cy.get(ethWalletContainer)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(ethWalletContainer)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
|
||||
'not.exist'
|
||||
);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
0.0
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Able to associate and disassociate vesting contract tokens', function () {
|
||||
@@ -200,9 +232,14 @@ context(
|
||||
cy.getByTestId('currency-title', txTimeout).should('have.length', 6);
|
||||
verifyEthWalletAssociatedBalance('2.0');
|
||||
verifyEthWalletTotalAssociatedBalance('2.0');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
|
||||
stakingPageDisassociateTokens('1', {
|
||||
type: 'contract',
|
||||
@@ -228,38 +265,61 @@ context(
|
||||
stakingPageAssociateTokens('21', { type: 'wallet' });
|
||||
cy.get('button').contains('Select a validator to nominate').click();
|
||||
stakingPageAssociateTokens('37', { type: 'contract' });
|
||||
cy.get(vestingContractSection).within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 37);
|
||||
});
|
||||
cy.get(vegaInWalletSection).within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
|
||||
});
|
||||
cy.get(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 37);
|
||||
});
|
||||
cy.get(vegaInWalletSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedKey).should(
|
||||
'contain',
|
||||
Cypress.env('vegaWalletPublicKeyShort')
|
||||
);
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 21);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
58
|
||||
);
|
||||
});
|
||||
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.get(vestingContractSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 31);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
52
|
||||
);
|
||||
});
|
||||
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);
|
||||
});
|
||||
cy.get(vegaInWalletSection)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(associatedAmount, txTimeout).should('contain', 12);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
43
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('Not able to associate more tokens than owned', function () {
|
||||
@@ -318,9 +378,14 @@ context(
|
||||
Cypress.env('vegaWalletPublicKey2')
|
||||
);
|
||||
stakingPageAssociateTokens('2');
|
||||
cy.get(vegaWallet).within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
|
||||
});
|
||||
cy.get(vegaWallet)
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
|
||||
'contain',
|
||||
2.0
|
||||
);
|
||||
});
|
||||
cy.get(associateCompleteText).should(
|
||||
'have.text',
|
||||
`Vega key ${Cypress.env(
|
||||
|
||||
@@ -4,10 +4,7 @@ import {
|
||||
waitForSpinner,
|
||||
} from '../../support/common.functions';
|
||||
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
|
||||
import {
|
||||
depositAsset,
|
||||
vegaWalletTeardown,
|
||||
} from '../../support/wallet-teardown.functions';
|
||||
import { depositAsset } from '../../support/wallet-teardown.functions';
|
||||
|
||||
const withdraw = 'withdraw';
|
||||
const withdrawalForm = 'withdraw-form';
|
||||
@@ -25,6 +22,13 @@ const withdrawalAmount = 'withdrawal-amount';
|
||||
const withdrawalRecipient = 'withdrawal-recipient';
|
||||
const withdrawFundsButton = 'withdraw-funds';
|
||||
const completeWithdrawalButton = 'complete-withdrawal';
|
||||
const tableTxHash = '[col-id="txHash"]';
|
||||
const tableAssetSymbol = '[col-id="asset.symbol"]';
|
||||
const tableAmount = '[col-id="amount"]';
|
||||
const tableReceiverAddress = '[col-id="details.receiverAddress"]';
|
||||
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
|
||||
const tableWithdrawnStatus = '[col-id="status"]';
|
||||
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
|
||||
const usdtName = 'USDC (local)';
|
||||
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
|
||||
const usdcSymbol = 'tUSDC';
|
||||
@@ -42,26 +46,23 @@ context(
|
||||
cy.visit('/');
|
||||
// When running tests locally, will fail if run without restarting capsule
|
||||
cy.updateCapsuleMultiSig().then(() => {
|
||||
depositAsset(usdcEthAddress, '100');
|
||||
ethereumWalletConnect();
|
||||
depositAsset(usdcEthAddress, '1000', 5);
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach('Navigate to withdrawal page', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
navigateTo(navigation.withdraw);
|
||||
cy.connectVegaWallet();
|
||||
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(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').find('option').should('have.length.at.least', 2);
|
||||
cy.getByTestId(ethAddressInput).should('be.visible');
|
||||
cy.getByTestId(amountInput).should('be.visible');
|
||||
@@ -70,7 +71,7 @@ context(
|
||||
|
||||
it('Unable to submit withdrawal with invalid fields', function () {
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
@@ -94,7 +95,7 @@ context(
|
||||
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(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(balanceAvailable, txTimeout).should('exist');
|
||||
cy.getByTestId(withdrawalThreshold).should(
|
||||
@@ -102,7 +103,7 @@ context(
|
||||
'100,000.00000T'
|
||||
);
|
||||
cy.getByTestId(delayTime).should('have.text', 'None');
|
||||
cy.getByTestId(amountInput).click().type('100');
|
||||
cy.getByTestId(amountInput).click().type('120');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
});
|
||||
|
||||
@@ -116,7 +117,7 @@ context(
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/txs/');
|
||||
cy.getByTestId(withdrawalAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.getByTestId(withdrawalAmount).should('have.text', '100.00');
|
||||
cy.getByTestId(withdrawalAmount).should('have.text', '120.00');
|
||||
cy.getByTestId(withdrawalRecipient)
|
||||
.should('have.text', truncatedWithdrawalEthAddress)
|
||||
.and('have.attr', 'href')
|
||||
@@ -128,26 +129,20 @@ context(
|
||||
'Withdraw asset complete'
|
||||
);
|
||||
cy.getByTestId(dialogClose).click();
|
||||
|
||||
// need to reload page to see withdrawal history complete
|
||||
cy.reload();
|
||||
waitForAssetsDisplayed(usdtName);
|
||||
|
||||
// withdrawal history for complete withdrawal displayed
|
||||
cy.get('[col-id="txHash"]', txTimeout)
|
||||
.should('have.length.above', 1)
|
||||
.eq(1)
|
||||
cy.get(tableWithdrawnStatus)
|
||||
.eq(1, txTimeout)
|
||||
.should('have.text', 'Completed')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', usdcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '100.00');
|
||||
cy.get('[col-id="details.receiverAddress"]')
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '120.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get('[col-id="withdrawnTimestamp"]').should('not.be.empty');
|
||||
cy.get('[col-id="status"]').should('have.text', 'Completed');
|
||||
cy.get('[col-id="txHash"]')
|
||||
cy.get(tableWithdrawnTimeStamp).should('not.be.empty');
|
||||
cy.get(tableTxHash)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/tx/');
|
||||
@@ -155,16 +150,16 @@ context(
|
||||
});
|
||||
|
||||
// Skipping because of bug #1857
|
||||
it.skip('Able to withdraw asset: -eth wallet not connected', function () {
|
||||
it('Able to withdraw asset: -eth wallet not connected', function () {
|
||||
const ethWalletAddress = Cypress.env('ethWalletPublicKey');
|
||||
cy.reload();
|
||||
waitForAssetsDisplayed(usdtName);
|
||||
// fill in withdrawal form
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).within(() => {
|
||||
cy.get('select').select(usdtSelectValue, { force: true });
|
||||
cy.getByTestId(ethAddressInput).should('be.empty');
|
||||
cy.getByTestId(amountInput).click().type('100');
|
||||
cy.getByTestId(amountInput).click().type('110');
|
||||
cy.getByTestId(submitWithdrawalButton).click();
|
||||
|
||||
// Need eth address to submit withdrawal
|
||||
@@ -180,20 +175,20 @@ context(
|
||||
'Transaction complete'
|
||||
);
|
||||
cy.getByTestId(dialogClose).click();
|
||||
|
||||
cy.getByTestId(completeWithdrawalButton)
|
||||
.eq(0)
|
||||
.parent()
|
||||
cy.get(tableTxHash)
|
||||
.eq(1)
|
||||
.should('have.text', 'Complete withdrawal')
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get('[col-id="asset.symbol"]').should('have.text', usdcSymbol);
|
||||
cy.get('[col-id="amount"]').should('have.text', '100.00');
|
||||
cy.get('[col-id="details.receiverAddress"]')
|
||||
cy.get(tableAssetSymbol).should('have.text', usdcSymbol);
|
||||
cy.get(tableAmount).should('have.text', '110.00');
|
||||
cy.get(tableReceiverAddress)
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://sepolia.etherscan.io/address/');
|
||||
cy.get('[col-id="createdTimestamp"]').should('not.be.empty');
|
||||
cy.get(tableCreatedTimeStamp).should('not.be.empty');
|
||||
cy.getByTestId(completeWithdrawalButton).click();
|
||||
// Unable to complete withdrawal in Capsule
|
||||
});
|
||||
});
|
||||
|
||||
@@ -202,26 +197,28 @@ context(
|
||||
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.`;
|
||||
|
||||
// Disconnect vega wallet
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('manage-vega-wallet').last().click();
|
||||
cy.getByTestId('disconnect').click();
|
||||
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
cy.getByTestId(withdraw).should('be.visible').click();
|
||||
cy.getByTestId(withdrawalForm).within(() => {
|
||||
cy.getByTestId(withdrawalForm, txTimeout).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');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('h1').should('have.text', 'Transaction failed');
|
||||
cy.getByTestId('Error').should('have.text', expectedErrorTxt);
|
||||
});
|
||||
});
|
||||
|
||||
function waitForAssetsDisplayed(expectedAsset: string) {
|
||||
cy.contains(expectedAsset, txTimeout).should('be.visible');
|
||||
cy.getByTestId('currency-title').should('contain.text', expectedAsset);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -131,7 +131,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
|
||||
.within(() => {
|
||||
cy.get('span')
|
||||
.first()
|
||||
.should('have.text', 'http://localhost:3028/query');
|
||||
.should('have.text', 'http://localhost:3008/graphql');
|
||||
cy.getByTestId('link').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,10 +17,15 @@ const banner = 'view-banner';
|
||||
|
||||
context('View functionality with public key', { tags: '@smoke' }, function () {
|
||||
before('send asset to wallet', function () {
|
||||
vegaWalletFaucetAssetsWithoutCheck('fUSDC', '1000000', vegaWalletPubKey);
|
||||
vegaWalletFaucetAssetsWithoutCheck(
|
||||
'816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc',
|
||||
'1000000',
|
||||
vegaWalletPubKey
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach('visit home page', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
waitForSpinner();
|
||||
cy.connectPublicKey(vegaWalletPubKey);
|
||||
|
||||
@@ -13,6 +13,7 @@ context(
|
||||
{ tags: '@regression' },
|
||||
function () {
|
||||
before('navigate to rewards page', function () {
|
||||
cy.clearLocalStorage();
|
||||
cy.visit('/');
|
||||
navigateTo(navigation.rewards);
|
||||
});
|
||||
|
||||
@@ -282,26 +282,30 @@ context(
|
||||
describe('Vega wallet with assets', function () {
|
||||
const assets = [
|
||||
{
|
||||
id: 'fUSDC',
|
||||
id: '816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc',
|
||||
name: 'USDC (fake)',
|
||||
symbol: 'fUSDC',
|
||||
amount: '1000000',
|
||||
expectedAmount: '10.00',
|
||||
},
|
||||
{
|
||||
id: 'fDAI',
|
||||
id: '8566db7257222b5b7ef2886394ad28b938b28680a54a169bbc795027b89d6665',
|
||||
name: 'DAI (fake)',
|
||||
symbol: 'fDAI',
|
||||
amount: '200000',
|
||||
expectedAmount: '2.00',
|
||||
},
|
||||
{
|
||||
id: 'fBTC',
|
||||
id: '73174a6fb1d5802ba0ac7bd7ab79e0a3a4837b262de0a4e80815a55442692bd0',
|
||||
name: 'BTC (fake)',
|
||||
symbol: 'fBTC',
|
||||
amount: '600000',
|
||||
expectedAmount: '6.00',
|
||||
},
|
||||
{
|
||||
id: 'fEURO',
|
||||
id: 'e02d4c15d790d1d2dffaf2dcd1cf06a1fe656656cf4ed18c8ce99f9e83643567',
|
||||
name: 'EURO (fake)',
|
||||
symbol: 'fEURO',
|
||||
amount: '800000',
|
||||
expectedAmount: '8.00',
|
||||
},
|
||||
@@ -322,15 +326,15 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
for (const { id, name, expectedAmount } of assets) {
|
||||
it(`should see ${id} within vega wallet`, () => {
|
||||
for (const { name, symbol, expectedAmount } of assets) {
|
||||
it(`should see ${name} within vega wallet`, () => {
|
||||
cy.get(walletContainer).within(() => {
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
.contains(id, txTimeout)
|
||||
.contains(name, txTimeout)
|
||||
.should('be.visible');
|
||||
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
.contains(id)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.siblings()
|
||||
.invoke('text')
|
||||
@@ -338,9 +342,9 @@ context(
|
||||
.should('be.gte', parseFloat(expectedAmount));
|
||||
|
||||
cy.get(vegaWalletCurrencyTitle)
|
||||
.contains(id)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.contains(name);
|
||||
.contains(symbol);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ export function getSubmittedProposalFromProposalList(proposalTitle: string) {
|
||||
export function getProposalIdFromList(proposalTitle: string) {
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get(proposalDetails)
|
||||
.invoke('text')
|
||||
@@ -133,7 +134,7 @@ export function waitForProposalSync() {
|
||||
// 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) => {
|
||||
cy.intercept('POST', '/graphql', (req) => {
|
||||
if (req.body.operationName === 'Delegations') {
|
||||
req.alias = 'proposalDelegationsCompletion';
|
||||
}
|
||||
@@ -143,7 +144,7 @@ export function waitForProposalSync() {
|
||||
cy.wait(['@proposalDelegationsCompletion', '@proposalDelegationsCompletion']);
|
||||
|
||||
// Turn off this intercept from here on in
|
||||
cy.intercept('POST', '/query', (req) => {
|
||||
cy.intercept('POST', '/graphql', (req) => {
|
||||
if (req.body.operationName === 'Delegations') {
|
||||
req.continue();
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ export function stakingValidatorPageRemoveStake(stake: string) {
|
||||
.and('contain', `Remove ${stake} $VEGA tokens at the end of epoch`)
|
||||
.and('be.visible')
|
||||
.click();
|
||||
cy.contains('been removed from validator', txTimeout).should('be.visible');
|
||||
closeDialog();
|
||||
}
|
||||
|
||||
@@ -184,7 +185,7 @@ export function validateValidatorListTotalStakeAndShare(
|
||||
) {
|
||||
cy.contains('Loading...', epochTimeout).should('not.exist');
|
||||
waitForBeginningOfEpoch();
|
||||
cy.get(`[row-id="${positionOnList}"]:visible`)
|
||||
cy.get(`[row-id="${positionOnList}"]`)
|
||||
.eq(1)
|
||||
.within(() => {
|
||||
cy.getByTestId(stakeValidatorListTotalStake, epochTimeout).should(
|
||||
@@ -220,12 +221,12 @@ export function ensureSpecifiedUnstakedTokensAreAssociated(
|
||||
}
|
||||
|
||||
export function closeStakingDialog() {
|
||||
cy.get('[data-testid="dialog-title"]:visible').should(
|
||||
cy.getByTestId('dialog-title').should(
|
||||
'contain.text',
|
||||
'At the beginning of the next epoch'
|
||||
);
|
||||
cy.get('[data-testid="dialog-content"]:visible')
|
||||
.first()
|
||||
cy.getByTestId('dialog-content')
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.get('a').should('have.text', 'Back to Staking').click();
|
||||
});
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
import { ethers, Wallet } from 'ethers';
|
||||
|
||||
const associatedAmountInWallet = '[data-testid="associated-amount"]:visible';
|
||||
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]:visible';
|
||||
const vegaWalletContainer = 'aside [data-testid="vega-wallet"]';
|
||||
const vegaWalletMnemonic = Cypress.env('vegaWalletMnemonic');
|
||||
const vegaWalletPubKey = Cypress.env('vegaWalletPublicKey');
|
||||
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
|
||||
@@ -35,18 +35,25 @@ const stakingBridgeContract = new StakingBridge(
|
||||
);
|
||||
const vestingContract = new TokenVesting(vegaTokenContractAddress, signer);
|
||||
|
||||
export async function depositAsset(assetEthAddress: string, amount: string) {
|
||||
export async function depositAsset(
|
||||
assetEthAddress: string,
|
||||
amount: string,
|
||||
decimalPlaces: number
|
||||
) {
|
||||
// Approve asset
|
||||
const faucet = new TokenFaucetable(assetEthAddress, signer);
|
||||
cy.wrap(faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(19)), {
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}).then(() => {
|
||||
cy.wrap(
|
||||
faucet.approve(Erc20BridgeAddress, amount + '0'.repeat(decimalPlaces + 1)),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
log: false,
|
||||
}
|
||||
).then(() => {
|
||||
const collateralBridge = new CollateralBridge(Erc20BridgeAddress, signer);
|
||||
cy.wrap(
|
||||
collateralBridge.deposit_asset(
|
||||
assetEthAddress,
|
||||
amount + '0'.repeat(18),
|
||||
amount + '0'.repeat(decimalPlaces),
|
||||
'0x' + vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
@@ -102,11 +109,12 @@ 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 }
|
||||
{ timeout: transactionTimeout }
|
||||
).then((stakeBalance) => {
|
||||
if (Number(stakeBalance) != 0) {
|
||||
cy.get(vegaWalletContainer).within(() => {
|
||||
cy.getByTestId('currency-value')
|
||||
.first()
|
||||
.invoke('text')
|
||||
.then(($associatedAmount) => {
|
||||
cy.wrap(
|
||||
@@ -114,14 +122,34 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
|
||||
String(stakeBalance),
|
||||
vegaWalletPubKey
|
||||
),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
{ timeout: transactionTimeout }
|
||||
);
|
||||
cy.get("[data-testid='currency-value']")
|
||||
.first()
|
||||
.invoke('text', {
|
||||
cy.wrap(
|
||||
vestingContract.stake_balance(ethWalletPubKey, vegaWalletPubKey),
|
||||
{
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('not.eq', $associatedAmount);
|
||||
log: false,
|
||||
}
|
||||
).then((vestingAmount) => {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.contains('Associated', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.parent()
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.getByTestId('currency-value', {
|
||||
timeout: transactionTimeout,
|
||||
})
|
||||
.should('have.length', 1)
|
||||
.invoke('text')
|
||||
.as('displayedAmount');
|
||||
cy.get('@displayedAmount', {
|
||||
timeout: transactionTimeout,
|
||||
}).should('not.eq', $associatedAmount);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -137,7 +165,7 @@ async function vegaWalletTeardownVesting(vestingContract: TokenVesting) {
|
||||
if (Number(vestingAmount) != 0) {
|
||||
cy.wrap(
|
||||
vestingContract.remove_stake(String(vestingAmount), vegaWalletPubKey),
|
||||
{ timeout: transactionTimeout, log: false }
|
||||
{ timeout: transactionTimeout }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -8,7 +8,7 @@ NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
|
||||
NX_VEGA_CONFIG_URL=''
|
||||
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_ETHEREUM_CHAIN_ID=1440
|
||||
NX_ETH_URL_CONNECT=1
|
||||
NX_ETH_WALLET_MNEMONIC=ozone access unlock valid olympic save include omit supply green clown session
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_URL=https://api.n08.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
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_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
|
||||
@@ -3,7 +3,7 @@ import './i18n';
|
||||
import React, { useEffect } from 'react';
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Integrations } from '@sentry/tracing';
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { BrowserRouter as Router, useLocation } from 'react-router-dom';
|
||||
import { AppLoader } from './app-loader';
|
||||
import { NetworkInfo } from '@vegaprotocol/network-info';
|
||||
import { BalanceManager } from './components/balance-manager';
|
||||
@@ -149,6 +149,20 @@ const Web3Container = ({
|
||||
);
|
||||
};
|
||||
|
||||
const ScrollToTop = () => {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
useEffect(() => {
|
||||
// "document.documentElement.scrollTo" is the magic for React Router Dom v6
|
||||
document.documentElement.scrollTo({
|
||||
top: 0,
|
||||
left: 0,
|
||||
});
|
||||
}, [pathname]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
const AppContainer = () => {
|
||||
const { config, loading, error } = useEthereumConfig();
|
||||
const { VEGA_ENV, GIT_COMMIT_HASH, GIT_BRANCH, ETHEREUM_PROVIDER_URL } =
|
||||
@@ -183,6 +197,7 @@ const AppContainer = () => {
|
||||
|
||||
return (
|
||||
<Router>
|
||||
<ScrollToTop />
|
||||
<AppStateProvider>
|
||||
<div className="grid min-h-full text-white">
|
||||
<AsyncRenderer<EthereumConfig | null>
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { DisconnectedNotice } from './disconnected-notice';
|
||||
|
||||
describe('DisconnectedNotice', () => {
|
||||
it('renders Notification when isDisconnected is true and correctNetworkChainId is valid', () => {
|
||||
render(
|
||||
<DisconnectedNotice isDisconnected={true} correctNetworkChainId={'1'} />
|
||||
);
|
||||
const disconnectedNotice = screen.getByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render Notification when isDisconnected is false", () => {
|
||||
render(
|
||||
<DisconnectedNotice isDisconnected={false} correctNetworkChainId={'1'} />
|
||||
);
|
||||
const disconnectedNotice = screen.queryByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render Notification when correctNetworkChainId is undefined", () => {
|
||||
render(
|
||||
<DisconnectedNotice
|
||||
isDisconnected={true}
|
||||
correctNetworkChainId={undefined}
|
||||
/>
|
||||
);
|
||||
const disconnectedNotice = screen.queryByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("doesn't render Notification when correctNetworkChainId is null", () => {
|
||||
render(
|
||||
<DisconnectedNotice isDisconnected={true} correctNetworkChainId={null} />
|
||||
);
|
||||
const disconnectedNotice = screen.queryByTestId('disconnected-notice');
|
||||
expect(disconnectedNotice).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Intent, Notification } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface DisconnectedNoticeProps {
|
||||
isDisconnected: boolean;
|
||||
correctNetworkChainId?: string | null;
|
||||
}
|
||||
|
||||
export const DisconnectedNotice = ({
|
||||
isDisconnected,
|
||||
correctNetworkChainId,
|
||||
}: DisconnectedNoticeProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (
|
||||
!isDisconnected ||
|
||||
correctNetworkChainId === undefined ||
|
||||
correctNetworkChainId === null
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="col-span-full" data-testid="disconnected-notice">
|
||||
<Notification
|
||||
message={t('disconnectedNotice', {
|
||||
correctNetwork: correctNetworkChainId,
|
||||
})}
|
||||
intent={Intent.Danger}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export * from './disconnected-notice';
|
||||
@@ -3,6 +3,7 @@ import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { DisconnectedNotice } from '../disconnected-notice';
|
||||
|
||||
import {
|
||||
AppStateActionType,
|
||||
@@ -26,7 +27,8 @@ import {
|
||||
import { Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import colors from 'tailwindcss/colors';
|
||||
import { useBalances } from '../../lib/balances/balances-store';
|
||||
import { useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
import { useEthereumConfig, useWeb3Disconnect } from '@vegaprotocol/web3';
|
||||
import { getChainName } from '@vegaprotocol/web3';
|
||||
|
||||
const removeLeadingAddressSymbol = (key: string) => {
|
||||
if (key && key.length > 2 && key.slice(0, 2) === '0x') {
|
||||
@@ -182,16 +184,21 @@ const ConnectedKey = () => {
|
||||
|
||||
export const EthWallet = () => {
|
||||
const { t } = useTranslation();
|
||||
const { appDispatch } = useAppState();
|
||||
const { appDispatch, appState } = useAppState();
|
||||
const { account, connector } = useWeb3React();
|
||||
const pendingTxs = usePendingTransactions();
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
const { config } = useEthereumConfig();
|
||||
|
||||
return (
|
||||
<WalletCard>
|
||||
<section data-testid="ethereum-wallet">
|
||||
<WalletCardHeader>
|
||||
<h1 className="m-0 uppercase">{t('ethereumKey')}</h1>
|
||||
<DisconnectedNotice
|
||||
isDisconnected={appState.disconnectNotice}
|
||||
correctNetworkChainId={getChainName(Number(config?.chain_id))}
|
||||
/>
|
||||
{account && (
|
||||
<div className="place-self-end font-mono">
|
||||
<div
|
||||
|
||||
@@ -14,7 +14,7 @@ export const AppLayout = ({ children }: AppLayoutProps) => {
|
||||
const { isReadOnly } = useVegaWallet();
|
||||
const AppLayoutClasses = classNames(
|
||||
'app w-full max-w-[1500px] mx-auto grid',
|
||||
'lg:text-body-large',
|
||||
'font-alpha lg:text-body-large',
|
||||
{
|
||||
'grid-rows-[repeat(2,min-content)_1fr_min-content]': !isReadOnly,
|
||||
'grid-rows-[repeat(3,min-content)_1fr_min-content]': isReadOnly,
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
getChainName,
|
||||
useWeb3ConnectStore,
|
||||
useWeb3Disconnect,
|
||||
Web3ConnectDialog,
|
||||
} from '@vegaprotocol/web3';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
@@ -36,9 +35,7 @@ export function Web3Connector({
|
||||
const appChainId = Number(chainId);
|
||||
return (
|
||||
<>
|
||||
<Web3Content appChainId={appChainId} setDialogOpen={setDialogOpen}>
|
||||
{children}
|
||||
</Web3Content>
|
||||
<Web3Content appChainId={appChainId}>{children}</Web3Content>
|
||||
<Web3ConnectDialog
|
||||
connectors={connectors}
|
||||
dialogOpen={appState.ethConnectOverlay}
|
||||
@@ -52,23 +49,59 @@ export function Web3Connector({
|
||||
interface Web3ContentProps {
|
||||
children: ReactElement;
|
||||
appChainId: number;
|
||||
setDialogOpen: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
const { connector, chainId } = useWeb3React();
|
||||
const [previousChainId, setPreviousChainId] = useState(chainId);
|
||||
const error = useWeb3ConnectStore((store) => store.error);
|
||||
const disconnect = useWeb3Disconnect(connector);
|
||||
|
||||
const showDisconnectNotice = useCallback(
|
||||
(isVisible: boolean) =>
|
||||
appDispatch({
|
||||
type: AppStateActionType.SET_DISCONNECT_NOTICE,
|
||||
isVisible,
|
||||
}),
|
||||
[appDispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (connector?.connectEagerly) {
|
||||
connector.connectEagerly();
|
||||
}
|
||||
// wallet connect doesnt handle connectEagerly being called when connector is also in the
|
||||
// wallet connect doesn't handle connectEagerly being called when connector is also in the
|
||||
// deps array.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (chainId !== undefined) {
|
||||
// We use this to detect when the user switches networks.
|
||||
setPreviousChainId(chainId);
|
||||
|
||||
if (chainId !== appChainId) {
|
||||
disconnect();
|
||||
|
||||
// If the user was previously connected, show the disconnect explanation notice.
|
||||
if (previousChainId !== undefined && !appState.disconnectNotice) {
|
||||
showDisconnectNotice(true);
|
||||
}
|
||||
} else if (appState.disconnectNotice) {
|
||||
showDisconnectNotice(false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
appChainId,
|
||||
appDispatch,
|
||||
appState.disconnectNotice,
|
||||
chainId,
|
||||
disconnect,
|
||||
previousChainId,
|
||||
showDisconnectNotice,
|
||||
]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<Splash>
|
||||
@@ -80,18 +113,5 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
);
|
||||
}
|
||||
|
||||
if (chainId !== undefined && chainId !== appChainId) {
|
||||
return (
|
||||
<Splash>
|
||||
<div className="flex flex-col items-center gap-12">
|
||||
<p className="text-white">
|
||||
This app only works on {getChainName(appChainId)}
|
||||
</p>
|
||||
<Button onClick={() => disconnect()}>Disconnect</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
@@ -43,6 +43,11 @@ export interface AppState {
|
||||
* Message to display in a banner at the top of the screen, currently always shown as a warning/error
|
||||
*/
|
||||
bannerMessage: string;
|
||||
/**
|
||||
* Displays a notice to the user that they have been disconnected because they've changed their
|
||||
* ethereum network to an incompatible one.
|
||||
*/
|
||||
disconnectNotice: boolean;
|
||||
}
|
||||
|
||||
export enum AppStateActionType {
|
||||
@@ -58,6 +63,7 @@ export enum AppStateActionType {
|
||||
SET_ASSOCIATION_BREAKDOWN,
|
||||
SET_TRANSACTION_OVERLAY,
|
||||
SET_BANNER_MESSAGE,
|
||||
SET_DISCONNECT_NOTICE,
|
||||
}
|
||||
|
||||
export type AppStateAction =
|
||||
@@ -90,6 +96,10 @@ export type AppStateAction =
|
||||
| {
|
||||
type: AppStateActionType.SET_BANNER_MESSAGE;
|
||||
message: string;
|
||||
}
|
||||
| {
|
||||
type: AppStateActionType.SET_DISCONNECT_NOTICE;
|
||||
isVisible: boolean;
|
||||
};
|
||||
|
||||
type AppStateContextShape = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react';
|
||||
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { AppStateActionType, AppStateContext } from './app-state-context';
|
||||
import type { AppState, AppStateAction } from './app-state-context';
|
||||
import { AppStateActionType, AppStateContext } from './app-state-context';
|
||||
|
||||
interface AppStateProviderProps {
|
||||
children: React.ReactNode;
|
||||
@@ -19,6 +19,7 @@ const initialAppState: AppState = {
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
};
|
||||
|
||||
function appStateReducer(state: AppState, action: AppStateAction): AppState {
|
||||
@@ -68,6 +69,12 @@ function appStateReducer(state: AppState, action: AppStateAction): AppState {
|
||||
bannerMessage: action.message,
|
||||
};
|
||||
}
|
||||
case AppStateActionType.SET_DISCONNECT_NOTICE: {
|
||||
return {
|
||||
...state,
|
||||
disconnectNotice: action.isVisible,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,13 +14,17 @@ import { ContractsContext } from './contracts-context';
|
||||
import { createDefaultProvider } from '../../lib/web3-connectors';
|
||||
import { useEthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ENV } from '../../config/env';
|
||||
import { ENV } from '../../config';
|
||||
|
||||
/**
|
||||
* Provides Vega Ethereum contract instances to its children.
|
||||
*/
|
||||
export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
const { provider: activeProvider, account } = useWeb3React();
|
||||
const {
|
||||
provider: activeProvider,
|
||||
account,
|
||||
chainId: activeChainId,
|
||||
} = useWeb3React();
|
||||
const { config } = useEthereumConfig();
|
||||
const { VEGA_ENV, ETHEREUM_PROVIDER_URL } = useEnvironment();
|
||||
const [contracts, setContracts] =
|
||||
@@ -39,7 +43,11 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
Number(config.chain_id)
|
||||
);
|
||||
const provider = activeProvider ? activeProvider : defaultProvider;
|
||||
|
||||
const provider =
|
||||
activeProvider && activeChainId === Number(config.chain_id)
|
||||
? activeProvider
|
||||
: defaultProvider;
|
||||
|
||||
if (
|
||||
account &&
|
||||
@@ -84,7 +92,14 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
// TODO: hacky quick fix for release to prevent race condition, find a better fix for this.
|
||||
cancelled = true;
|
||||
};
|
||||
}, [activeProvider, account, config, VEGA_ENV, ETHEREUM_PROVIDER_URL]);
|
||||
}, [
|
||||
activeProvider,
|
||||
activeChainId,
|
||||
account,
|
||||
config,
|
||||
VEGA_ENV,
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
]);
|
||||
|
||||
if (!contracts) {
|
||||
return (
|
||||
|
||||
@@ -790,5 +790,6 @@
|
||||
"approval (% validator voting power)": "approval (% validator voting power)",
|
||||
"67% voting power required": "67% voting power required",
|
||||
"Token": "Token",
|
||||
"associateVegaNow": "Associate $VEGA now"
|
||||
"associateVegaNow": "Associate $VEGA now",
|
||||
"disconnectedNotice": "You have been disconnected. Connect your ETH wallet to the {{correctNetwork}} network to use this app."
|
||||
}
|
||||
|
||||
@@ -56,6 +56,7 @@ const mockAppState: AppState = {
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
};
|
||||
|
||||
jest.mock('../../../contexts/app-state/app-state-context', () => ({
|
||||
|
||||
+10
-14
@@ -1,8 +1,7 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||
import { format } from 'date-fns';
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { ProposalChangeTable } from './proposal-change-table';
|
||||
|
||||
@@ -24,16 +23,15 @@ it('Renders all data for table', () => {
|
||||
expect(screen.getByText('Closes on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(new Date(proposal?.terms.closingDatetime), DATE_FORMAT_DETAILED)
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Proposed enactment')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0)
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
@@ -43,7 +41,7 @@ it('Renders all data for table', () => {
|
||||
|
||||
expect(screen.getByText('Proposed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(format(new Date(proposal?.datetime), DATE_FORMAT_DETAILED))
|
||||
screen.getByText(formatDateWithLocalTimezone(new Date(proposal?.datetime)))
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Type')).toBeInTheDocument();
|
||||
@@ -62,16 +60,15 @@ it('Changes data based on if data is in future or past', () => {
|
||||
expect(screen.getByText('Closed on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(new Date(proposal?.terms.closingDatetime), DATE_FORMAT_DETAILED)
|
||||
formatDateWithLocalTimezone(new Date(proposal?.terms.closingDatetime))
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText('Enacted on')).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(
|
||||
format(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0)
|
||||
)
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
@@ -91,9 +88,8 @@ it('Does not render enactment time for freeform proposal', () => {
|
||||
expect(screen.queryByText('Enacted on')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText(
|
||||
format(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
formatDateWithLocalTimezone(
|
||||
new Date(proposal?.terms.enactmentDatetime || 0)
|
||||
)
|
||||
)
|
||||
).not.toBeInTheDocument();
|
||||
|
||||
+6
-8
@@ -1,12 +1,11 @@
|
||||
import { format, isFuture } from 'date-fns';
|
||||
import { isFuture } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
@@ -35,16 +34,15 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
{isFuture(new Date(terms?.closingDatetime))
|
||||
? t('closesOn')
|
||||
: t('closedOn')}
|
||||
{format(new Date(terms?.closingDatetime), DATE_FORMAT_DETAILED)}
|
||||
{formatDateWithLocalTimezone(new Date(terms?.closingDatetime))}
|
||||
</KeyValueTableRow>
|
||||
{terms?.change.__typename !== 'NewFreeform' ? (
|
||||
<KeyValueTableRow>
|
||||
{isFuture(new Date(terms?.enactmentDatetime || 0))
|
||||
? t('proposedEnactment')
|
||||
: t('enactedOn')}
|
||||
{format(
|
||||
new Date(terms?.enactmentDatetime || 0),
|
||||
DATE_FORMAT_DETAILED
|
||||
{formatDateWithLocalTimezone(
|
||||
new Date(terms?.enactmentDatetime || 0)
|
||||
)}
|
||||
</KeyValueTableRow>
|
||||
) : null}
|
||||
@@ -54,7 +52,7 @@ export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('proposedOn')}
|
||||
{format(new Date(proposal?.datetime), DATE_FORMAT_DETAILED)}
|
||||
{formatDateWithLocalTimezone(new Date(proposal?.datetime))}
|
||||
</KeyValueTableRow>
|
||||
{proposal?.rejectionReason ? (
|
||||
<KeyValueTableRow>
|
||||
|
||||
@@ -19,6 +19,7 @@ const mockAppState: AppState = {
|
||||
ethConnectOverlay: false,
|
||||
transactionOverlay: false,
|
||||
bannerMessage: '',
|
||||
disconnectNotice: false,
|
||||
};
|
||||
|
||||
jest.mock('../../../contexts/app-state/app-state-context', () => ({
|
||||
|
||||
@@ -9,6 +9,9 @@ query ProposalMarketsQuery {
|
||||
code
|
||||
}
|
||||
}
|
||||
proposal {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
|
||||
export type ProposalMarketsQueryQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ProposalMarketsQueryQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string } } } }> } | null };
|
||||
export type ProposalMarketsQueryQuery = { __typename?: 'Query', marketsConnection?: { __typename?: 'MarketConnection', edges: Array<{ __typename?: 'MarketEdge', node: { __typename?: 'Market', id: string, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, code: string } }, proposal?: { __typename?: 'Proposal', state: Types.ProposalState } | null } }> } | null };
|
||||
|
||||
|
||||
export const ProposalMarketsQueryDocument = gql`
|
||||
@@ -21,6 +21,9 @@ export const ProposalMarketsQueryDocument = gql`
|
||||
code
|
||||
}
|
||||
}
|
||||
proposal {
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-94
@@ -1,94 +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 ProposalMarketsQueryQueryVariables = Types.Exact<{
|
||||
[key: string]: never;
|
||||
}>;
|
||||
|
||||
export type ProposalMarketsQueryQuery = {
|
||||
__typename?: 'Query';
|
||||
marketsConnection?: {
|
||||
__typename?: 'MarketConnection';
|
||||
edges: Array<{
|
||||
__typename?: 'MarketEdge';
|
||||
node: {
|
||||
__typename?: 'Market';
|
||||
id: string;
|
||||
tradableInstrument: {
|
||||
__typename?: 'TradableInstrument';
|
||||
instrument: { __typename?: 'Instrument'; name: string; code: string };
|
||||
};
|
||||
};
|
||||
}>;
|
||||
} | null;
|
||||
};
|
||||
|
||||
export const ProposalMarketsQueryDocument = gql`
|
||||
query ProposalMarketsQuery {
|
||||
marketsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
tradableInstrument {
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useProposalMarketsQueryQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useProposalMarketsQueryQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useProposalMarketsQueryQuery` 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 } = useProposalMarketsQueryQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useProposalMarketsQueryQuery(
|
||||
baseOptions?: Apollo.QueryHookOptions<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useQuery<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>(ProposalMarketsQueryDocument, options);
|
||||
}
|
||||
export function useProposalMarketsQueryLazyQuery(
|
||||
baseOptions?: Apollo.LazyQueryHookOptions<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>
|
||||
) {
|
||||
const options = { ...defaultOptions, ...baseOptions };
|
||||
return Apollo.useLazyQuery<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>(ProposalMarketsQueryDocument, options);
|
||||
}
|
||||
export type ProposalMarketsQueryQueryHookResult = ReturnType<
|
||||
typeof useProposalMarketsQueryQuery
|
||||
>;
|
||||
export type ProposalMarketsQueryLazyQueryHookResult = ReturnType<
|
||||
typeof useProposalMarketsQueryLazyQuery
|
||||
>;
|
||||
export type ProposalMarketsQueryQueryResult = Apollo.QueryResult<
|
||||
ProposalMarketsQueryQuery,
|
||||
ProposalMarketsQueryQueryVariables
|
||||
>;
|
||||
+104
-8
@@ -1,15 +1,16 @@
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { MemoryRouter as Router } from 'react-router-dom';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { mockWalletContext } from '../../test-helpers/mocks';
|
||||
import { ProposeUpdateMarket } from './propose-update-market';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/react-helpers';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/react-helpers';
|
||||
import type { ProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
|
||||
import { ProposalMarketsQueryDocument } from './__generated___/UpdateMarket';
|
||||
import type { ProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
||||
import { ProposalMarketsQueryDocument } from './__generated__/UpdateMarket';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
const updateMarketNetworkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
@@ -89,6 +90,10 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
code: 'ETHiUSDT',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -101,11 +106,16 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Las Vegas nuggets',
|
||||
code: 'Nuggets2',
|
||||
code: 'Nuggets',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_OPEN,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
@@ -115,10 +125,14 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Nuggets',
|
||||
code: 'Nuggets',
|
||||
name: 'California Nuggets',
|
||||
code: 'Nuggets2',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -130,10 +144,71 @@ const marketQueryMock: MockedResponse<ProposalMarketsQueryQuery> = {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'CELUSD (June 2022)',
|
||||
code: 'CELUSD',
|
||||
name: 'Nevada Nuggets',
|
||||
code: 'Nugetts3',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_REJECTED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'asdfe1755208914b6f258a28babd19ae8dfbaf4084d8867d8a120c50dca1e17f',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'NZ Nuggets',
|
||||
code: 'Nugetts4',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_DECLINED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'asdfe1755208914b6f258a28babd19ae8dfbaf4084d8867d8a120c50dca1e17f',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Aussie Nuggets',
|
||||
code: 'Nugetts5',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_PASSED,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
__typename: 'MarketEdge',
|
||||
node: {
|
||||
__typename: 'Market',
|
||||
id: 'xcvbe1755208914b6f258a28babd19ae8dfbaf4084d8867d8a120c50dca1e17f',
|
||||
tradableInstrument: {
|
||||
__typename: 'TradableInstrument',
|
||||
instrument: {
|
||||
__typename: 'Instrument',
|
||||
name: 'Tasmanian Nuggets',
|
||||
code: 'Nugetts6',
|
||||
},
|
||||
},
|
||||
proposal: {
|
||||
__typename: 'Proposal',
|
||||
state: ProposalState.STATE_FAILED,
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -199,6 +274,27 @@ describe('Propose Update Market', () => {
|
||||
expect(screen.getByTestId('proposal-market-select')).toHaveValue('');
|
||||
});
|
||||
|
||||
it('should only render enacted markets', async () => {
|
||||
renderComponent();
|
||||
expect(
|
||||
await screen.findByText('Update market proposal')
|
||||
).toBeInTheDocument();
|
||||
// (state enacted)
|
||||
expect(screen.getByText('ETHUSD (September Market)')).toBeInTheDocument();
|
||||
// (state open)
|
||||
expect(screen.queryByText('Las Vegas nuggets')).not.toBeInTheDocument();
|
||||
// (state waiting for node vote)
|
||||
expect(screen.queryByText('California nuggets')).not.toBeInTheDocument();
|
||||
// (state rejected)
|
||||
expect(screen.queryByText('Nevada nuggets')).not.toBeInTheDocument();
|
||||
// (state declined)
|
||||
expect(screen.queryByText('NZ nuggets')).not.toBeInTheDocument();
|
||||
// (state passed)
|
||||
expect(screen.queryByText('Aussie nuggets')).not.toBeInTheDocument();
|
||||
// (state failed)
|
||||
expect(screen.queryByText('Tasmanian nuggets')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the correct market details when the market select is used', async () => {
|
||||
renderComponent();
|
||||
expect(
|
||||
|
||||
+11
-5
@@ -2,25 +2,28 @@ import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
doesValueEquateToParam,
|
||||
getClosingTimestamp,
|
||||
getEnactmentTimestamp,
|
||||
useProposalSubmit,
|
||||
doesValueEquateToParam,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { createDocsLinks, validateJson } from '@vegaprotocol/utils';
|
||||
import { NetworkParams, useNetworkParams } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
ProposalFormDescription,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormSubheader,
|
||||
ProposalFormSubmit,
|
||||
ProposalFormTerms,
|
||||
ProposalFormTitle,
|
||||
ProposalFormTransactionDialog,
|
||||
ProposalFormDownloadJson,
|
||||
ProposalFormVoteAndEnactmentDeadline,
|
||||
} from '../../components/propose';
|
||||
import { ProposalMinRequirements } from '../../components/shared';
|
||||
import {
|
||||
ProposalMinRequirements,
|
||||
ProposalUserAction,
|
||||
} from '../../components/shared';
|
||||
import {
|
||||
AsyncRenderer,
|
||||
ExternalLink,
|
||||
@@ -31,9 +34,9 @@ import {
|
||||
Select,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalUserAction } from '../../components/shared';
|
||||
import { useProposalMarketsQueryQuery } from './__generated___/UpdateMarket';
|
||||
import { useProposalMarketsQueryQuery } from './__generated__/UpdateMarket';
|
||||
import { downloadJson } from '../../../../lib/download-json';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
export interface UpdateMarketProposalFormFields {
|
||||
proposalVoteDeadline: string;
|
||||
@@ -73,6 +76,9 @@ export const ProposeUpdateMarket = () => {
|
||||
|
||||
return marketsData.marketsConnection.edges
|
||||
.map((edge) => edge.node)
|
||||
.filter(
|
||||
(market) => market.proposal?.state === ProposalState.STATE_ENACTED
|
||||
)
|
||||
.sort((a, b) => {
|
||||
const aName = a.tradableInstrument.instrument.name;
|
||||
const bName = b.tradableInstrument.instrument.name;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { usePreviousEpochQuery } from '../__generated__/PreviousEpoch';
|
||||
import { ValidatorTables } from './validator-tables';
|
||||
import { useRefreshAfterEpoch } from '../../../hooks/use-refresh-after-epoch';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ENV } from '../../../config';
|
||||
|
||||
export const EpochData = () => {
|
||||
// errorPolicy due to vegaprotocol/vega issue 5898
|
||||
@@ -14,11 +15,23 @@ export const EpochData = () => {
|
||||
data: nodesData,
|
||||
error: nodesError,
|
||||
loading: nodesLoading,
|
||||
refetch,
|
||||
refetch: nodesRefetch,
|
||||
} = useNodesQuery();
|
||||
const { data: userStakingData } = useStakingQuery({
|
||||
|
||||
const { delegationsPagination } = ENV;
|
||||
const {
|
||||
data: userStakingData,
|
||||
error: userStakingError,
|
||||
loading: userStakingLoading,
|
||||
refetch: userStakingRefetch,
|
||||
} = useStakingQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
delegationsPagination: delegationsPagination
|
||||
? {
|
||||
first: Number(delegationsPagination),
|
||||
}
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
const { data: previousEpochData } = usePreviousEpochQuery({
|
||||
@@ -28,10 +41,17 @@ export const EpochData = () => {
|
||||
skip: !nodesData?.epoch.id,
|
||||
});
|
||||
|
||||
useRefreshAfterEpoch(nodesData?.epoch.timestamps.expiry, refetch);
|
||||
useRefreshAfterEpoch(nodesData?.epoch.timestamps.expiry, () => {
|
||||
nodesRefetch();
|
||||
userStakingRefetch();
|
||||
});
|
||||
|
||||
return (
|
||||
<AsyncRenderer loading={nodesLoading} error={nodesError} data={nodesData}>
|
||||
<AsyncRenderer
|
||||
loading={nodesLoading || userStakingLoading}
|
||||
error={nodesError || userStakingError}
|
||||
data={nodesData}
|
||||
>
|
||||
{nodesData?.epoch &&
|
||||
nodesData.epoch.timestamps.start &&
|
||||
nodesData?.epoch.timestamps.expiry && (
|
||||
|
||||
+6
-5
@@ -3,7 +3,7 @@ import { forwardRef, useMemo, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Button, Icon } from '@vegaprotocol/ui-toolkit';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
} from '../../shared';
|
||||
import {
|
||||
defaultColDef,
|
||||
NODE_LIST_GRID_STYLES,
|
||||
PendingStakeRenderer,
|
||||
stakedTotalPercentage,
|
||||
StakeShareRenderer,
|
||||
@@ -211,7 +210,7 @@ export const ConsensusValidatorsTable = ({
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? stakedTotalPercentage(userStakeShare)
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
@@ -382,12 +381,14 @@ export const ConsensusValidatorsTable = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-testid="consensus-validators-table">
|
||||
<div
|
||||
data-testid="consensus-validators-table"
|
||||
className="validators-table"
|
||||
>
|
||||
{nodes.length > 0 && (
|
||||
<AgGrid
|
||||
domLayout="autoHeight"
|
||||
style={{ width: '100%' }}
|
||||
customThemeParams={NODE_LIST_GRID_STYLES}
|
||||
getRowHeight={(params: RowHeightParams) => getRowHeight(params)}
|
||||
defaultColDef={defaultColDef}
|
||||
tooltipShowDelay={0}
|
||||
|
||||
@@ -59,9 +59,9 @@ export const addUserDataToValidator = (
|
||||
: undefined,
|
||||
[ValidatorFields.USER_STAKE_SHARE]:
|
||||
currentEpochUserStaking && Number(currentEpochUserStaking.amount) > 0
|
||||
? new BigNumber(currentEpochUserStaking.amount).dividedBy(
|
||||
new BigNumber(currentUserStakeAvailable)
|
||||
)
|
||||
? new BigNumber(currentEpochUserStaking.amount)
|
||||
.dividedBy(new BigNumber(currentUserStakeAvailable))
|
||||
.times(100)
|
||||
: undefined,
|
||||
};
|
||||
};
|
||||
@@ -79,17 +79,6 @@ export interface ValidatorsTableProps {
|
||||
validatorsView: ValidatorsView;
|
||||
}
|
||||
|
||||
// Custom styling to account for the scrollbar. This is needed because the
|
||||
// AG Grid places the scrollbar over the bottom validator, which obstructs
|
||||
export const NODE_LIST_GRID_STYLES = `
|
||||
.ag-theme-balham-dark .ag-body-horizontal-scroll {
|
||||
opacity: 0.75;
|
||||
}
|
||||
.ag-theme-balham-dark *:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
||||
export const stakedTotalPercentage = (stakeScore: string) =>
|
||||
formatNumberPercentage(toBigNum(stakeScore, 0).times(100), 2);
|
||||
|
||||
|
||||
+11
-6
@@ -1,7 +1,7 @@
|
||||
import { forwardRef, useMemo, useRef } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { AgGridDynamic as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import {
|
||||
@@ -13,7 +13,6 @@ import {
|
||||
} from '../../shared';
|
||||
import {
|
||||
defaultColDef,
|
||||
NODE_LIST_GRID_STYLES,
|
||||
StakeNeededForPromotionRenderer,
|
||||
stakedTotalPercentage,
|
||||
ValidatorFields,
|
||||
@@ -26,7 +25,11 @@ import {
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import type { ValidatorsTableProps } from './shared';
|
||||
import { formatNumber, toBigNum } from '@vegaprotocol/utils';
|
||||
import {
|
||||
formatNumber,
|
||||
formatNumberPercentage,
|
||||
toBigNum,
|
||||
} from '@vegaprotocol/utils';
|
||||
|
||||
interface StandbyPendingValidatorsTableProps extends ValidatorsTableProps {
|
||||
stakeNeededForPromotion: string | undefined;
|
||||
@@ -163,7 +166,7 @@ export const StandbyPendingValidatorsTable = ({
|
||||
: undefined,
|
||||
[ValidatorFields.PENDING_USER_STAKE]: pendingUserStake,
|
||||
[ValidatorFields.USER_STAKE_SHARE]: userStakeShare
|
||||
? stakedTotalPercentage(userStakeShare)
|
||||
? formatNumberPercentage(new BigNumber(userStakeShare))
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
@@ -244,11 +247,13 @@ export const StandbyPendingValidatorsTable = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div data-testid="standby-pending-validators-table">
|
||||
<div
|
||||
data-testid="standby-pending-validators-table"
|
||||
className="validators-table"
|
||||
>
|
||||
<AgGrid
|
||||
domLayout="autoHeight"
|
||||
style={{ width: '100%' }}
|
||||
customThemeParams={NODE_LIST_GRID_STYLES}
|
||||
rowHeight={68}
|
||||
defaultColDef={defaultColDef}
|
||||
tooltipShowDelay={0}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
@import 'ag-grid-community/dist/styles/ag-grid.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
|
||||
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
|
||||
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@@ -19,3 +23,38 @@
|
||||
@apply border-vega-dark-200;
|
||||
}
|
||||
}
|
||||
|
||||
/* AG GRID - Do not edit without updating other global stylesheets for each app */
|
||||
|
||||
.vega-ag-grid .ag-root-wrapper {
|
||||
border: solid 0px;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-react-container {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.vega-ag-grid .ag-cell,
|
||||
.vega-ag-grid .ag-full-width-row .ag-cell-wrapper.ag-row-group {
|
||||
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
|
||||
}
|
||||
|
||||
/* Dark variables */
|
||||
.ag-theme-balham-dark {
|
||||
--ag-background-color: theme(colors.black);
|
||||
--ag-border-color: theme(colors.neutral[700]);
|
||||
--ag-header-background-color: theme(colors.black);
|
||||
--ag-odd-row-background-color: theme(colors.black);
|
||||
--ag-header-column-separator-color: theme(colors.neutral[600]);
|
||||
--ag-row-border-color: theme(colors.black);
|
||||
--ag-row-hover-color: theme(colors.neutral[800]);
|
||||
--ag-font-size: 12px;
|
||||
}
|
||||
|
||||
.validators-table .ag-theme-balham-dark .ag-body-horizontal-scroll {
|
||||
opacity: 0.75;
|
||||
}
|
||||
.validators-table .ag-theme-balham-dark *:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ NX_DEPLOY_URL=$DEPLOY_URL
|
||||
NX_DEPLOY_PRIME_URL=$DEPLOY_PRIME_URL
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_ENV = 'TESTNET'
|
||||
NX_VEGA_URL="https://api.n11.testnet.vega.xyz/graphql"
|
||||
NX_VEGA_URL="https://api.n07.testnet.vega.xyz/graphql"
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_ENV=LOCAL
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_VEGA_ENV=TESTNET
|
||||
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\"}
|
||||
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# App configuration variables
|
||||
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/fairground/vegawallet-fairground.toml
|
||||
NX_VEGA_URL=https://api.n09.testnet.vega.xyz/graphql
|
||||
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
|
||||
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
|
||||
NX_VEGA_ENV=TESTNET
|
||||
|
||||
@@ -1,17 +1,3 @@
|
||||
{
|
||||
"hosts": [
|
||||
"https://vega-data-graphql.chorus.one/query",
|
||||
"https://vega.xprv.io/datanode/query",
|
||||
"http://nala.mainnet.vega.community:3008/query",
|
||||
"http://commodum.mainnet.vega.community:3008/query",
|
||||
"http://lovali.mainnet.vega.community:3008/query",
|
||||
"http://b-harvest.mainnet.vega.community:3008/query",
|
||||
"http://staking-facilities.mainnet.vega.community:3008/query",
|
||||
"http://figment.mainnet.vega.community:3008/query",
|
||||
"http://nodes-guru.mainnet.vega.community:3008/query",
|
||||
"http://p2p.mainnet.vega.community:3008/query",
|
||||
"http://rockaway.mainnet.vega.community:3008/query",
|
||||
"http://greenfield-one.mainnet.vega.community:3008/query",
|
||||
"http://ryabina.mainnet.vega.community:3008/query"
|
||||
]
|
||||
"hosts": ["https://vega.xprv.io/datanode/query"]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,7 @@
|
||||
{
|
||||
"hosts": [
|
||||
"https://api-n00.testnet.vega.rocks/graphql",
|
||||
"https://api-n06.testnet.vega.rocks/graphql",
|
||||
"https://api-n07.testnet.vega.rocks/graphql",
|
||||
"https://api-n08.testnet.vega.rocks/graphql",
|
||||
"https://api-n09.testnet.vega.rocks/graphql",
|
||||
"https://api-n10.testnet.vega.rocks/graphql",
|
||||
"https://api-n11.testnet.vega.rocks/graphql",
|
||||
"https://api-n12.testnet.vega.rocks/graphql"
|
||||
"https://api-n07.testnet.vega.rocks/graphql"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\",\"STAGNET3\":\"https://stagnet3.console.vega.xyz\"}
|
||||
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
@@ -27,6 +27,6 @@ CYPRESS_VEGA_ENV=CUSTOM
|
||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
||||
CYPRESS_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
@@ -7,7 +7,7 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\",\"STAGNET3\":\"https://stagnet3.console.vega.xyz\"}
|
||||
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_ETH_LOCAL_PROVIDER_URL=http://localhost:8545/
|
||||
NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supply green clown session"
|
||||
@@ -24,6 +24,6 @@ CYPRESS_VEGA_ENV=CUSTOM
|
||||
CYPRESS_VEGA_PUBLIC_KEY=02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65
|
||||
CYPRESS_VEGA_PUBLIC_KEY2=7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535
|
||||
CYPRESS_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
CYPRESS_VEGA_URL=http://localhost:3028/query
|
||||
CYPRESS_VEGA_URL=http://localhost:3008/graphql
|
||||
CYPRESS_VEGA_WALLET_URL=http://localhost:1789
|
||||
CYPRESS_VEGA_WALLET_API_TOKEN=
|
||||
|
||||
@@ -0,0 +1,375 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
|
||||
import { addDays, subDays } from 'date-fns';
|
||||
import {
|
||||
chainIdQuery,
|
||||
statisticsQuery,
|
||||
createDataConnection,
|
||||
oracleSpecDataConnectionQuery,
|
||||
createMarketFragment,
|
||||
marketsQuery,
|
||||
marketsDataQuery,
|
||||
createMarketsDataFragment,
|
||||
assetQuery,
|
||||
networkParamsQuery,
|
||||
} from '@vegaprotocol/mock';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getDateTimeFormat,
|
||||
} from '@vegaprotocol/utils';
|
||||
|
||||
describe('Closed markets', { tags: '@smoke' }, () => {
|
||||
const rowSelector =
|
||||
'[data-testid="tab-closed-markets"] .ag-center-cols-container .ag-row';
|
||||
|
||||
const assetsResult = assetQuery();
|
||||
// @ts-ignore asset definitely exists
|
||||
const settlementAsset = assetsResult.assetsConnection.edges[0].node;
|
||||
|
||||
const settledMarket = createMarketFragment({
|
||||
id: '0',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
marketTimestamps: {
|
||||
open: subDays(new Date(), 10).toISOString(),
|
||||
close: subDays(new Date(), 4).toISOString(),
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
product: {
|
||||
dataSourceSpecForTradingTermination: {
|
||||
id: 'market-1-trading-termination-oracle-id',
|
||||
},
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: 'market-1-settlement-data-oracle-id',
|
||||
},
|
||||
settlementAsset,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const terminatedMarket = createMarketFragment({
|
||||
id: '1',
|
||||
state: MarketState.STATE_TRADING_TERMINATED,
|
||||
marketTimestamps: {
|
||||
open: subDays(new Date(), 10).toISOString(),
|
||||
close: null, // market
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [
|
||||
`settlement-expiry-date:${addDays(new Date(), 4).toISOString()}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const delayedSettledMarket = createMarketFragment({
|
||||
id: '2',
|
||||
state: MarketState.STATE_TRADING_TERMINATED,
|
||||
marketTimestamps: {
|
||||
open: subDays(new Date(), 10).toISOString(),
|
||||
close: null, // market
|
||||
},
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [
|
||||
`settlement-expiry-date:${subDays(new Date(), 2).toISOString()}`,
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const unknownMarket = createMarketFragment({
|
||||
id: '3',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
});
|
||||
|
||||
const closedMarketsResult = [
|
||||
{
|
||||
node: settledMarket,
|
||||
},
|
||||
{
|
||||
node: terminatedMarket,
|
||||
},
|
||||
{
|
||||
node: delayedSettledMarket,
|
||||
},
|
||||
{ node: unknownMarket },
|
||||
{
|
||||
node: createMarketFragment({ id: '4', state: MarketState.STATE_PENDING }),
|
||||
},
|
||||
{
|
||||
node: createMarketFragment({ id: '5', state: MarketState.STATE_ACTIVE }),
|
||||
},
|
||||
];
|
||||
|
||||
const settledMarketData = createMarketsDataFragment({
|
||||
market: {
|
||||
id: settledMarket.id,
|
||||
},
|
||||
bestBidPrice: '1000',
|
||||
bestOfferPrice: '2000',
|
||||
markPrice: '1500',
|
||||
});
|
||||
|
||||
const closedMarketsDataResult = [
|
||||
{
|
||||
node: {
|
||||
data: settledMarketData,
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
data: createMarketsDataFragment({
|
||||
market: {
|
||||
id: terminatedMarket.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
data: createMarketsDataFragment({
|
||||
market: {
|
||||
id: delayedSettledMarket.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
data: createMarketsDataFragment({
|
||||
market: {
|
||||
id: unknownMarket.id,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const specDataConnection = createDataConnection();
|
||||
|
||||
before(() => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
aliasGQLQuery(req, 'NetworkParams', networkParamsQuery());
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'Markets',
|
||||
marketsQuery({
|
||||
marketsConnection: {
|
||||
edges: closedMarketsResult,
|
||||
},
|
||||
})
|
||||
);
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'MarketsData',
|
||||
marketsDataQuery({
|
||||
marketsConnection: {
|
||||
edges: closedMarketsDataResult,
|
||||
},
|
||||
})
|
||||
);
|
||||
aliasGQLQuery(
|
||||
req,
|
||||
'OracleSpecDataConnection',
|
||||
oracleSpecDataConnectionQuery()
|
||||
);
|
||||
});
|
||||
|
||||
cy.mockSubscription();
|
||||
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Closed markets"]').click();
|
||||
});
|
||||
|
||||
it('renders a settled market', () => {
|
||||
const expectedMarkets = closedMarketsResult.filter((edge) => {
|
||||
return [
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
].includes(edge.node.state);
|
||||
});
|
||||
const product = settledMarket.tradableInstrument.instrument.product;
|
||||
|
||||
// rows should be filtered to only include settled/terminated markets
|
||||
cy.get(rowSelector).should('have.length', expectedMarkets.length);
|
||||
|
||||
// check each column in the first row renders correctly
|
||||
// 6001-MARK-001
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="code"]')
|
||||
.should('have.text', settledMarket.tradableInstrument.instrument.code);
|
||||
|
||||
// 6001-MARK-002
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="name"]')
|
||||
.should('have.text', settledMarket.tradableInstrument.instrument.name);
|
||||
|
||||
// 6001-MARK-003
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', MarketStateMapping[settledMarket.state]);
|
||||
|
||||
// 6001-MARK-004
|
||||
// 6001-MARK-005
|
||||
// 6001-MARK-009
|
||||
// 6001-MARK-008
|
||||
// 6001-MARK-010
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementDate"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should(($el) => {
|
||||
const href = $el.attr('href');
|
||||
expect(href).to.match(
|
||||
new RegExp(
|
||||
`/oracles/${product.dataSourceSpecForTradingTermination.id}`
|
||||
)
|
||||
);
|
||||
})
|
||||
.should('have.text', '4 days ago')
|
||||
.should(
|
||||
'have.attr',
|
||||
'title',
|
||||
getDateTimeFormat().format(
|
||||
new Date(settledMarket.marketTimestamps.close)
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-011
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="bestBidPrice"]')
|
||||
.should(
|
||||
'have.text',
|
||||
addDecimalsFormatNumber(
|
||||
settledMarketData.bestBidPrice,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-012
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="bestOfferPrice"]')
|
||||
.should(
|
||||
'have.text',
|
||||
addDecimalsFormatNumber(
|
||||
settledMarketData.bestOfferPrice,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-013
|
||||
cy.get(rowSelector).first().find('[col-id="markPrice"]').should(
|
||||
'have.text',
|
||||
|
||||
addDecimalsFormatNumber(
|
||||
settledMarketData.markPrice,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-014
|
||||
// 6001-MARK-015
|
||||
// 6001-MARK-016
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementDataOracleId"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should(($el) => {
|
||||
const href = $el.attr('href');
|
||||
expect(href).to.match(
|
||||
new RegExp(`/oracles/${product.dataSourceSpecForSettlementData.id}`)
|
||||
);
|
||||
})
|
||||
.should(
|
||||
'have.text',
|
||||
addDecimalsFormatNumber(
|
||||
// @ts-ignore cannot deep un-partial
|
||||
specDataConnection.externalData.data.data[0].value,
|
||||
settledMarket.decimalPlaces
|
||||
)
|
||||
);
|
||||
|
||||
// 6001-MARK-017
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="realisedPNL"]')
|
||||
.should('have.text', '-');
|
||||
|
||||
// 6001-MARK-018
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementAsset"]')
|
||||
.should('have.text', product.settlementAsset.symbol);
|
||||
|
||||
// 6001-MARK-020
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="id"]')
|
||||
.should('have.text', settledMarket.id);
|
||||
});
|
||||
|
||||
// test market list for market in terminated state
|
||||
it('renders a terminated market', () => {
|
||||
cy.get(rowSelector)
|
||||
.eq(1)
|
||||
.find('[col-id="state"]')
|
||||
.should('have.text', MarketStateMapping[terminatedMarket.state]);
|
||||
|
||||
// 6001-MARK-006
|
||||
// 6001-MARK-007
|
||||
cy.get(rowSelector)
|
||||
.eq(1)
|
||||
.find('[col-id="settlementDate"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should('have.text', 'Expected in 4 days');
|
||||
});
|
||||
|
||||
it('renders a terminated market which was expected to have settled', () => {
|
||||
cy.get(rowSelector)
|
||||
.eq(2)
|
||||
.find('[col-id="settlementDate"]')
|
||||
.should('have.class', 'text-danger')
|
||||
.find('[data-testid="link"]')
|
||||
.should('have.text', 'Expected 2 days ago');
|
||||
});
|
||||
|
||||
it('renders terminated market which doesnt have settlement date metadata', () => {
|
||||
cy.get(rowSelector)
|
||||
.eq(3)
|
||||
.find('[col-id="settlementDate"]')
|
||||
.find('[data-testid="link"]')
|
||||
.should('have.text', 'Unknown');
|
||||
});
|
||||
|
||||
it('can open asset detail dialog', () => {
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'Asset', assetsResult);
|
||||
});
|
||||
|
||||
cy.get(rowSelector)
|
||||
.first()
|
||||
.find('[col-id="settlementAsset"]')
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
// 6001-MARK-019
|
||||
cy.get('[data-testid="dialog-title"]').should(
|
||||
'have.text',
|
||||
`Asset details - ${settlementAsset.symbol}`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -101,7 +101,7 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
cy.visit('/');
|
||||
cy.visit('/#/markets/market-1');
|
||||
cy.wait('@MarketsCandles');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
});
|
||||
@@ -112,7 +112,6 @@ describe('deposit actions', { tags: '@smoke' }, () => {
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains('[data-testid="deposit"]', 'Deposit to trade').click();
|
||||
connectEthereumWallet('MetaMask');
|
||||
cy.getByTestId('deposit-submit').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -153,7 +153,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
// the choose market overlay is no longer showing
|
||||
cy.contains('Select a market to get started').should('not.exist');
|
||||
cy.contains('Loading...').should('not.exist');
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-1');
|
||||
cy.url().should('eq', Cypress.config().baseUrl + '/#/markets/market-0');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -172,6 +172,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
// @ts-ignore partial deep check failing
|
||||
const data = marketsDataQuery(override);
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'MarketsData', data);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { MarketTradingModeMapping } from '@vegaprotocol/types';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
|
||||
const marketInfoBtn = 'Info';
|
||||
const row = 'key-value-table-row';
|
||||
@@ -12,7 +13,12 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockTradingPage(
|
||||
MarketState.STATE_ACTIVE,
|
||||
undefined,
|
||||
undefined,
|
||||
'COMPROMISED'
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
@@ -20,6 +26,11 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
cy.wait('@MarketInfo');
|
||||
});
|
||||
|
||||
it('show oracle banner', () => {
|
||||
cy.getByTestId(marketTitle).contains('Oracle').click();
|
||||
cy.getByTestId('oracle-status').should('contain.text', 'COMPROMISED');
|
||||
});
|
||||
|
||||
it('current fees displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Current fees').click();
|
||||
validateMarketDataRow(0, 'Maker Fee', '0.02%');
|
||||
@@ -169,25 +180,21 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
it('oracle displayed', () => {
|
||||
cy.getByTestId(marketTitle).contains('Oracle').click();
|
||||
|
||||
validateMarketDataRow(0, 'Settlement Data Property', 'prices.BTC.value');
|
||||
validateMarketDataRow(
|
||||
1,
|
||||
'Trading Termination Property',
|
||||
'termination.BTC.value'
|
||||
);
|
||||
|
||||
// check that links to github for oracle proofs are shown
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('oracle-proof-links')
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', 'https://github.com/vegaprotocol/well-known');
|
||||
.getByTestId('provider-name')
|
||||
.and('contain', 'Another oracle');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('oracle-spec-links')
|
||||
.find(`[data-testid="${externalLink}"]`)
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', '/oracles');
|
||||
.getByTestId('signed-proofs')
|
||||
.and('contain', '1');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('verified-proofs')
|
||||
.and('contain', '1');
|
||||
|
||||
cy.getByTestId(accordionContent)
|
||||
.getByTestId('signed-proofs')
|
||||
.and('contain', '1');
|
||||
});
|
||||
|
||||
it('proposal displayed', () => {
|
||||
|
||||
@@ -222,6 +222,7 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
// @ts-ignore partial deep check failing
|
||||
const market = marketsQuery(override);
|
||||
aliasGQLQuery(req, 'Market', market);
|
||||
aliasGQLQuery(req, 'ProposalOfMarket', {
|
||||
|
||||
@@ -32,7 +32,7 @@ describe('Navbar', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
it('Resources dropdown should be correctly rendered', () => {
|
||||
const resourceSelector = 'ul li:last-child';
|
||||
const resourceSelector = 'ul li:contains(Resources)';
|
||||
['Docs', 'Give Feedback'].forEach((text, index) => {
|
||||
cy.get('nav').find(resourceSelector).contains('Resources').click();
|
||||
cy.get('nav')
|
||||
@@ -91,7 +91,7 @@ describe('Navbar', { tags: '@smoke' }, () => {
|
||||
cy.getByTestId('button-menu-drawer').click();
|
||||
cy.getByTestId('menu-drawer').should('be.visible');
|
||||
cy.getByTestId('menu-drawer')
|
||||
.find('[data-testid="theme-switcher"]')
|
||||
.find('[data-testid="Settings"]')
|
||||
.should('be.visible');
|
||||
cy.getByTestId('button-menu-drawer').click();
|
||||
cy.getByTestId('menu-drawer').should('not.be.visible');
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import { closeWelcomeDialog } from '../support/helpers';
|
||||
|
||||
describe('Settings page', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage().then(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
closeWelcomeDialog();
|
||||
cy.get('[aria-label="cog icon"]').click();
|
||||
});
|
||||
});
|
||||
it('telemetry checkbox should work well', () => {
|
||||
cy.location('hash').should('equal', '#/settings');
|
||||
cy.getByTestId('telemetry-approval').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'unchecked'
|
||||
);
|
||||
cy.get('[for="telemetry-approval"]').click();
|
||||
cy.getByTestId('telemetry-approval').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
cy.reload();
|
||||
cy.getByTestId('telemetry-approval').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
cy.get('[for="telemetry-approval"]').click();
|
||||
cy.getByTestId('telemetry-approval').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'unchecked'
|
||||
);
|
||||
cy.reload();
|
||||
cy.getByTestId('telemetry-approval').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'unchecked'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,17 +1,25 @@
|
||||
import { checkSorting } from '@vegaprotocol/cypress';
|
||||
|
||||
const dialogClose = 'dialog-close';
|
||||
|
||||
describe('accounts', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Assets');
|
||||
});
|
||||
|
||||
it('renders accounts', () => {
|
||||
// 7001-COLL-001
|
||||
// 7001-COLL-002
|
||||
// 7001-COLL-003
|
||||
// 7001-COLL-004
|
||||
// 7001-COLL-005
|
||||
// 7001-COLL-006
|
||||
// 7001-COLL-007
|
||||
|
||||
const tradingAccountRowId = '[row-id="t-0"]';
|
||||
cy.getByTestId('Collateral').click();
|
||||
|
||||
@@ -24,29 +32,113 @@ describe('accounts', { tags: '@smoke' }, () => {
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="accounts-actions"]')
|
||||
.should('have.text', 'DepositWithdraw');
|
||||
.find('[col-id="used"]')
|
||||
.should('have.text', '1.010.00%');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[data-testid="deposit"]')
|
||||
.should('have.text', 'Deposit');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="accounts-actions"] [data-testid="withdraw"]')
|
||||
.should('have.text', 'Withdraw');
|
||||
.find('[col-id="available"]')
|
||||
.should('have.text', '100,000.00');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="total"]')
|
||||
.should('have.text', '100,001.01');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="accounts-actions"]')
|
||||
.should('have.text', ' ');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get(tradingAccountRowId)
|
||||
.find('[col-id="total"]')
|
||||
.should('have.text', '100,001.01');
|
||||
|
||||
cy.getByTestId('tab-accounts')
|
||||
.get('[col-id="accounts-actions"]')
|
||||
.find('[data-testid="dropdown-menu"]')
|
||||
.eq(1)
|
||||
.click();
|
||||
cy.getByTestId('deposit').should('be.visible');
|
||||
cy.getByTestId('withdraw').should('be.visible');
|
||||
cy.getByTestId('breakdown').should('be.visible');
|
||||
cy.getByTestId('Collateral').click({ force: true });
|
||||
});
|
||||
|
||||
it('should open asset details dialog when clicked on symbol', () => {
|
||||
// 7001-COLL-008
|
||||
// 6501-ASSE-001
|
||||
// 6501-ASSE-002
|
||||
// 6501-ASSE-003
|
||||
// 6501-ASSE-004
|
||||
// 6501-ASSE-005
|
||||
// 6501-ASSE-006
|
||||
// 6501-ASSE-007
|
||||
// 6501-ASSE-008
|
||||
// 6501-ASSE-009
|
||||
// 6501-ASSE-010
|
||||
// 6501-ASSE-011
|
||||
// 6501-ASSE-012
|
||||
// 6501-ASSE-013
|
||||
const titles = [
|
||||
'ID',
|
||||
'Type',
|
||||
'Name',
|
||||
'Symbol',
|
||||
'Decimals',
|
||||
'Quantum',
|
||||
'Status',
|
||||
'Contract address',
|
||||
'Withdrawal threshold',
|
||||
'Lifetime limit',
|
||||
'Infrastructure fee account balance',
|
||||
'Global reward pool account balance',
|
||||
'Maker paid fees account balance',
|
||||
'Maker received fees account balance',
|
||||
'Liquidity provision fee reward account balance',
|
||||
'Market proposer reward account balance',
|
||||
];
|
||||
cy.getByTestId('asset').contains('tEURO').click();
|
||||
cy.get('[data-testid="dialog-content"]:visible').should('exist');
|
||||
cy.get('[data-testid="dialog-close"]:visible').click();
|
||||
cy.get('[data-testid$="_label"]').should('have.length', 16);
|
||||
cy.get('[data-testid$="_label"]').each((element, index) => {
|
||||
cy.wrap(element).should('have.text', titles[index]);
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
cy.getByTestId(dialogClose).should('not.exist');
|
||||
});
|
||||
|
||||
it('should open usage breakdown dialog when clicked on used', () => {
|
||||
// 7001-COLL-009
|
||||
cy.getByTestId('breakdown').contains('1.01').click();
|
||||
const headers = ['Market', 'Account type', 'Balance'];
|
||||
cy.getByTestId('usage-breakdown').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
cy.wrap($header).should('have.text', headers[i]);
|
||||
});
|
||||
});
|
||||
cy.getByTestId(dialogClose).click();
|
||||
});
|
||||
|
||||
it('sorting usage breakdown columns should work well', () => {
|
||||
// 7001-COLL-010
|
||||
cy.getByTestId('breakdown').contains('1.01').click();
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(1)
|
||||
.should('have.text', 'Margin');
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(2)
|
||||
.should('have.text', 'Margin');
|
||||
cy.getByTestId('usage-breakdown')
|
||||
.find('[col-id="type"]')
|
||||
.eq(3)
|
||||
.should('have.text', 'General');
|
||||
|
||||
cy.getByTestId(dialogClose).click();
|
||||
});
|
||||
|
||||
describe('sorting by ag-grid columns should work well', () => {
|
||||
|
||||
@@ -88,5 +88,18 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
|
||||
'Size cannot be lower than 1'
|
||||
);
|
||||
});
|
||||
it('must have total margin available', () => {
|
||||
// 7001-COLL-011
|
||||
cy.getByTestId('tab-ticket')
|
||||
.find('.text-xs')
|
||||
.eq(5)
|
||||
.within(() => {
|
||||
cy.get('[data-state="closed"]').should(
|
||||
'have.text',
|
||||
'Total margin available'
|
||||
);
|
||||
cy.get('.text-neutral-500').should('have.text', '~100,000.01 tDAI');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -85,7 +85,7 @@ describe('fills', { tags: '@regression' }, () => {
|
||||
cy.wrap($role)
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
const roles = ['Maker', 'Taker'];
|
||||
const roles = ['Maker', 'Taker', '-'];
|
||||
expect(roles.indexOf(text.trim())).to.be.greaterThan(-1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -455,7 +455,7 @@ describe('amend and cancel order', { tags: '@smoke' }, () => {
|
||||
});
|
||||
});
|
||||
it('must be warned (pre-submit) if the input price has too many digits after the decimal place for the market', () => {
|
||||
// 7003-MORD-016
|
||||
// 7003-MORD-013
|
||||
updateOrder({
|
||||
id: orderId,
|
||||
status: Schema.OrderStatus.STATUS_ACTIVE,
|
||||
|
||||
@@ -9,3 +9,8 @@ export const selectAsset = (assetIndex: number) => {
|
||||
// eslint-disable-next-line
|
||||
cy.wait(100);
|
||||
};
|
||||
|
||||
export const closeWelcomeDialog = () => {
|
||||
cy.getByTestId('select-market-list').should('exist');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ export function updateOrder(
|
||||
override?: PartialDeep<OrderUpdateFieldsFragment>
|
||||
): void {
|
||||
const update: OrdersUpdateSubscription = orderUpdateSubscription({
|
||||
// @ts-ignore partial deep check failing
|
||||
orders: [override],
|
||||
});
|
||||
if (!sendOrderUpdate) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { CyHttpMessages } from 'cypress/types/net-stubbing';
|
||||
import type { Provider, Status } from '@vegaprotocol/oracles';
|
||||
import {
|
||||
accountsQuery,
|
||||
assetQuery,
|
||||
@@ -45,7 +46,9 @@ const marketDataOverride = (
|
||||
{
|
||||
node: {
|
||||
data: {
|
||||
// @ts-ignore conflict between incoming and outgoing types
|
||||
trigger: data.trigger,
|
||||
// @ts-ignore same as above
|
||||
marketTradingMode: data.tradingMode,
|
||||
marketState: data.state,
|
||||
},
|
||||
@@ -62,6 +65,7 @@ const marketsDataOverride = (
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
// @ts-ignore conflict between incoming and outgoing types
|
||||
tradingMode: data.tradingMode,
|
||||
state: data.state,
|
||||
},
|
||||
@@ -155,7 +159,6 @@ const mockTradingPage = (
|
||||
aliasGQLQuery(req, 'ProposalsList', proposalListQuery());
|
||||
aliasGQLQuery(req, 'Deposits', depositsQuery());
|
||||
};
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
@@ -164,50 +167,56 @@ declare global {
|
||||
mockTradingPage(
|
||||
state?: Schema.MarketState,
|
||||
tradingMode?: Schema.MarketTradingMode,
|
||||
trigger?: Schema.AuctionTrigger
|
||||
trigger?: Schema.AuctionTrigger,
|
||||
oracleStatus?: Status
|
||||
): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const addMockTradingPage = () => {
|
||||
Cypress.Commands.add(
|
||||
'mockTradingPage',
|
||||
(state = Schema.MarketState.STATE_ACTIVE, tradingMode, trigger) => {
|
||||
(
|
||||
state = Schema.MarketState.STATE_ACTIVE,
|
||||
tradingMode,
|
||||
trigger,
|
||||
oracleStatus
|
||||
) => {
|
||||
cy.mockGQL((req) => {
|
||||
mockTradingPage(req, state, tradingMode, trigger);
|
||||
});
|
||||
|
||||
const oracle: Provider = {
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: oracleStatus || 'GOOD',
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message',
|
||||
available: true,
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-${ORACLE_PUBKEY}.toml`,
|
||||
};
|
||||
// Prevent request to github, return some dummy content
|
||||
cy.intercept(
|
||||
'GET',
|
||||
/^https:\/\/raw.githubusercontent.com\/vegaprotocol\/well-known/,
|
||||
{
|
||||
body: [
|
||||
{
|
||||
name: 'Another oracle',
|
||||
url: 'https://zombo.com',
|
||||
description_markdown:
|
||||
'Some markdown describing the oracle provider.\n\nTwitter: @FacesPics2\n',
|
||||
oracle: {
|
||||
status: 'GOOD',
|
||||
status_reason: '',
|
||||
first_verified: '2022-01-01T00:00:00.000Z',
|
||||
last_verified: '2022-12-31T00:00:00.000Z',
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
},
|
||||
proofs: [
|
||||
{
|
||||
format: 'signed_message',
|
||||
available: true,
|
||||
type: 'public_key',
|
||||
public_key: ORACLE_PUBKEY,
|
||||
message: 'SOMEHEX',
|
||||
},
|
||||
],
|
||||
github_link: `https://github.com/vegaprotocol/well-known/blob/main/oracle-providers/public_key-${ORACLE_PUBKEY}.toml`,
|
||||
},
|
||||
],
|
||||
body: [oracle],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ NX_VEGA_ENV=CUSTOM
|
||||
NX_VEGA_EXPLORER_URL=https://stagnet3.explorer.vega.xyz
|
||||
NX_VEGA_NETWORKS={\"TESTNET\":\"https://console.fairground.wtf\",\"STAGNET1\":\"https://stagnet1.console.vega.xyz\",\"STAGNET3\":\"https://stagnet3.console.vega.xyz\"}
|
||||
NX_VEGA_TOKEN_URL=https://token.fairground.wtf
|
||||
NX_VEGA_URL=http://localhost:3028/query
|
||||
NX_VEGA_URL=http://localhost:3008/graphql
|
||||
NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
|
||||
@@ -11,4 +11,4 @@ NX_VEGA_WALLET_URL=http://localhost:1789
|
||||
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
|
||||
NX_VEGA_REPO_URL=https://github.com/vegaprotocol/vega/releases
|
||||
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
|
||||
@@ -27,7 +27,10 @@ import { NO_MARKET } from './constants';
|
||||
import { LiquidityContainer } from '../liquidity/liquidity';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
usePaneLayout,
|
||||
useScreenDimensions,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import {
|
||||
useMarketClickHandler,
|
||||
useMarketLiquidityClickHandler,
|
||||
@@ -83,15 +86,20 @@ interface BottomPanelProps {
|
||||
|
||||
const MarketBottomPanel = memo(
|
||||
({ marketId, pinnedAsset }: BottomPanelProps) => {
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'bottom' });
|
||||
const { screenSize } = useScreenDimensions();
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
return 'xxxl' === screenSize ? (
|
||||
<ResizableGrid proportionalLayout minSize={200}>
|
||||
<ResizableGrid
|
||||
proportionalLayout
|
||||
minSize={200}
|
||||
onChange={handleOnLayoutChange}
|
||||
>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizes[0] || '50%'}
|
||||
minSize={50}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -119,7 +127,7 @@ const MarketBottomPanel = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizes[1] || '50%'}
|
||||
minSize={50}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -186,22 +194,29 @@ MarketBottomPanel.displayName = 'MarketBottomPanel';
|
||||
const MainGrid = memo(
|
||||
({
|
||||
marketId,
|
||||
onSelect,
|
||||
pinnedAsset,
|
||||
}: {
|
||||
marketId: string;
|
||||
onSelect: (marketId: string, metaKey?: boolean) => void;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
|
||||
const [sizesMiddle, handleOnMiddleLayoutChange] = usePaneLayout({
|
||||
id: 'middle',
|
||||
});
|
||||
|
||||
return (
|
||||
<ResizableGrid vertical>
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}>
|
||||
<ResizableGrid proportionalLayout={false} minSize={200}>
|
||||
<ResizableGrid
|
||||
proportionalLayout={false}
|
||||
minSize={200}
|
||||
onChange={handleOnMiddleLayoutChange}
|
||||
>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.High}
|
||||
minSize={200}
|
||||
preferredSize="50%"
|
||||
preferredSize={sizesMiddle[0] || '50%'}
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-main-left">
|
||||
@@ -219,7 +234,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={330}
|
||||
preferredSize={sizesMiddle[1] || 330}
|
||||
minSize={300}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -238,7 +253,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={430}
|
||||
preferredSize={sizesMiddle[2] || 430}
|
||||
minSize={200}
|
||||
>
|
||||
<TradeGridChild>
|
||||
@@ -256,7 +271,7 @@ const MainGrid = memo(
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize="25%"
|
||||
preferredSize={sizes[1] || '25%'}
|
||||
minSize={50}
|
||||
>
|
||||
<MarketBottomPanel marketId={marketId} pinnedAsset={pinnedAsset} />
|
||||
@@ -278,11 +293,7 @@ export const TradeGrid = ({
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<MainGrid
|
||||
marketId={market?.id || ''}
|
||||
onSelect={onSelect}
|
||||
pinnedAsset={pinnedAsset}
|
||||
/>
|
||||
<MainGrid marketId={market?.id || ''} pinnedAsset={pinnedAsset} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -350,7 +361,10 @@ export const TradePanels = ({
|
||||
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<div>
|
||||
<TradeMarketHeader market={market} onSelect={onSelect} />
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<div className="h-full">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
import { act, render, screen, within } from '@testing-library/react';
|
||||
import { Closed } from './closed';
|
||||
import { MarketStateMapping } from '@vegaprotocol/types';
|
||||
import { PositionStatus } from '@vegaprotocol/types';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { subDays } from 'date-fns';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/oracles';
|
||||
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/oracles';
|
||||
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import type {
|
||||
PositionsQuery,
|
||||
PositionFieldsFragment,
|
||||
} from '@vegaprotocol/positions';
|
||||
import { PositionsDocument } from '@vegaprotocol/positions';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
import type { MarketsDataQuery, MarketsQuery } from '@vegaprotocol/market-list';
|
||||
import { MarketsDataDocument } from '@vegaprotocol/market-list';
|
||||
import { MarketsDocument } from '@vegaprotocol/market-list';
|
||||
import {
|
||||
createMarketFragment,
|
||||
marketsQuery,
|
||||
marketsDataQuery,
|
||||
createMarketsDataFragment,
|
||||
} from '@vegaprotocol/mock';
|
||||
|
||||
describe('Closed', () => {
|
||||
let originalNow: typeof Date.now;
|
||||
const mockNowTimestamp = 1672531200000;
|
||||
const settlementDateMetaDate = subDays(
|
||||
new Date(mockNowTimestamp),
|
||||
3
|
||||
).toISOString();
|
||||
const settlementDateTag = `settlement-expiry-date:${settlementDateMetaDate}`;
|
||||
const pubKey = 'pubKey';
|
||||
const marketId = 'market-0';
|
||||
const settlementDataProperty = 'spec-binding';
|
||||
const settlementDataId = 'settlement-data-oracle-id';
|
||||
|
||||
const market = createMarketFragment({
|
||||
id: marketId,
|
||||
state: MarketState.STATE_SETTLED,
|
||||
tradableInstrument: {
|
||||
instrument: {
|
||||
metadata: {
|
||||
tags: [settlementDateTag],
|
||||
},
|
||||
product: {
|
||||
dataSourceSpecForSettlementData: {
|
||||
id: settlementDataId,
|
||||
},
|
||||
dataSourceSpecBinding: {
|
||||
settlementDataProperty,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const marketsMock: MockedResponse<MarketsQuery> = {
|
||||
request: {
|
||||
query: MarketsDocument,
|
||||
},
|
||||
result: {
|
||||
data: marketsQuery({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: market,
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
const marketsData = createMarketsDataFragment({
|
||||
__typename: 'MarketData',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: marketId,
|
||||
},
|
||||
bestBidPrice: '1000',
|
||||
bestOfferPrice: '2000',
|
||||
markPrice: '1500',
|
||||
});
|
||||
const marketsDataMock: MockedResponse<MarketsDataQuery> = {
|
||||
request: {
|
||||
query: MarketsDataDocument,
|
||||
},
|
||||
result: {
|
||||
data: marketsDataQuery({
|
||||
marketsConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
data: marketsData,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
// Create mock oracle data
|
||||
const property = {
|
||||
__typename: 'Property' as const,
|
||||
name: settlementDataProperty,
|
||||
value: '12345',
|
||||
};
|
||||
const oracleDataMock: MockedResponse<OracleSpecDataConnectionQuery> = {
|
||||
request: {
|
||||
query: OracleSpecDataConnectionDocument,
|
||||
variables: {
|
||||
oracleSpecId: settlementDataId,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
oracleSpec: {
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
data: [property],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// Create mock position
|
||||
const createPosition = (): PositionFieldsFragment => {
|
||||
return {
|
||||
__typename: 'Position' as const,
|
||||
realisedPNL: '1000',
|
||||
unrealisedPNL: '2000',
|
||||
openVolume: '3000',
|
||||
averageEntryPrice: '100',
|
||||
updatedAt: new Date().toISOString(),
|
||||
positionStatus: PositionStatus.POSITION_STATUS_UNSPECIFIED,
|
||||
lossSocializationAmount: '1000',
|
||||
market: {
|
||||
__typename: 'Market',
|
||||
id: marketId,
|
||||
},
|
||||
};
|
||||
};
|
||||
const position = createPosition();
|
||||
const positionsMock: MockedResponse<PositionsQuery> = {
|
||||
request: {
|
||||
query: PositionsDocument,
|
||||
variables: {
|
||||
partyId: pubKey,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
party: {
|
||||
__typename: 'Party',
|
||||
id: pubKey,
|
||||
positionsConnection: {
|
||||
__typename: 'PositionConnection',
|
||||
edges: [{ __typename: 'PositionEdge', node: position }],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
originalNow = Date.now;
|
||||
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Date.now = originalNow;
|
||||
});
|
||||
|
||||
it('renders correctly formatted and filtered rows', async () => {
|
||||
await act(async () => {
|
||||
render(
|
||||
<MockedProvider
|
||||
mocks={[marketsMock, marketsDataMock, positionsMock, oracleDataMock]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
);
|
||||
});
|
||||
// screen.debug(document, Infinity);
|
||||
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
const expectedHeaders = [
|
||||
'Market',
|
||||
'Description',
|
||||
'Status',
|
||||
'Settlement date',
|
||||
'Best bid',
|
||||
'Best offer',
|
||||
'Mark price',
|
||||
'Settlement price',
|
||||
'Realised PNL',
|
||||
'Settlement asset',
|
||||
'Market ID',
|
||||
];
|
||||
expect(headers).toHaveLength(expectedHeaders.length);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
market.tradableInstrument.instrument.code,
|
||||
market.tradableInstrument.instrument.name,
|
||||
MarketStateMapping[market.state],
|
||||
'3 days ago',
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces),
|
||||
addDecimalsFormatNumber(
|
||||
marketsData!.bestOfferPrice,
|
||||
market.decimalPlaces
|
||||
),
|
||||
addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces),
|
||||
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
||||
addDecimalsFormatNumber(property.value, market.decimalPlaces),
|
||||
addDecimalsFormatNumber(position.realisedPNL, market.decimalPlaces),
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol,
|
||||
market.id,
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
});
|
||||
|
||||
it('only renders settled and terminated markets', async () => {
|
||||
const mixedMarkets = [
|
||||
{
|
||||
// inlclude as settled
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'include-0',
|
||||
state: MarketState.STATE_SETTLED,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// omit this market
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'discard-0',
|
||||
state: MarketState.STATE_SUSPENDED,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// include as terminated
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'include-1',
|
||||
state: MarketState.STATE_TRADING_TERMINATED,
|
||||
}),
|
||||
},
|
||||
{
|
||||
// omit this market
|
||||
__typename: 'MarketEdge' as const,
|
||||
node: createMarketFragment({
|
||||
id: 'discard-1',
|
||||
state: MarketState.STATE_ACTIVE,
|
||||
}),
|
||||
},
|
||||
];
|
||||
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
|
||||
request: {
|
||||
query: MarketsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: mixedMarkets,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
await act(async () => {
|
||||
render(
|
||||
<MockedProvider
|
||||
mocks={[
|
||||
mixedMarketsMock,
|
||||
marketsDataMock,
|
||||
positionsMock,
|
||||
oracleDataMock,
|
||||
]}
|
||||
>
|
||||
<VegaWalletContext.Provider
|
||||
value={{ pubKey } as VegaWalletContextShape}
|
||||
>
|
||||
<Closed />
|
||||
</VegaWalletContext.Provider>
|
||||
</MockedProvider>
|
||||
);
|
||||
});
|
||||
|
||||
// check that the number of rows in datagrid is 2
|
||||
const container = within(
|
||||
document.querySelector('.ag-center-cols-container') as HTMLElement
|
||||
);
|
||||
const expectedRows = mixedMarkets.filter((m) => {
|
||||
return [
|
||||
MarketState.STATE_SETTLED,
|
||||
MarketState.STATE_TRADING_TERMINATED,
|
||||
].includes(m.node.state);
|
||||
});
|
||||
|
||||
// check rows length is correct
|
||||
const rows = container.getAllByRole('row');
|
||||
expect(rows).toHaveLength(expectedRows.length);
|
||||
|
||||
// check that only included ids are shown
|
||||
const cells = screen
|
||||
.getAllByRole('gridcell')
|
||||
.filter((cell) => cell.getAttribute('col-id') === 'id')
|
||||
.map((cell) => cell.textContent?.trim());
|
||||
expect(cells).toEqual(expectedRows.map((m) => m.node.id));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,285 @@
|
||||
import compact from 'lodash/compact';
|
||||
import { isAfter } from 'date-fns';
|
||||
import type {
|
||||
VegaICellRendererParams,
|
||||
VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
|
||||
import { useMemo } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketState, MarketStateMapping } from '@vegaprotocol/types';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
getMarketExpiryDate,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { usePositionsQuery } from '@vegaprotocol/positions';
|
||||
import type { MarketMaybeWithData } from '@vegaprotocol/market-list';
|
||||
import { closedMarketsWithDataProvider } from '@vegaprotocol/market-list';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import type { ColDef } from 'ag-grid-community';
|
||||
import { SettlementDateCell } from './settlement-date-cell';
|
||||
import { SettlementPriceCell } from './settlement-price-cell';
|
||||
import { useDataProvider } from '@vegaprotocol/react-helpers';
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
type SettlementAsset =
|
||||
MarketMaybeWithData['tradableInstrument']['instrument']['product']['settlementAsset'];
|
||||
|
||||
interface Row {
|
||||
id: string;
|
||||
code: string;
|
||||
name: string;
|
||||
decimalPlaces: number;
|
||||
state: MarketState;
|
||||
metadata: string[];
|
||||
closeTimestamp: string | null;
|
||||
bestBidPrice: string | undefined;
|
||||
bestOfferPrice: string | undefined;
|
||||
markPrice: string | undefined;
|
||||
settlementDataOracleId: string;
|
||||
settlementDataSpecBinding: string;
|
||||
tradingTerminationOracleId: string;
|
||||
settlementAsset: SettlementAsset;
|
||||
realisedPNL: string | undefined;
|
||||
}
|
||||
|
||||
export const Closed = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
const {
|
||||
data: marketData,
|
||||
loading,
|
||||
error,
|
||||
reload,
|
||||
} = useDataProvider({
|
||||
dataProvider: closedMarketsWithDataProvider,
|
||||
variables: undefined,
|
||||
});
|
||||
const { data: positionData } = usePositionsQuery({
|
||||
variables: {
|
||||
partyId: pubKey || '',
|
||||
},
|
||||
skip: !pubKey,
|
||||
});
|
||||
|
||||
// find a position for each market and add the realised pnl to
|
||||
// a normalized object
|
||||
const rowData = compact(marketData).map((market) => {
|
||||
const position = positionData?.party?.positionsConnection?.edges?.find(
|
||||
(edge) => {
|
||||
return edge.node.market.id === market.id;
|
||||
}
|
||||
);
|
||||
|
||||
const row: Row = {
|
||||
id: market.id,
|
||||
code: market.tradableInstrument.instrument.code,
|
||||
name: market.tradableInstrument.instrument.name,
|
||||
decimalPlaces: market.decimalPlaces,
|
||||
state: market.state,
|
||||
metadata: market.tradableInstrument.instrument.metadata.tags ?? [],
|
||||
closeTimestamp: market.marketTimestamps.close,
|
||||
bestBidPrice: market.data?.bestBidPrice,
|
||||
bestOfferPrice: market.data?.bestOfferPrice,
|
||||
markPrice: market.data?.markPrice,
|
||||
settlementDataOracleId:
|
||||
market.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForSettlementData.id,
|
||||
settlementDataSpecBinding:
|
||||
market.tradableInstrument.instrument.product.dataSourceSpecBinding
|
||||
.settlementDataProperty,
|
||||
tradingTerminationOracleId:
|
||||
market.tradableInstrument.instrument.product
|
||||
.dataSourceSpecForTradingTermination.id,
|
||||
settlementAsset:
|
||||
market.tradableInstrument.instrument.product.settlementAsset,
|
||||
realisedPNL: position?.node.realisedPNL,
|
||||
};
|
||||
|
||||
return row;
|
||||
});
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<ClosedMarketsDataGrid rowData={rowData} />
|
||||
<div className="pointer-events-none absolute inset-0">
|
||||
<AsyncRenderer
|
||||
loading={loading}
|
||||
error={error}
|
||||
data={marketData}
|
||||
noDataMessage={t('No markets')}
|
||||
reload={reload}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
|
||||
const openAssetDialog = useAssetDetailsDialogStore((store) => store.open);
|
||||
const colDefs = useMemo(() => {
|
||||
const cols: ColDef[] = [
|
||||
{
|
||||
headerName: t('Market'),
|
||||
field: 'code',
|
||||
},
|
||||
{
|
||||
headerName: t('Description'),
|
||||
field: 'name',
|
||||
},
|
||||
{
|
||||
headerName: t('Status'),
|
||||
field: 'state',
|
||||
valueFormatter: ({ value }: VegaValueFormatterParams<Row, 'state'>) => {
|
||||
if (!value) return '-';
|
||||
return MarketStateMapping[value];
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Settlement date'),
|
||||
colId: 'settlementDate', // colId needed if no field property provided otherwise column order is ruined in tests
|
||||
valueGetter: ({ data }: { data: Row }) => {
|
||||
return getMarketExpiryDate(data.metadata);
|
||||
},
|
||||
cellRenderer: ({ value, data }: { value: Date | null; data: Row }) => {
|
||||
return (
|
||||
<SettlementDateCell
|
||||
oracleSpecId={data.tradingTerminationOracleId}
|
||||
metaDate={value}
|
||||
marketState={data.state}
|
||||
closeTimestamp={data.closeTimestamp}
|
||||
/>
|
||||
);
|
||||
},
|
||||
cellClassRules: {
|
||||
'text-danger': ({
|
||||
value,
|
||||
data,
|
||||
}: {
|
||||
value: Date | null;
|
||||
data: Row;
|
||||
}) => {
|
||||
const date = data.closeTimestamp
|
||||
? new Date(data.closeTimestamp)
|
||||
: value;
|
||||
|
||||
if (!date) return false;
|
||||
|
||||
if (
|
||||
// expiry has passed and market is not yet settled
|
||||
isAfter(new Date(), date) &&
|
||||
data.state !== MarketState.STATE_SETTLED
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Best bid'),
|
||||
field: 'bestBidPrice',
|
||||
type: 'numericColumn',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'bestBidPrice'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Best offer'),
|
||||
field: 'bestOfferPrice',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'bestOfferPrice'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Mark price'),
|
||||
field: 'markPrice',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'markPrice'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Settlement price'),
|
||||
type: 'numericColumn',
|
||||
field: 'settlementDataOracleId',
|
||||
// 'tradableInstrument.instrument.product.dataSourceSpecForSettlementData.id',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaICellRendererParams<Row, 'settlementDataOracleId'>) => (
|
||||
<SettlementPriceCell
|
||||
oracleSpecId={value}
|
||||
decimalPlaces={data?.decimalPlaces ?? 0}
|
||||
settlementDataSpecBinding={data?.settlementDataSpecBinding}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Realised PNL'),
|
||||
field: 'realisedPNL',
|
||||
cellClass: 'font-mono ag-right-aligned-cell',
|
||||
type: 'numericColumn',
|
||||
valueFormatter: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'realisedPNL'>) => {
|
||||
if (!value || !data) return '-';
|
||||
return addDecimalsFormatNumber(value, data.decimalPlaces);
|
||||
},
|
||||
},
|
||||
{
|
||||
headerName: t('Settlement asset'),
|
||||
field: 'settlementAsset',
|
||||
cellRenderer: ({
|
||||
value,
|
||||
data,
|
||||
}: VegaValueFormatterParams<Row, 'settlementAsset'>) => (
|
||||
<button
|
||||
className="underline"
|
||||
onClick={() => {
|
||||
if (!value) return;
|
||||
openAssetDialog(value.id);
|
||||
}}
|
||||
>
|
||||
{value ? value.symbol : '-'}
|
||||
</button>
|
||||
),
|
||||
},
|
||||
{
|
||||
headerName: t('Market ID'),
|
||||
field: 'id',
|
||||
},
|
||||
];
|
||||
return cols;
|
||||
}, [openAssetDialog]);
|
||||
|
||||
return (
|
||||
<AgGrid
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
rowData={rowData}
|
||||
columnDefs={colDefs}
|
||||
getRowId={({ data }) => data.id}
|
||||
defaultColDef={{
|
||||
flex: 1,
|
||||
resizable: true,
|
||||
}}
|
||||
overlayNoRowsTemplate="No data"
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -5,6 +5,7 @@ import { LocalStoragePersistTabs as Tabs, Tab } from '@vegaprotocol/ui-toolkit';
|
||||
import { Markets } from './markets';
|
||||
import { Proposed } from './proposed';
|
||||
import { usePageTitleStore } from '../../stores';
|
||||
import { Closed } from './closed';
|
||||
|
||||
export const MarketsPage = () => {
|
||||
const { updateTitle } = usePageTitleStore((store) => ({
|
||||
@@ -21,6 +22,9 @@ export const MarketsPage = () => {
|
||||
<Tab id="proposed-markets" name={t('Proposed markets')}>
|
||||
<Proposed />
|
||||
</Tab>
|
||||
<Tab id="closed-markets" name={t('Closed markets')}>
|
||||
<Closed />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,9 @@ export const Proposed = () => {
|
||||
const externalLink = tokenLink(TOKEN_NEW_MARKET_PROPOSAL);
|
||||
return (
|
||||
<>
|
||||
<ProposalsList />
|
||||
<div className="min-h-[200px]">
|
||||
<ProposalsList />
|
||||
</div>
|
||||
<ExternalLink className="py-4 px-[11px] text-sm" href={externalLink}>
|
||||
{t('Propose a new market')}
|
||||
</ExternalLink>
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { addDays, subDays } from 'date-fns';
|
||||
import type { SettlementDataCellProps } from './settlement-date-cell';
|
||||
import { SettlementDateCell } from './settlement-date-cell';
|
||||
|
||||
describe('SettlementDateCell', () => {
|
||||
let originalNow: typeof Date.now;
|
||||
const mockNowTimestamp = 1672531200000;
|
||||
|
||||
const createProps = (): SettlementDataCellProps => {
|
||||
return {
|
||||
oracleSpecId: 'oracle-spec-id',
|
||||
metaDate: null,
|
||||
closeTimestamp: null,
|
||||
marketState: MarketState.STATE_SETTLED,
|
||||
};
|
||||
};
|
||||
|
||||
beforeAll(() => {
|
||||
originalNow = Date.now;
|
||||
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
Date.now = originalNow;
|
||||
});
|
||||
|
||||
it('renders unknown if date cannot be determined', () => {
|
||||
const props = createProps();
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent('Unknown');
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders using close timestamp if provided', () => {
|
||||
const daysAgo = 3;
|
||||
const props = createProps();
|
||||
props.closeTimestamp = subDays(
|
||||
new Date(mockNowTimestamp),
|
||||
daysAgo
|
||||
).toISOString();
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`${daysAgo} days ago`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders using meta tag date if no close timestamp provided', () => {
|
||||
const daysAgo = 4;
|
||||
const props = createProps();
|
||||
props.metaDate = subDays(new Date(mockNowTimestamp), daysAgo);
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`${daysAgo} days ago`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders past expected settlement date', () => {
|
||||
const daysAgo = 3;
|
||||
const props = createProps();
|
||||
props.metaDate = subDays(new Date(mockNowTimestamp), daysAgo);
|
||||
props.marketState = MarketState.STATE_TRADING_TERMINATED;
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`Expected ${daysAgo} days ago`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders future expected settlement date', () => {
|
||||
const daysAgo = 3;
|
||||
const props = createProps();
|
||||
props.metaDate = addDays(new Date(mockNowTimestamp), daysAgo);
|
||||
props.marketState = MarketState.STATE_TRADING_TERMINATED;
|
||||
|
||||
render(<SettlementDateCell {...props} />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link).toHaveTextContent(`Expected in ${daysAgo} days`);
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { MarketState } from '@vegaprotocol/types';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { formatDistanceToNowStrict, isAfter } from 'date-fns';
|
||||
|
||||
export interface SettlementDataCellProps {
|
||||
oracleSpecId: string;
|
||||
metaDate: Date | null;
|
||||
closeTimestamp: string | null;
|
||||
marketState: MarketState;
|
||||
}
|
||||
|
||||
export const SettlementDateCell = ({
|
||||
oracleSpecId,
|
||||
metaDate,
|
||||
closeTimestamp,
|
||||
marketState,
|
||||
}: SettlementDataCellProps) => {
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const date = closeTimestamp ? new Date(closeTimestamp) : metaDate;
|
||||
|
||||
let text = '';
|
||||
if (!date) {
|
||||
text = t('Unknown');
|
||||
} else {
|
||||
// pass Date.now() to date constructor for easier mocking
|
||||
const expiryHasPassed = isAfter(new Date(Date.now()), date);
|
||||
const distance = formatDistanceToNowStrict(date); // X days/mins ago
|
||||
|
||||
if (expiryHasPassed) {
|
||||
if (marketState !== MarketState.STATE_SETTLED) {
|
||||
text = t('Expected %s ago', distance);
|
||||
} else {
|
||||
text = t('%s ago', distance);
|
||||
}
|
||||
} else {
|
||||
text = t('Expected in %s', distance);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={linkCreator(EXPLORER_ORACLE.replace(':id', oracleSpecId))}
|
||||
className="underline"
|
||||
target="_blank"
|
||||
title={
|
||||
date ? getDateTimeFormat().format(date) : t('Unknown settlement date')
|
||||
}
|
||||
>
|
||||
{text}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,113 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import type { Property } from '@vegaprotocol/types';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { OracleSpecDataConnectionQuery } from '@vegaprotocol/oracles';
|
||||
import { OracleSpecDataConnectionDocument } from '@vegaprotocol/oracles';
|
||||
import type { SettlementPriceCellProps } from './settlement-price-cell';
|
||||
import { SettlementPriceCell } from './settlement-price-cell';
|
||||
|
||||
describe('SettlementPriceCell', () => {
|
||||
const createMock = (
|
||||
id: string,
|
||||
property: Property
|
||||
): MockedResponse<OracleSpecDataConnectionQuery> => {
|
||||
return {
|
||||
request: {
|
||||
query: OracleSpecDataConnectionDocument,
|
||||
variables: {
|
||||
oracleSpecId: id,
|
||||
},
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
oracleSpec: {
|
||||
dataConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
externalData: {
|
||||
data: {
|
||||
data: [property],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
const createProps = (): SettlementPriceCellProps => {
|
||||
return {
|
||||
oracleSpecId: 'oracle-spec-id',
|
||||
decimalPlaces: 2,
|
||||
settlementDataSpecBinding: 'settlement-data-spec-binding',
|
||||
};
|
||||
};
|
||||
it('renders fetches and renders the settlment data value', async () => {
|
||||
const props = createProps();
|
||||
const property = {
|
||||
__typename: 'Property' as const,
|
||||
name: props.settlementDataSpecBinding as string,
|
||||
value: '1234',
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mock = createMock(props.oracleSpecId!, property);
|
||||
|
||||
render(
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<SettlementPriceCell {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
const link = await screen.findByRole('link');
|
||||
expect(link).toHaveTextContent('12.34');
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders "-" if no spec value is found', async () => {
|
||||
const props = createProps();
|
||||
const property = {
|
||||
__typename: 'Property' as const,
|
||||
name: 'no matching spec binding',
|
||||
value: '1234',
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const mock = createMock(props.oracleSpecId!, property);
|
||||
|
||||
render(
|
||||
<MockedProvider mocks={[mock]}>
|
||||
<SettlementPriceCell {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
const link = await screen.findByRole('link');
|
||||
expect(link).toHaveTextContent('-');
|
||||
expect(link).toHaveAttribute(
|
||||
'href',
|
||||
expect.stringContaining(`/oracles/${props.oracleSpecId}`)
|
||||
);
|
||||
});
|
||||
|
||||
it('renders "-" with no link if oracle spec id is not provided', () => {
|
||||
const props = createProps();
|
||||
props.oracleSpecId = undefined;
|
||||
|
||||
render(
|
||||
<MockedProvider mocks={[]}>
|
||||
<SettlementPriceCell {...props} />
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
expect(screen.getByText('-')).toBeInTheDocument();
|
||||
expect(screen.queryByRole('link')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import { DApp, EXPLORER_ORACLE, useLinks } from '@vegaprotocol/environment';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useOracleSpecBindingData } from '@vegaprotocol/oracles';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
||||
|
||||
export interface SettlementPriceCellProps {
|
||||
oracleSpecId: string | undefined;
|
||||
decimalPlaces: number;
|
||||
settlementDataSpecBinding: string | undefined;
|
||||
}
|
||||
|
||||
export const SettlementPriceCell = ({
|
||||
oracleSpecId,
|
||||
decimalPlaces,
|
||||
settlementDataSpecBinding,
|
||||
}: SettlementPriceCellProps) => {
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const { property, loading } = useOracleSpecBindingData(
|
||||
oracleSpecId,
|
||||
settlementDataSpecBinding
|
||||
);
|
||||
|
||||
if (!oracleSpecId || loading) {
|
||||
return <span>-</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Link
|
||||
href={linkCreator(EXPLORER_ORACLE.replace(':id', oracleSpecId))}
|
||||
className="underline font-mono"
|
||||
target="_blank"
|
||||
>
|
||||
{property
|
||||
? addDecimalsFormatNumber(property.value, decimalPlaces)
|
||||
: t('-')}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
@@ -7,6 +7,7 @@ import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { usePaneLayout } from '@vegaprotocol/react-helpers';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { DepositsContainer } from './deposits-container';
|
||||
import { LayoutPriority } from 'allotment';
|
||||
@@ -34,11 +35,11 @@ export const Portfolio = () => {
|
||||
|
||||
const onMarketClick = useMarketClickHandler(true);
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler(true);
|
||||
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'portfolio' });
|
||||
const wrapperClasses = 'h-full max-h-full flex flex-col';
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<ResizableGrid vertical>
|
||||
<ResizableGrid vertical onChange={handleOnLayoutChange}>
|
||||
<ResizableGridPanel minSize={75}>
|
||||
<PortfolioGridChild>
|
||||
<Tabs storageKey="console-portfolio-top">
|
||||
@@ -78,7 +79,7 @@ export const Portfolio = () => {
|
||||
</ResizableGridPanel>
|
||||
<ResizableGridPanel
|
||||
priority={LayoutPriority.Low}
|
||||
preferredSize={300}
|
||||
preferredSize={sizes[1] || 300}
|
||||
minSize={50}
|
||||
>
|
||||
<PortfolioGridChild>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export { Settings as default } from './settings';
|
||||
export { SettingsButton } from './settings-button';
|
||||
@@ -0,0 +1,16 @@
|
||||
import { Icon, NavigationLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Links, Routes } from '../../pages/client-router';
|
||||
import { COG } from '@blueprintjs/icons/src/generated/iconNames';
|
||||
|
||||
export const SettingsButton = ({ withMobile }: { withMobile?: boolean }) => {
|
||||
return (
|
||||
<NavigationLink data-testid="Settings" to={Links[Routes.SETTINGS]()}>
|
||||
{withMobile ? (
|
||||
t('Settings')
|
||||
) : (
|
||||
<Icon name={COG} className="!align-middle" />
|
||||
)}
|
||||
</NavigationLink>
|
||||
);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user