Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc8f40051c | ||
|
|
7af0915a32 | ||
|
|
ab1a2cb222 | ||
|
|
cdd91c24f2 | ||
|
|
e3eb13ca72 | ||
|
|
3f0ebbf33d | ||
|
|
026aa3964b | ||
|
|
e1a1ae0fa1 | ||
|
|
aa4c5a4a57 | ||
|
|
0874314d2a | ||
|
|
a70a7bcc8e | ||
|
|
4461a6a497 | ||
|
|
1041864ad8 | ||
|
|
015e0188ec | ||
|
|
228236c152 | ||
|
|
cd6e906dc6 | ||
|
|
8a47a92dbd | ||
|
|
af14c27df9 | ||
|
|
38edcd88c0 | ||
|
|
a74cf02030 | ||
|
|
6686755763 | ||
|
|
79cf8163be | ||
|
|
1688e26f4b | ||
|
|
5e101a8eaf | ||
|
|
65071f57d0 | ||
|
|
b87c981a90 | ||
|
|
b509e5b347 | ||
|
|
bb9fce4831 | ||
|
|
918825aca8 | ||
|
|
74bf183fad | ||
|
|
674baa1ee3 | ||
|
|
d18a0c7e15 | ||
|
|
76a6005b77 | ||
|
|
79414614c2 | ||
|
|
fde49e5446 | ||
|
|
45283161aa | ||
|
|
375d447fa8 | ||
|
|
1dd97a2bce | ||
|
|
d4a1a9b193 | ||
|
|
376c5241a8 | ||
|
|
8e6d8517cf | ||
|
|
5f9ec222c1 | ||
|
|
8954c41c0a | ||
|
|
4414ab4f47 | ||
|
|
d1d4bacc68 | ||
|
|
f476688c7f | ||
|
|
8845222700 | ||
|
|
e64c464091 | ||
|
|
b2c2d0d7d6 | ||
|
|
1fb61d313c | ||
|
|
ccf5ff632c | ||
|
|
54d6aaf56f | ||
|
|
736b262947 | ||
|
|
6a07127185 | ||
|
|
45f0ba7c0e | ||
|
|
6f4ea68778 |
+2
-1
@@ -51,7 +51,8 @@
|
||||
"ul": ["list"],
|
||||
"ol": ["list"]
|
||||
}
|
||||
]
|
||||
],
|
||||
"no-console": ["error", { "allow": ["warn", "error"] }]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: Feature Epic
|
||||
about: A template to capture and scope user requirements, high level process, and basic mockups for an upcoming feature as part of the initial core spec review process.
|
||||
title: 'Epic: '
|
||||
labels: feature-epic
|
||||
---
|
||||
|
||||
## Core Feature
|
||||
|
||||
<Name>
|
||||
|
||||
## Tasks
|
||||
|
||||
- [ ] Define high level requirements
|
||||
- [ ] Create basic mockups
|
||||
- [ ] Update "API Requirements" in core spec
|
||||
- [ ] Update "User-Interface Spec" in relevant front end repo
|
||||
- [ ] Create detailed user stories using normal template
|
||||
|
||||
## High Level Requirements
|
||||
|
||||
## Basic Mockups
|
||||
|
||||
## Link to API Requirements in Core spec
|
||||
|
||||
## Link to User Interface Specs
|
||||
|
||||
## Linked User Stories
|
||||
@@ -7,7 +7,7 @@ on:
|
||||
jobs:
|
||||
after-release:
|
||||
runs-on: ubuntu-22.04
|
||||
timeout-minutes: 30
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -30,18 +30,20 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Wait for publish to complete
|
||||
uses: lewagon/wait-on-check-action@v1.3.1
|
||||
with:
|
||||
ref: ${{ github.event.release.tag_name }}
|
||||
check-name: '(CD) publish dist / trading'
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
wait-interval: 10
|
||||
|
||||
- name: resolve ipfs hashes for release
|
||||
run: |
|
||||
echo "Name: ${{ github.event.release.name }}"
|
||||
echo "Description: ${{ github.event.release.body }}"
|
||||
echo "Tag: ${{ github.event.release.tag_name }}"
|
||||
commit="$(git rev-list -n 1 ${{ github.event.release.tag_name }})"
|
||||
echo "Commit: $commit"
|
||||
until docker pull vegaprotocol/trading:$commit; do
|
||||
echo "Image not pushed yet, waiting 60 seconds"
|
||||
sleep 60
|
||||
done
|
||||
docker run --rm vegaprotocol/trading:$commit cat /ipfs-hash > ipfs-hash
|
||||
docker run --rm vegaprotocol/trading:mainnet cat /ipfs-hash > ipfs-hash
|
||||
curl -L https://dist.ipfs.tech/kubo/v0.20.0/kubo_v0.20.0_linux-amd64.tar.gz -o kubo.tgz
|
||||
tar -xzf kubo.tgz
|
||||
export PATH="$PATH:$PWD/kubo"
|
||||
|
||||
@@ -29,6 +29,12 @@ jobs:
|
||||
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
|
||||
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
|
||||
echo IS_S3_RELEASE=false >> $GITHUB_ENV
|
||||
echo IS_DEV_IMAGE=false >> $GITHUB_ENV
|
||||
|
||||
- name: Is dev image
|
||||
if: ${{ contains(github.ref, 'develop') && github.event_name == 'push' && matrix.app == 'trading' }}
|
||||
run: |
|
||||
echo IS_DEV_IMAGE=true >> $GITHUB_ENV
|
||||
|
||||
- name: Is PR
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
@@ -36,7 +42,7 @@ jobs:
|
||||
echo IS_PR=true >> $GITHUB_ENV
|
||||
|
||||
- name: Is mainnet release
|
||||
if: ${{ contains(github.ref, 'release/mainnnet') && !contains(github.ref, 'mirror') }}
|
||||
if: ${{ contains(github.ref, 'release/mainnet') && !contains(github.ref, 'mirror') }}
|
||||
run: |
|
||||
echo IS_MAINNET_RELEASE=true >> $GITHUB_ENV
|
||||
|
||||
@@ -75,7 +81,7 @@ jobs:
|
||||
|
||||
- name: Log in to the Container registry (docker hub)
|
||||
uses: docker/login-action@v2
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
@@ -118,8 +124,12 @@ jobs:
|
||||
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
|
||||
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
|
||||
DIST_LOCATION=dist/storybook/ui-toolkit
|
||||
elif [ "${{ matrix.app }}" = "static" ]; then
|
||||
yarn nx build static || (yarn install && yarn nx build static)
|
||||
else
|
||||
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
|
||||
fi
|
||||
if [[ -z "$DIST_LOCATION" ]]; then
|
||||
DIST_LOCATION=dist/apps/${{ matrix.app }}
|
||||
fi
|
||||
mv $DIST_LOCATION dist-result
|
||||
@@ -169,7 +179,7 @@ jobs:
|
||||
uses: docker/build-push-action@v3
|
||||
continue-on-error: true
|
||||
id: dockerhub-push
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' }}
|
||||
if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
|
||||
with:
|
||||
context: .
|
||||
file: docker/node-outside-docker.Dockerfile
|
||||
@@ -179,7 +189,7 @@ jobs:
|
||||
ENV_NAME=${{ env.ENV_NAME }}
|
||||
tags: |
|
||||
vegaprotocol/${{ matrix.app }}:${{ github.sha }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || '' }}
|
||||
vegaprotocol/${{ matrix.app }}:${{ env.IS_MAINNET_RELEASE == 'true' && 'mainnet' || env.IS_TESTNET_RELEASE == 'true' && 'testnet' || env.IS_DEV_IMAGE == 'true' && 'develop' || '' }}
|
||||
|
||||
- name: Publish dist as docker image (ghcr - retry)
|
||||
uses: docker/build-push-action@v3
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
context('Proposal page', { tags: '@smoke' }, function () {
|
||||
describe('Verify elements on page', function () {
|
||||
const proposalHeading = 'proposals-heading';
|
||||
const dateTimeRegex =
|
||||
/(\d{1,2})\/(\d{1,2})\/(\d{4}), (\d{1,2}):(\d{1,2}):(\d{1,2})/gm;
|
||||
const proposalTitle = 'Add Lorem Ipsum market';
|
||||
|
||||
before('Create market proposal', function () {
|
||||
cy.visit('/');
|
||||
cy.createMarket();
|
||||
});
|
||||
|
||||
it('Able to view proposal', function () {
|
||||
cy.navigate_to('governanceProposals');
|
||||
cy.getByTestId(proposalHeading).should('be.visible');
|
||||
// get first proposal in list
|
||||
cy.get('[row-index="0"]').within(() => {
|
||||
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
|
||||
cy.get_element_by_col_id('type').should('have.text', 'NewMarket');
|
||||
cy.get_element_by_col_id('state').should('have.text', 'Enacted');
|
||||
cy.getByTestId('vote-progress').should('be.visible');
|
||||
cy.get('[col-id="cDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
cy.get('[col-id="eDate"]')
|
||||
.invoke('text')
|
||||
.should('match', dateTimeRegex);
|
||||
cy.getByTestId('external-link')
|
||||
.should('have.attr', 'href')
|
||||
.and('contains', 'https://governance.fairground.wtf/proposals/');
|
||||
cy.contains('View terms').should('exist').click();
|
||||
});
|
||||
cy.getByTestId('dialog-title').should('have.text', proposalTitle);
|
||||
cy.get('.language-json').should('exist');
|
||||
});
|
||||
|
||||
it('Proposal page displayed on mobile', function () {
|
||||
cy.common_switch_to_mobile_and_click_toggle();
|
||||
cy.navigate_to('governanceProposals', true);
|
||||
cy.getByTestId(proposalHeading).should('be.visible');
|
||||
cy.get('[row-index="0"]').within(() => {
|
||||
cy.get_element_by_col_id('title').should('have.text', proposalTitle);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,11 @@ context('Validator page', { tags: '@smoke' }, function () {
|
||||
it('should be able to see validator tiles', function () {
|
||||
cy.getNodes().then((nodes) => {
|
||||
nodes.forEach((node) => {
|
||||
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
|
||||
if (node.rankingScore.performanceScore > 0) {
|
||||
cy.get(`[validator-id="${node.id}"]`).should('be.visible');
|
||||
} else {
|
||||
cy.get(`[validator-id="${node.id}"]`).should('not.exist');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -23,7 +23,7 @@ export const Footer = () => {
|
||||
);
|
||||
|
||||
return (
|
||||
<footer className="grid grid-rows-2 lg:grid-cols-[1fr_auto] text-xs md:text-md lg:flex md:col-span-2 px-4 py-2 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||
<footer className="grid grid-cols-[1fr_auto] items-center text-xs md:text-md lg:flex md:col-span-2 px-4 pt-2 pb-3 gap-4 border-t border-vega-light-200 dark:border-vega-dark-200">
|
||||
<div className="flex justify-between gap-2 align-middle">
|
||||
{GIT_COMMIT_HASH && (
|
||||
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
@@ -43,9 +43,12 @@ export const Footer = () => {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4">
|
||||
{VEGA_URL && <NodeUrl url={VEGA_URL} />}
|
||||
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}>
|
||||
<div className="content-center flex pr-4 md:border-r border-neutral-700 dark:border-neutral-300">
|
||||
<span className="pr-2">{VEGA_URL && <NodeUrl url={VEGA_URL} />}</span>
|
||||
<Link
|
||||
className="ml-2 underline-offset-4"
|
||||
onClick={() => setNodeSwitcherOpen(true)}
|
||||
>
|
||||
{t('Change')}
|
||||
</Link>
|
||||
</div>
|
||||
@@ -59,7 +62,10 @@ export const Footer = () => {
|
||||
) : null}
|
||||
</div>
|
||||
<div className="pl-2 align-center lg:align-right lg:flex lg:justify-end gap-2 align-middle lg:max-w-xs lg:ml-auto">
|
||||
<RouteLink to={`/${Routes.DISCLAIMER}`} className="underline">
|
||||
<RouteLink
|
||||
to={`/${Routes.DISCLAIMER}`}
|
||||
className="underline underline-offset-4"
|
||||
>
|
||||
Disclaimer
|
||||
</RouteLink>
|
||||
</div>
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuSubContent,
|
||||
Icon,
|
||||
Button,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { Dispatch, SetStateAction } from 'react';
|
||||
import { FilterLabel } from './tx-filter-label';
|
||||
@@ -100,15 +99,13 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
|
||||
<DropdownMenu
|
||||
modal={false}
|
||||
trigger={
|
||||
<DropdownMenuTrigger className="ml-0">
|
||||
<Button size="xs" data-testid="filter-trigger">
|
||||
<FilterLabel filters={filters} />
|
||||
</Button>
|
||||
<DropdownMenuTrigger>
|
||||
<FilterLabel filters={filters} />
|
||||
</DropdownMenuTrigger>
|
||||
}
|
||||
>
|
||||
<DropdownMenuContent>
|
||||
{filters.size > 0 ? null : (
|
||||
{filters.size > 1 ? null : (
|
||||
<>
|
||||
<DropdownMenuCheckboxItem
|
||||
onCheckedChange={() => setFilters(new Set(AllFilterOptions))}
|
||||
|
||||
@@ -44,7 +44,7 @@ export const TxsListNavigation = ({
|
||||
</Button>
|
||||
<Button
|
||||
size="xs"
|
||||
disabled={!hasMoreTxs || loading}
|
||||
disabled={!hasMoreTxs}
|
||||
onClick={() => {
|
||||
nextPage();
|
||||
}}
|
||||
|
||||
@@ -86,14 +86,18 @@ describe('Txs infinite list item', () => {
|
||||
render(
|
||||
<MockedProvider>
|
||||
<MemoryRouter>
|
||||
<TxsInfiniteListItem
|
||||
type="testType"
|
||||
submitter="testPubKey"
|
||||
hash="testTxHash"
|
||||
block="1"
|
||||
code={0}
|
||||
command={{}}
|
||||
/>
|
||||
<table>
|
||||
<tbody>
|
||||
<TxsInfiniteListItem
|
||||
type="testType"
|
||||
submitter="testPubKey"
|
||||
hash="testTxHash"
|
||||
block="1"
|
||||
code={0}
|
||||
command={{}}
|
||||
/>
|
||||
</tbody>
|
||||
</table>
|
||||
</MemoryRouter>
|
||||
</MockedProvider>
|
||||
);
|
||||
|
||||
@@ -14,9 +14,9 @@ const DEFAULT_TRUNCATE_LENGTH = 7;
|
||||
|
||||
export function getIdTruncateLength(screen: Screen): number {
|
||||
if (['xxxl', 'xxl'].includes(screen)) {
|
||||
return 64;
|
||||
} else if (['xl', 'lg', 'md'].includes(screen)) {
|
||||
return 32;
|
||||
} else if (['xl', 'lg', 'md'].includes(screen)) {
|
||||
return 16;
|
||||
}
|
||||
return DEFAULT_TRUNCATE_LENGTH;
|
||||
}
|
||||
|
||||
@@ -5,11 +5,17 @@ import type { BlockExplorerTransactionResult } from '../../routes/types/block-ex
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
const generateHash = (): string =>
|
||||
Array.from(
|
||||
{ length: 64 },
|
||||
() => '0123456789ABCDEF'[Math.floor(Math.random() * 16)]
|
||||
).join('');
|
||||
|
||||
const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
|
||||
return Array.from(Array(number)).map((_) => ({
|
||||
block: '87901',
|
||||
index: 2,
|
||||
hash: '0F8B98DA0923A50786B852D9CA11E051CACC4C733E1DB93D535C7D81DBD10F6F',
|
||||
hash: generateHash(),
|
||||
submitter:
|
||||
'4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
|
||||
type: 'Submit Order',
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Table, TableRow } from '../table';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response';
|
||||
import { getTxsDataUrl } from '../../hooks/use-txs-data';
|
||||
import { getTxsDataUrl } from '../../hooks/get-txs-data-url';
|
||||
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
|
||||
import EmptyList from '../empty-list/empty-list';
|
||||
import { TxsInfiniteListItem } from './txs-infinite-list-item';
|
||||
@@ -14,7 +14,7 @@ interface TxsPerBlockProps {
|
||||
|
||||
export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
|
||||
const filters = `filters[block.height]=${blockHeight}`;
|
||||
const url = getTxsDataUrl({ limit: txCount.toString(), filters });
|
||||
const url = getTxsDataUrl({ filters, count: txCount });
|
||||
const {
|
||||
state: { data, loading, error },
|
||||
} = useFetch<BlockExplorerTransactions>(url);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { DATA_SOURCES } from '../config';
|
||||
|
||||
type IGetTxsDataFirstPage = {
|
||||
baseUrl?: string;
|
||||
count?: number;
|
||||
party?: string;
|
||||
filters?: string;
|
||||
};
|
||||
|
||||
interface IGetTxsDataPrevious extends IGetTxsDataFirstPage {
|
||||
before: string;
|
||||
}
|
||||
|
||||
interface IGetTxsDataNext extends IGetTxsDataFirstPage {
|
||||
after: string;
|
||||
}
|
||||
|
||||
type IGetTxsDataUrl =
|
||||
| IGetTxsDataPrevious
|
||||
| IGetTxsDataNext
|
||||
| IGetTxsDataFirstPage;
|
||||
|
||||
export const BE_TXS_PER_REQUEST = 25;
|
||||
|
||||
/**
|
||||
* Properly encodes the filters and parameters for a request to the block explorer
|
||||
* API for transactions. As the API uses a slightly less common format for encoding
|
||||
* filters, some of it is more manual than you might expect.
|
||||
*
|
||||
* @param params An object containing the pagination and filters
|
||||
* @returns string URL to call
|
||||
*/
|
||||
export const getTxsDataUrl = (params: IGetTxsDataUrl) => {
|
||||
const baseUrl =
|
||||
params.baseUrl || `${DATA_SOURCES.blockExplorerUrl}/transactions`;
|
||||
const url = new URL(baseUrl);
|
||||
const count = `${params.count || BE_TXS_PER_REQUEST}`;
|
||||
|
||||
if ('before' in params && params.before?.length > 0) {
|
||||
url.searchParams.append('last', count);
|
||||
url.searchParams.append('before', params.before);
|
||||
} else if ('after' in params && params.after?.length > 0) {
|
||||
url.searchParams.append('first', count);
|
||||
url.searchParams.append('after', params.after);
|
||||
} else {
|
||||
url.searchParams.append('first', count);
|
||||
}
|
||||
|
||||
// Hacky fix for param as array
|
||||
let urlAsString = url.toString();
|
||||
if (params.filters && params.filters?.length > 0) {
|
||||
urlAsString += '&' + params.filters.replaceAll(' ', '%20');
|
||||
}
|
||||
if (params.party && params.party?.length > 0) {
|
||||
urlAsString += `&filters[tx.submitter]=${params.party}`;
|
||||
}
|
||||
|
||||
return urlAsString;
|
||||
};
|
||||
@@ -0,0 +1,48 @@
|
||||
import { getTxsDataUrl } from './get-txs-data-url'; // import the function to be tested
|
||||
|
||||
describe('getTxsDataUrl', () => {
|
||||
it('should return the correct URL without filters and party', () => {
|
||||
const params = {
|
||||
count: 10,
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl = 'https://example.com/transactions?first=10';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
|
||||
it('should return the correct URL with "before" in params', () => {
|
||||
const params = {
|
||||
count: 5,
|
||||
before: '100.1',
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl = 'https://example.com/transactions?last=5&before=100.1';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
|
||||
it('should return the correct URL with "after" in params', () => {
|
||||
const params = {
|
||||
count: 5,
|
||||
after: '222.1',
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl = 'https://example.com/transactions?first=5&after=222.1';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
|
||||
it('should return the correct URL with filters and party', () => {
|
||||
const params = {
|
||||
count: 10,
|
||||
filters: 'filters[cmd.type]=Made Up Transaction',
|
||||
party: '1234',
|
||||
baseUrl: 'https://example.com/transactions',
|
||||
};
|
||||
const expectedUrl =
|
||||
'https://example.com/transactions?first=10&filters[cmd.type]=Made%20Up%20Transaction&filters[tx.submitter]=1234';
|
||||
|
||||
expect(getTxsDataUrl(params)).toEqual(expectedUrl);
|
||||
});
|
||||
});
|
||||
@@ -1,130 +1,141 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import type { URLSearchParamsInit } from 'react-router-dom';
|
||||
import { useCallback } from 'react';
|
||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||
import type {
|
||||
BlockExplorerTransactionResult,
|
||||
BlockExplorerTransactions,
|
||||
} from '../routes/types/block-explorer-response';
|
||||
import { DATA_SOURCES } from '../config';
|
||||
import isNumber from 'lodash/isNumber';
|
||||
import { AllFilterOptions } from '../components/txs/tx-filter';
|
||||
import type { FilterOption } from '../components/txs/tx-filter';
|
||||
import { BE_TXS_PER_REQUEST, getTxsDataUrl } from './get-txs-data-url';
|
||||
|
||||
export interface TxsStateProps {
|
||||
txsData: BlockExplorerTransactionResult[];
|
||||
hasMoreTxs: boolean;
|
||||
cursor: string;
|
||||
previousCursors: string[];
|
||||
hasPreviousPage: boolean;
|
||||
export function getTypeFilters(filters?: Set<FilterOption>) {
|
||||
if (!filters) {
|
||||
return '';
|
||||
} else if (filters.size > 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const forcedSingleFilter = Array.from(filters)[0];
|
||||
return `filters[cmd.type]=${forcedSingleFilter}`;
|
||||
}
|
||||
|
||||
export interface IUseTxsData {
|
||||
limit: number;
|
||||
filters?: string;
|
||||
count?: number;
|
||||
before?: string;
|
||||
after?: string;
|
||||
party?: string;
|
||||
filters?: Set<FilterOption>;
|
||||
}
|
||||
|
||||
interface IGetTxsDataUrl {
|
||||
limit: string;
|
||||
filters?: string;
|
||||
}
|
||||
export const useTxsData = ({
|
||||
count = 25,
|
||||
before,
|
||||
after,
|
||||
filters,
|
||||
party,
|
||||
}: IUseTxsData) => {
|
||||
const [, setSearchParams] = useSearchParams();
|
||||
let hasMoreTxs = true;
|
||||
let txsData: BlockExplorerTransactionResult[] = [];
|
||||
|
||||
export const getTxsDataUrl = ({ limit, filters }: IGetTxsDataUrl) => {
|
||||
const url = new URL(`${DATA_SOURCES.blockExplorerUrl}/transactions`);
|
||||
|
||||
if (limit) {
|
||||
url.searchParams.append('limit', limit);
|
||||
}
|
||||
|
||||
// Hacky fix for param as array
|
||||
let urlAsString = url.toString();
|
||||
if (filters) {
|
||||
urlAsString += '&' + filters.replace(' ', '%20');
|
||||
}
|
||||
|
||||
return urlAsString;
|
||||
};
|
||||
|
||||
export const useTxsData = ({ limit, filters }: IUseTxsData) => {
|
||||
const [
|
||||
{ txsData, hasMoreTxs, cursor, previousCursors, hasPreviousPage },
|
||||
setTxsState,
|
||||
] = useState<TxsStateProps>({
|
||||
txsData: [],
|
||||
hasMoreTxs: false,
|
||||
previousCursors: [],
|
||||
cursor: '',
|
||||
hasPreviousPage: false,
|
||||
const url = getTxsDataUrl({
|
||||
filters: getTypeFilters(filters),
|
||||
count,
|
||||
before,
|
||||
after,
|
||||
party,
|
||||
});
|
||||
|
||||
const url = getTxsDataUrl({ limit: limit.toString(), filters });
|
||||
|
||||
const {
|
||||
state: { data, error, loading },
|
||||
refetch,
|
||||
} = useFetch<BlockExplorerTransactions>(url, {}, true);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && data && isNumber(data.transactions.length)) {
|
||||
setTxsState((prev) => {
|
||||
return {
|
||||
...prev,
|
||||
txsData: data.transactions,
|
||||
hasMoreTxs: data.transactions.length >= limit,
|
||||
cursor: data?.transactions.at(-1)?.cursor || '',
|
||||
};
|
||||
});
|
||||
}
|
||||
}, [loading, setTxsState, data, limit]);
|
||||
if (!loading && data && isNumber(data.transactions.length)) {
|
||||
hasMoreTxs = data.transactions.length >= count;
|
||||
txsData = data.transactions;
|
||||
}
|
||||
|
||||
const nextPage = useCallback(() => {
|
||||
const c = data?.transactions.at(0)?.cursor;
|
||||
const newPreviousCursors = c ? [...previousCursors, c] : previousCursors;
|
||||
|
||||
setTxsState((prev) => ({
|
||||
...prev,
|
||||
hasPreviousPage: true,
|
||||
previousCursors: newPreviousCursors,
|
||||
}));
|
||||
|
||||
return refetch({
|
||||
limit,
|
||||
before: cursor,
|
||||
});
|
||||
}, [data, previousCursors, cursor, limit, refetch]);
|
||||
const after = data?.transactions.at(-1)?.cursor || '';
|
||||
const params: URLSearchParamsInit = { after };
|
||||
if (filters) {
|
||||
params.filters = Array.from(filters).join(',');
|
||||
}
|
||||
setSearchParams(params);
|
||||
}, [filters, data, setSearchParams]);
|
||||
|
||||
const previousPage = useCallback(() => {
|
||||
const previousCursor = [...previousCursors].pop();
|
||||
const newPreviousCursors = previousCursors.slice(0, -1);
|
||||
setTxsState((prev) => ({
|
||||
...prev,
|
||||
hasPreviousPage: newPreviousCursors.length > 0,
|
||||
previousCursors: newPreviousCursors,
|
||||
}));
|
||||
return refetch({
|
||||
limit,
|
||||
before: previousCursor,
|
||||
});
|
||||
}, [previousCursors, limit, refetch]);
|
||||
const before = data?.transactions[0]?.cursor || '';
|
||||
const params: URLSearchParamsInit = { before };
|
||||
if (filters && filters.size > 0 && filters.size === 1) {
|
||||
params.filters = Array.from(filters)[0];
|
||||
}
|
||||
setSearchParams(params);
|
||||
}, [filters, data, setSearchParams]);
|
||||
|
||||
const refreshTxs = useCallback(async () => {
|
||||
setTxsState(() => ({
|
||||
txsData: [],
|
||||
cursor: '',
|
||||
previousCursors: [],
|
||||
hasMoreTxs: false,
|
||||
hasPreviousPage: false,
|
||||
}));
|
||||
const params: URLSearchParamsInit = {};
|
||||
if (filters && filters.size > 0 && filters.size === 1) {
|
||||
params.filters = Array.from(filters)[0];
|
||||
}
|
||||
setSearchParams(params);
|
||||
|
||||
refetch({ limit });
|
||||
}, [setTxsState, limit, refetch, filters]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
refetch({ count: BE_TXS_PER_REQUEST });
|
||||
}, [setSearchParams, refetch, filters]);
|
||||
|
||||
const updateFilters = useCallback(
|
||||
(newFilters: Set<FilterOption>) => {
|
||||
const params: URLSearchParamsInit = {};
|
||||
if (newFilters && newFilters.size === 1) {
|
||||
params.filters = Array.from(newFilters)[0];
|
||||
}
|
||||
|
||||
setSearchParams(params);
|
||||
},
|
||||
[setSearchParams]
|
||||
);
|
||||
|
||||
return {
|
||||
updateFilters,
|
||||
txsData,
|
||||
loading,
|
||||
error,
|
||||
hasMoreTxs,
|
||||
hasPreviousPage,
|
||||
previousCursors,
|
||||
cursor,
|
||||
refreshTxs,
|
||||
nextPage,
|
||||
previousPage,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a Set of filters based on the URLSearchParams, or
|
||||
* defaults to all.
|
||||
* @param params
|
||||
* @returns Set
|
||||
*/
|
||||
export function getInitialFilters(params: URLSearchParams): Set<FilterOption> {
|
||||
const defaultFilters = new Set(AllFilterOptions);
|
||||
|
||||
const p = params.get('filters');
|
||||
|
||||
if (!p) {
|
||||
return defaultFilters;
|
||||
}
|
||||
|
||||
const filters = new Set<FilterOption>();
|
||||
p.split(',').forEach((f) => {
|
||||
if (AllFilterOptions.includes(f as FilterOption)) {
|
||||
filters.add(f as FilterOption);
|
||||
}
|
||||
});
|
||||
|
||||
if (filters.size === 0) {
|
||||
return defaultFilters;
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
@@ -204,6 +204,7 @@ describe('Block', () => {
|
||||
(useFetch as jest.Mock).mockReturnValue({
|
||||
state: { data: createBlockResponse(1), loading: false, error: null },
|
||||
});
|
||||
|
||||
render(renderComponent(1));
|
||||
await waitFor(() => screen.getByTestId('block-header'));
|
||||
expect(screen.getByTestId('previous-block-button')).toHaveAttribute(
|
||||
|
||||
@@ -15,9 +15,13 @@ import { PartyBlockAccounts } from './components/party-block-accounts';
|
||||
import { isValidPartyId } from './components/party-id-error';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
|
||||
import type { FilterOption } from '../../../components/txs/tx-filter';
|
||||
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const Party = () => {
|
||||
const [params] = useSearchParams();
|
||||
|
||||
const [filters, setFilters] = useState(new Set(AllFilterOptions));
|
||||
const { party } = useParams<{ party: string }>();
|
||||
|
||||
@@ -27,24 +31,21 @@ const Party = () => {
|
||||
const partyId = toNonHex(party ? party : '');
|
||||
const { isMobile } = useScreenDimensions();
|
||||
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
|
||||
const baseFilters = `filters[tx.submitter]=${partyId}`;
|
||||
const f =
|
||||
filters && filters.size === 1
|
||||
? `${baseFilters}&filters[cmd.type]=${Array.from(filters)[0]}`
|
||||
: baseFilters;
|
||||
|
||||
const {
|
||||
hasMoreTxs,
|
||||
nextPage,
|
||||
refreshTxs,
|
||||
previousPage,
|
||||
error,
|
||||
refreshTxs,
|
||||
loading,
|
||||
txsData,
|
||||
hasPreviousPage,
|
||||
hasMoreTxs,
|
||||
updateFilters,
|
||||
} = useTxsData({
|
||||
limit: 25,
|
||||
filters: f,
|
||||
filters: filters.size === 1 ? filters : undefined,
|
||||
before: params.get('before') || undefined,
|
||||
after: !params.get('before') ? params.get('after') || undefined : undefined,
|
||||
party: partyId,
|
||||
});
|
||||
|
||||
const variables = useMemo(() => ({ partyId }), [partyId]);
|
||||
@@ -102,15 +103,21 @@ const Party = () => {
|
||||
refreshTxs={refreshTxs}
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
hasPreviousPage={hasPreviousPage}
|
||||
hasPreviousPage={true}
|
||||
loading={loading}
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
>
|
||||
<TxsFilter filters={filters} setFilters={setFilters} />
|
||||
<TxsFilter
|
||||
filters={filters}
|
||||
setFilters={(f) => {
|
||||
setFilters(f);
|
||||
updateFilters(f as Set<FilterOption>);
|
||||
}}
|
||||
/>
|
||||
</TxsListNavigation>
|
||||
{!error && txsData ? (
|
||||
<TxsInfiniteList
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
hasMoreTxs={true}
|
||||
areTxsLoading={loading}
|
||||
txs={txsData}
|
||||
loadMoreTxs={nextPage}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { RouteTitle } from '../../../components/route-title';
|
||||
import { TxsInfiniteList } from '../../../components/txs';
|
||||
import { useTxsData } from '../../../hooks/use-txs-data';
|
||||
import { useTxsData, getInitialFilters } from '../../../hooks/use-txs-data';
|
||||
import { useDocumentTitle } from '../../../hooks/use-document-title';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
|
||||
import { TxsFilter } from '../../../components/txs/tx-filter';
|
||||
import type { FilterOption } from '../../../components/txs/tx-filter';
|
||||
import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
|
||||
|
||||
const BE_TXS_PER_REQUEST = 25;
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
export const TxsList = () => {
|
||||
useDocumentTitle(['Transactions']);
|
||||
@@ -27,25 +27,22 @@ export const TxsList = () => {
|
||||
* @returns {JSX.Element} Transaction List and controls
|
||||
*/
|
||||
export const TxsListFiltered = () => {
|
||||
const [filters, setFilters] = useState(new Set(AllFilterOptions));
|
||||
|
||||
const f =
|
||||
filters && filters.size === 1
|
||||
? `filters[cmd.type]=${Array.from(filters)[0]}`
|
||||
: '';
|
||||
const [params] = useSearchParams();
|
||||
const [filters, setFilters] = useState(getInitialFilters(params));
|
||||
|
||||
const {
|
||||
hasMoreTxs,
|
||||
nextPage,
|
||||
previousPage,
|
||||
error,
|
||||
refreshTxs,
|
||||
loading,
|
||||
txsData,
|
||||
hasPreviousPage,
|
||||
hasMoreTxs,
|
||||
updateFilters,
|
||||
} = useTxsData({
|
||||
limit: BE_TXS_PER_REQUEST,
|
||||
filters: f,
|
||||
filters: filters.size === 1 ? filters : undefined,
|
||||
before: params.get('before') || undefined,
|
||||
after: !params.get('before') ? params.get('after') || undefined : undefined,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -54,11 +51,17 @@ export const TxsListFiltered = () => {
|
||||
refreshTxs={refreshTxs}
|
||||
nextPage={nextPage}
|
||||
previousPage={previousPage}
|
||||
hasPreviousPage={hasPreviousPage}
|
||||
hasPreviousPage={true}
|
||||
loading={loading}
|
||||
hasMoreTxs={hasMoreTxs}
|
||||
>
|
||||
<TxsFilter filters={filters} setFilters={setFilters} />
|
||||
<TxsFilter
|
||||
filters={filters}
|
||||
setFilters={(f) => {
|
||||
setFilters(f);
|
||||
updateFilters(f as Set<FilterOption>);
|
||||
}}
|
||||
/>
|
||||
</TxsListNavigation>
|
||||
<TxsInfiniteList
|
||||
hasFilters={filters.size > 0}
|
||||
|
||||
@@ -15,6 +15,9 @@ query ExplorerNodes {
|
||||
stakedByDelegates
|
||||
stakedTotal
|
||||
pendingStake
|
||||
rankingScore {
|
||||
performanceScore
|
||||
}
|
||||
epochData {
|
||||
total
|
||||
offline
|
||||
|
||||
@@ -6,7 +6,7 @@ const defaultOptions = {} as const;
|
||||
export type ExplorerNodesQueryVariables = Types.Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
|
||||
export type ExplorerNodesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, infoUrl: string, avatarUrl?: string | null, pubkey: string, tmPubkey: string, ethereumAddress: string, location: string, status: Types.NodeStatus, stakedByOperator: string, stakedByDelegates: string, stakedTotal: string, pendingStake: string, rankingScore: { __typename?: 'RankingScore', performanceScore: string }, epochData?: { __typename?: 'EpochData', total: number, offline: number, online: number } | null } } | null> | null } };
|
||||
|
||||
|
||||
export const ExplorerNodesDocument = gql`
|
||||
@@ -27,6 +27,9 @@ export const ExplorerNodesDocument = gql`
|
||||
stakedByDelegates
|
||||
stakedTotal
|
||||
pendingStake
|
||||
rankingScore {
|
||||
performanceScore
|
||||
}
|
||||
epochData {
|
||||
total
|
||||
offline
|
||||
|
||||
@@ -91,7 +91,15 @@ export const ValidatorsPage = () => {
|
||||
const { data: tmData } = useTendermintValidators(5000);
|
||||
const { data, loading, error, refetch } = useExplorerNodesQuery();
|
||||
|
||||
const validators = compact(data?.nodesConnection.edges?.map((e) => e?.node));
|
||||
const validators = compact(
|
||||
data?.nodesConnection.edges
|
||||
?.map((e) => e?.node)
|
||||
.filter(
|
||||
(node) =>
|
||||
node?.rankingScore?.performanceScore &&
|
||||
new BigNumber(node.rankingScore.performanceScore).isGreaterThan(0)
|
||||
)
|
||||
);
|
||||
|
||||
// voting power
|
||||
const powers = compact(tmData?.result.validators).map(
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"changes": {
|
||||
"decimalPlaces": "5",
|
||||
"positionDecimalPlaces": "5",
|
||||
"linearSlippageFactor": "0.001",
|
||||
"quadraticSlippageFactor": "0",
|
||||
"lpPriceRange": "10",
|
||||
"instrument": {
|
||||
"name": "Token test market",
|
||||
"code": "Token.24h",
|
||||
"future": {
|
||||
"settlementAsset": "816af99af60d684502a40824758f6b5377e6af48e50a9ee8ef478ecb879ea8bc",
|
||||
"quoteName": "fUSDC",
|
||||
"dataSourceSpecForSettlementData": {
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "prices.BTC.value",
|
||||
"type": "TYPE_INTEGER",
|
||||
"numberDecimalPlaces": "0"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_GREATER_THAN",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecForTradingTermination": {
|
||||
"external": {
|
||||
"oracle": {
|
||||
"signers": [
|
||||
{
|
||||
"pubKey": {
|
||||
"key": "70d14a321e02e71992fd115563df765000ccc4775cbe71a0e2f9ff5a3b9dc680"
|
||||
}
|
||||
}
|
||||
],
|
||||
"filters": [
|
||||
{
|
||||
"key": {
|
||||
"name": "trading.terminated.ETH5",
|
||||
"type": "TYPE_BOOLEAN"
|
||||
},
|
||||
"conditions": [
|
||||
{
|
||||
"operator": "OPERATOR_EQUALS",
|
||||
"value": "true"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"dataSourceSpecBinding": {
|
||||
"settlementDataProperty": "prices.BTC.value",
|
||||
"tradingTerminationProperty": "trading.terminated.ETH5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"metadata": ["sector:food", "sector:materials", "source:docs.vega.xyz"],
|
||||
"priceMonitoringParameters": {
|
||||
"triggers": [
|
||||
{
|
||||
"horizon": "43200",
|
||||
"probability": "0.9999999",
|
||||
"auctionExtension": "600"
|
||||
}
|
||||
]
|
||||
},
|
||||
"liquidityMonitoringParameters": {
|
||||
"targetStakeParameters": {
|
||||
"timeWindow": "3600",
|
||||
"scalingFactor": 10
|
||||
},
|
||||
"triggeringRatio": "0.7",
|
||||
"auctionExtension": "1"
|
||||
},
|
||||
"logNormal": {
|
||||
"tau": 0.0001140771161,
|
||||
"riskAversionParameter": 0.001,
|
||||
"params": {
|
||||
"mu": 0,
|
||||
"r": 0.016,
|
||||
"sigma": 0.8
|
||||
}
|
||||
},
|
||||
"successor": {
|
||||
"parentMarketId": "",
|
||||
"insurancePoolFraction": "0.75"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,17 +218,33 @@ describe(
|
||||
getProposalInformationFromTable('Tokens for proposal')
|
||||
.should('have.text', (1).toFixed(2))
|
||||
.and('be.visible');
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
// 3002-PROP-021
|
||||
cy.getByTestId('user-voted-yes').should('exist');
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(changeVoteButton).should('be.visible').click();
|
||||
voteForProposal('against');
|
||||
cy.getByTestId(proposalVoteProgressAgainstPercentage)
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
cy.getByTestId(voteBreakdownToggle).click();
|
||||
getProposalInformationFromTable('Tokens against proposal')
|
||||
.should('have.text', (1).toFixed(2))
|
||||
.and('be.visible');
|
||||
getProposalInformationFromTable('Number of voting parties')
|
||||
.should('have.text', '1')
|
||||
.and('be.visible');
|
||||
navigateTo(navigation.proposals);
|
||||
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
|
||||
getProposalFromTitle(rawProposal.rationale.title).within(() => {
|
||||
cy.getByTestId('user-voted-no').should('exist');
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 3001-VOTE-042, 3001-VOTE-057, 3001-VOTE-058, 3001-VOTE-059, 3001-VOTE-060
|
||||
|
||||
@@ -26,6 +26,8 @@ const votesTable = 'votes-table';
|
||||
const openProposals = 'open-proposals';
|
||||
const proposalVoteProgressForPercentage =
|
||||
'vote-progress-indicator-percentage-for';
|
||||
const majorityVoteReached = 'majority-reached';
|
||||
const minParticipationReached = 'participation-reached';
|
||||
const proposalTimeout = { timeout: 8000 };
|
||||
|
||||
context(
|
||||
@@ -72,7 +74,7 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
// 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
|
||||
// 3001-VOTE-020 3001-VOTE-021 3001-VOTE-046 3001-VOTE-044 3001-VOTE-074 3001-VOTE-074
|
||||
it('Able to enact proposal by voting', function () {
|
||||
const proposalTitle = 'Add New proposal with short enactment';
|
||||
const proposalTx = createUpdateNetworkProposalTxBody();
|
||||
@@ -85,10 +87,18 @@ context(
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => cy.getByTestId(viewProposalButton).click());
|
||||
.within(() => {
|
||||
// 3001-VOTE-019 time to vote is highlighted red
|
||||
cy.getByTestId('vote-details')
|
||||
.find('span')
|
||||
.should('have.class', 'text-vega-pink');
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(proposalStatus).should('have.text', 'Open');
|
||||
voteForProposal('for');
|
||||
cy.getByTestId(majorityVoteReached).should('exist');
|
||||
cy.getByTestId(minParticipationReached).should('exist');
|
||||
cy.getByTestId(proposalStatus, proposalTimeout)
|
||||
.should('have.text', 'Passed')
|
||||
.then(() => {
|
||||
@@ -104,6 +114,14 @@ context(
|
||||
cy.getByTestId(proposalVoteProgressForPercentage)
|
||||
.contains('100.00%')
|
||||
.and('be.visible');
|
||||
navigateTo(navigation.proposals);
|
||||
cy.contains(proposalTitle)
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(majorityVoteReached).should('exist');
|
||||
cy.getByTestId(minParticipationReached).should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
// 3001-VOTE-047
|
||||
|
||||
@@ -220,7 +220,7 @@ context(
|
||||
function () {
|
||||
const proposalTitle = 'Test new market proposal';
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.getByTestId(newProposalTitle).type('Test new market proposal');
|
||||
cy.getByTestId(newProposalTitle).type(proposalTitle);
|
||||
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
|
||||
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
@@ -606,6 +606,94 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
it('able to submit successor market proposal', function () {
|
||||
const proposalTitle = 'Test successor market proposal';
|
||||
|
||||
cy.createMarket();
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
cy.getByTestId('closed-proposals').within(() => {
|
||||
cy.contains('Add Lorem Ipsum market')
|
||||
.parentsUntil(proposalListItem)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId(viewProposalBtn).click();
|
||||
});
|
||||
});
|
||||
getProposalInformationFromTable('ID').invoke('text').as('parentMarketId');
|
||||
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
|
||||
cy.getByTestId(newProposalTitle).type(proposalTitle);
|
||||
cy.getByTestId(newProposalDescription).type(
|
||||
'E2E test for successor market'
|
||||
);
|
||||
cy.fixture('/proposals/successor-market').then((newMarketProposal) => {
|
||||
newMarketProposal.changes.successor.parentMarketId =
|
||||
this.parentMarketId;
|
||||
const newMarketPayload = JSON.stringify(newMarketProposal);
|
||||
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
|
||||
parseSpecialCharSequences: false,
|
||||
delay: 2,
|
||||
});
|
||||
});
|
||||
cy.getByTestId(proposalDownloadBtn)
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.then(() => {
|
||||
cy.wrap(
|
||||
getDownloadedProposalJsonPath('vega-new-market-proposal-')
|
||||
).then((filePath) => {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
submitUniqueRawProposal({ proposalBody: filePath });
|
||||
});
|
||||
});
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() => {
|
||||
// 3003-PMAN-008
|
||||
cy.getByTestId('proposal-successor-info')
|
||||
.should('have.text', 'Successor market to: TEST.24h')
|
||||
.find('a')
|
||||
.should('have.attr', 'href')
|
||||
.and('contain', this.parentMarketId);
|
||||
cy.getByTestId('view-proposal-btn').click();
|
||||
});
|
||||
// #3003-PMAN-010
|
||||
cy.getByTestId(proposalJsonToggle).click();
|
||||
cy.get('.language-json').within(() => {
|
||||
cy.get('.hljs-attr').should('contain.text', 'parentMarketId');
|
||||
cy.get('.hljs-string').should('contain.text', this.parentMarketId);
|
||||
cy.get('.hljs-attr').should('contain.text', 'insurancePoolFraction');
|
||||
cy.get('.hljs-string').should('contain.text', '0.75');
|
||||
});
|
||||
cy.getByTestId('proposal-market-data').within(() => {
|
||||
cy.getByTestId('proposal-market-data-toggle').click();
|
||||
cy.contains('Key details').click();
|
||||
// 3003-PMAN-009
|
||||
getMarketProposalDetailsFromTable('Parent Market ID').should(
|
||||
'have.text',
|
||||
this.parentMarketId
|
||||
);
|
||||
getMarketProposalDetailsFromTable('Insurance Pool Fraction').should(
|
||||
'have.text',
|
||||
'0.75'
|
||||
);
|
||||
getMarketProposalDetailsFromTable('Trading Mode').should(
|
||||
'have.text',
|
||||
'No trading'
|
||||
);
|
||||
});
|
||||
// 3003-PMAN-011
|
||||
cy.contains('Parent Market ID').realHover();
|
||||
cy.getByTestId('tooltip-content').should(
|
||||
'contain.text',
|
||||
'The ID of the market this market succeeds.'
|
||||
);
|
||||
cy.contains('Insurance Pool Fraction').realHover();
|
||||
cy.getByTestId('tooltip-content').should(
|
||||
'contain.text',
|
||||
'The fraction of the insurance pool balance that is carried over from the parent market to the successor.'
|
||||
);
|
||||
});
|
||||
|
||||
after('Disassociate from second wallet key if present', function () {
|
||||
cy.reload();
|
||||
waitForSpinner();
|
||||
|
||||
@@ -49,8 +49,8 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
navigateTo(navigation.proposals);
|
||||
});
|
||||
|
||||
// 3001-VOTE-018
|
||||
it('Newly created proposals list - proposals closest to closing date appear higher in list', function () {
|
||||
// 3001-VOTE-005
|
||||
const proposalDays = [364, 50, 2];
|
||||
for (let index = 0; index < proposalDays.length; index++) {
|
||||
goToMakeNewProposal(governanceProposalType.RAW);
|
||||
@@ -120,16 +120,24 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
|
||||
submitUniqueRawProposal({ proposalTitle: proposalTitle });
|
||||
getProposalFromTitle(proposalTitle).within(() => {
|
||||
// 3001-VOTE-039
|
||||
cy.getByTestId(voteStatus).should(
|
||||
cy.getByTestId('participation-not-reached').should(
|
||||
'have.text',
|
||||
'Participation not reached'
|
||||
'Min. participation not reached'
|
||||
);
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
voteForProposal('for');
|
||||
navigateTo(navigation.proposals);
|
||||
getProposalFromTitle(proposalTitle).within(() => {
|
||||
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
|
||||
cy.getByTestId(voteStatus).should(
|
||||
'have.text',
|
||||
'Currently expected to pass'
|
||||
);
|
||||
cy.getByTestId('user-voted-yes').should('exist');
|
||||
cy.getByTestId('participation-reached').should(
|
||||
'have.text',
|
||||
'Min. participation reached'
|
||||
);
|
||||
cy.getByTestId(viewProposalButton).click();
|
||||
});
|
||||
cy.getByTestId(voteBreakDownToggle).click();
|
||||
|
||||
@@ -96,12 +96,14 @@ 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', rewardsTimeOut)
|
||||
.should('contain.text', '0.4415')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE')
|
||||
.should('contain.text', '0.0004')
|
||||
.and('contain.text', '(44.15%)');
|
||||
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut).should(
|
||||
'contain.text',
|
||||
'0.4415'
|
||||
);
|
||||
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE').should(
|
||||
'contain.text',
|
||||
'0.0004'
|
||||
);
|
||||
cy.getByTestId('total').should('have.text', '0.4419');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -351,6 +351,12 @@ context(
|
||||
);
|
||||
stakingPageDisassociateAllTokens();
|
||||
});
|
||||
|
||||
it('Able to associate over 1 million tokens', function () {
|
||||
cy.get(ethWalletAssociateButton).click();
|
||||
stakingPageAssociateTokens('2000000', { approve: true });
|
||||
stakingPageDisassociateAllTokens();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -17,6 +17,7 @@ const governanceDocsUrl = 'https://vega.xyz/governance';
|
||||
const networkUpgradeProposalListItem = 'protocol-upgrade-proposals-list-item';
|
||||
const closedProposals = 'closed-proposals';
|
||||
const closedProposalToggle = 'closed-proposals-toggle-networkUpgrades';
|
||||
const protocolUpgradeTime = 'protocol-upgrade-time';
|
||||
|
||||
context(
|
||||
'Governance Page - verify elements on page',
|
||||
@@ -174,6 +175,7 @@ context(
|
||||
});
|
||||
});
|
||||
|
||||
// 3009-NTWU-003 3009-NTWU-004 3009-NTWU-007
|
||||
it('should see details of network upgrade proposal', function () {
|
||||
mockNetworkUpgradeProposal();
|
||||
navigateTo(navigation.proposals);
|
||||
@@ -228,6 +230,7 @@ context(
|
||||
cy.getByTestId(closedProposalToggle).should('not.exist');
|
||||
});
|
||||
|
||||
// 3009-NTWU-001 3009-NTWU-002 3009-NTWU-006 3009-NTWU-009
|
||||
it('should display network upgrade banner with estimate', function () {
|
||||
mockNetworkUpgradeProposal();
|
||||
cy.visit('/');
|
||||
@@ -259,10 +262,18 @@ context(
|
||||
.as('displayedEstimate');
|
||||
cy.get('@displayedEstimate').then((estimateText) => {
|
||||
// Estimated time should automatically update every second
|
||||
cy.getByTestId('protocol-upgrade-time')
|
||||
cy.getByTestId(protocolUpgradeTime)
|
||||
.invoke('text')
|
||||
.should('not.eq', estimateText);
|
||||
});
|
||||
// time estimate on proposal detail
|
||||
cy.getByTestId('protocol-upgrade-proposal').within(() => {
|
||||
cy.get('@displayedEstimate').then((estimateText) => {
|
||||
cy.getByTestId(protocolUpgradeTime)
|
||||
.invoke('text')
|
||||
.should('not.eq', estimateText);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -339,7 +339,7 @@ context(
|
||||
cy.getByTestId(vegaWalletCurrencyTitle)
|
||||
.contains(name)
|
||||
.parent()
|
||||
.siblings()
|
||||
.siblings(txTimeout)
|
||||
.should((elementAmount) => {
|
||||
const displayedAmount = parseFloat(elementAmount.text());
|
||||
expect(displayedAmount).be.gte(expectedAmount);
|
||||
|
||||
@@ -107,7 +107,7 @@ export function dissociateFromSecondWalletKey() {
|
||||
cy.getByTestId('vega-in-wallet')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId('eth-wallet-associated-balances')
|
||||
cy.getByTestId('eth-wallet-associated-balances', txTimeout)
|
||||
.last()
|
||||
.within(() => {
|
||||
cy.getByTestId('associated-key')
|
||||
|
||||
@@ -78,13 +78,21 @@ export function stakingPageAssociateTokens(
|
||||
}
|
||||
cy.get(tokenAmountInputBox, epochTimeout).type(amount);
|
||||
if (approve) {
|
||||
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega', txTimeout).should(
|
||||
'not.exist'
|
||||
);
|
||||
cy.getByTestId('wallet-associate').then((walletAssociateField) => {
|
||||
if (
|
||||
walletAssociateField.find('[data-testid="token-input-approve-button"]')
|
||||
.length
|
||||
) {
|
||||
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click();
|
||||
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
|
||||
'be.visible'
|
||||
);
|
||||
cy.contains(
|
||||
'Approve $VEGA Tokens for staking on Vega',
|
||||
txTimeout
|
||||
).should('not.exist');
|
||||
}
|
||||
});
|
||||
}
|
||||
cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './json-diff';
|
||||
@@ -0,0 +1,30 @@
|
||||
import { render, screen, cleanup } from '@testing-library/react';
|
||||
import { JsonDiff } from './json-diff';
|
||||
|
||||
describe('JsonDiff', () => {
|
||||
afterEach(cleanup);
|
||||
|
||||
it('renders without crashing', () => {
|
||||
render(<JsonDiff left={{}} right={{}} />);
|
||||
expect(screen.getByTestId('json-diff')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the correct message when both objects are identical', () => {
|
||||
render(<JsonDiff left={{}} right={{}} />);
|
||||
expect(screen.getByText('Data is identical')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not show the "identical" message when both objects are not identical', () => {
|
||||
render(
|
||||
<JsonDiff
|
||||
left={{
|
||||
name: 'test',
|
||||
}}
|
||||
right={{
|
||||
name: 'test2',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expect(screen.queryByText('Data is identical')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { formatters, create } from 'jsondiffpatch';
|
||||
import 'jsondiffpatch/dist/formatters-styles/html.css';
|
||||
import 'jsondiffpatch/dist/formatters-styles/annotated.css';
|
||||
|
||||
export type JsonValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| JsonValue[]
|
||||
| { [key: string]: JsonValue };
|
||||
|
||||
interface JsonDiffProps {
|
||||
left: JsonValue;
|
||||
right: JsonValue;
|
||||
objectHash?: (obj: unknown) => string | undefined;
|
||||
}
|
||||
|
||||
export const JsonDiff = ({ right, left, objectHash }: JsonDiffProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [html, setHtml] = useState<string | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
const delta = create({
|
||||
objectHash,
|
||||
}).diff(left, right);
|
||||
|
||||
if (delta) {
|
||||
const deltaHtml = formatters.html.format(delta, left);
|
||||
|
||||
formatters.html.hideUnchanged();
|
||||
|
||||
setHtml(deltaHtml);
|
||||
} else {
|
||||
setHtml(undefined);
|
||||
}
|
||||
}, [right, left, objectHash]);
|
||||
|
||||
return html ? (
|
||||
<div data-testid="json-diff" dangerouslySetInnerHTML={{ __html: html }} />
|
||||
) : (
|
||||
<p data-testid="json-diff">{t('dataIsIdentical')}</p>
|
||||
);
|
||||
};
|
||||
@@ -18,10 +18,7 @@ import { VegaWallet } from '../vega-wallet';
|
||||
import { useLocation, useMatch } from 'react-router-dom';
|
||||
import { useEffect } from 'react';
|
||||
import { useTelemetryDialog } from '../telemetry-dialog/telemetry-dialog';
|
||||
import {
|
||||
ProtocolUpgradeCountdown,
|
||||
ProtocolUpgradeCountdownMode,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { ProtocolUpgradeCountdown } from '@vegaprotocol/proposals';
|
||||
|
||||
export const SettingsLink = () => {
|
||||
const { open, isOpen, close } = useTelemetryDialog();
|
||||
@@ -68,9 +65,7 @@ export const Nav = ({ theme }: Pick<NavigationProps, 'theme'>) => {
|
||||
actions={
|
||||
<>
|
||||
<SettingsLink />
|
||||
<ProtocolUpgradeCountdown
|
||||
mode={ProtocolUpgradeCountdownMode.IN_ESTIMATED_TIME_REMAINING}
|
||||
/>
|
||||
<ProtocolUpgradeCountdown />
|
||||
</>
|
||||
}
|
||||
>
|
||||
|
||||
@@ -853,5 +853,7 @@
|
||||
"consensusNodes": "consensus nodes",
|
||||
"activeNodes": "active nodes",
|
||||
"Estimated time to upgrade": "Estimated time to upgrade",
|
||||
"Upgraded at": "Upgraded at"
|
||||
"Upgraded at": "Upgraded at",
|
||||
"dataIsIdentical": "Data is identical",
|
||||
"updatesToMarket": "Updates to market"
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@ export const downloadJson = (jsonString: string, proposalTitle: string) => {
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './proposal-market-changes';
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
import { render, fireEvent } from '@testing-library/react';
|
||||
import {
|
||||
ProposalMarketChanges,
|
||||
applyImmutableKeysFromEarlierVersion,
|
||||
} from './proposal-market-changes';
|
||||
import type { JsonValue } from '../../../../components/json-diff';
|
||||
|
||||
describe('applyImmutableKeysFromEarlierVersion', () => {
|
||||
it('returns an empty object if any argument is not an object or null', () => {
|
||||
const earlierVersion: JsonValue = null;
|
||||
const updatedVersion: JsonValue = null;
|
||||
expect(
|
||||
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
it('overrides updatedVersion with values from earlierVersion for immutable keys', () => {
|
||||
const earlierVersion: JsonValue = {
|
||||
decimalPlaces: 2,
|
||||
positionDecimalPlaces: 3,
|
||||
instrument: {
|
||||
name: 'Instrument1',
|
||||
future: {
|
||||
settlementAsset: 'Asset1',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const updatedVersion: JsonValue = {
|
||||
decimalPlaces: 3, // should be overridden by 2
|
||||
positionDecimalPlaces: 4, // should be overridden by 3
|
||||
instrument: {
|
||||
name: 'Instrument2', // should be overridden by 'Instrument1'
|
||||
future: {
|
||||
settlementAsset: 'Asset2', // should be overridden by 'Asset1'
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const expected: JsonValue = {
|
||||
decimalPlaces: 2,
|
||||
positionDecimalPlaces: 3,
|
||||
instrument: {
|
||||
name: 'Instrument1',
|
||||
future: {
|
||||
settlementAsset: 'Asset1',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
applyImmutableKeysFromEarlierVersion(earlierVersion, updatedVersion)
|
||||
).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ProposalMarketChanges', () => {
|
||||
it('renders correctly', () => {
|
||||
const { getByTestId } = render(
|
||||
<ProposalMarketChanges
|
||||
originalProposal={{}}
|
||||
latestEnactedProposal={{}}
|
||||
updatedProposal={{}}
|
||||
/>
|
||||
);
|
||||
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('JsonDiff is not visible when showChanges is false', () => {
|
||||
const { queryByTestId } = render(
|
||||
<ProposalMarketChanges
|
||||
originalProposal={{}}
|
||||
latestEnactedProposal={{}}
|
||||
updatedProposal={{}}
|
||||
/>
|
||||
);
|
||||
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('JsonDiff is visible when showChanges is true', async () => {
|
||||
const { getByTestId } = render(
|
||||
<ProposalMarketChanges
|
||||
originalProposal={{}}
|
||||
latestEnactedProposal={{}}
|
||||
updatedProposal={{}}
|
||||
/>
|
||||
);
|
||||
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
|
||||
expect(getByTestId('json-diff')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import cloneDeep from 'lodash/cloneDeep';
|
||||
import set from 'lodash/set';
|
||||
import get from 'lodash/get';
|
||||
import { JsonDiff } from '../../../../components/json-diff';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useState } from 'react';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import type { JsonValue } from '../../../../components/json-diff';
|
||||
|
||||
const immutableKeys = [
|
||||
'decimalPlaces',
|
||||
'positionDecimalPlaces',
|
||||
'instrument.name',
|
||||
'instrument.future.settlementAsset',
|
||||
];
|
||||
|
||||
export const applyImmutableKeysFromEarlierVersion = (
|
||||
earlierVersion: JsonValue,
|
||||
updatedVersion: JsonValue
|
||||
) => {
|
||||
if (
|
||||
typeof earlierVersion !== 'object' ||
|
||||
earlierVersion === null ||
|
||||
typeof updatedVersion !== 'object' ||
|
||||
updatedVersion === null
|
||||
) {
|
||||
// If either version is not an object or is null, return null or throw an error
|
||||
return {};
|
||||
}
|
||||
|
||||
const updatedVersionCopy = cloneDeep(updatedVersion);
|
||||
|
||||
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
|
||||
immutableKeys.forEach((key) => {
|
||||
set(updatedVersionCopy, key, get(earlierVersion, key));
|
||||
});
|
||||
|
||||
return updatedVersionCopy;
|
||||
};
|
||||
|
||||
interface ProposalMarketChangesProps {
|
||||
originalProposal: JsonValue;
|
||||
latestEnactedProposal: JsonValue | undefined;
|
||||
updatedProposal: JsonValue;
|
||||
}
|
||||
|
||||
export const ProposalMarketChanges = ({
|
||||
originalProposal,
|
||||
latestEnactedProposal,
|
||||
updatedProposal,
|
||||
}: ProposalMarketChangesProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [showChanges, setShowChanges] = useState(false);
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-market-changes">
|
||||
<CollapsibleToggle
|
||||
toggleState={showChanges}
|
||||
setToggleState={setShowChanges}
|
||||
dataTestId={'proposal-market-changes-toggle'}
|
||||
>
|
||||
<SubHeading title={t('updatesToMarket')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showChanges && (
|
||||
<div className="mb-6">
|
||||
<JsonDiff
|
||||
left={latestEnactedProposal || originalProposal}
|
||||
right={
|
||||
latestEnactedProposal
|
||||
? applyImmutableKeysFromEarlierVersion(
|
||||
latestEnactedProposal,
|
||||
updatedProposal
|
||||
)
|
||||
: applyImmutableKeysFromEarlierVersion(
|
||||
originalProposal,
|
||||
updatedProposal
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
+1
-1
@@ -200,7 +200,7 @@ export const ProposalMarketData = ({
|
||||
title={marketData.tradableInstrument.instrument.code}
|
||||
open={isOpen}
|
||||
onChange={(isOpen) => (isOpen ? open() : close())}
|
||||
size="medium"
|
||||
size="large"
|
||||
dataTestId="market-json-dialog"
|
||||
>
|
||||
<CopyWithTooltip text={JSON.stringify(marketData)}>
|
||||
|
||||
@@ -17,6 +17,7 @@ import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
|
||||
export enum ProposalType {
|
||||
@@ -34,6 +35,10 @@ export interface ProposalProps {
|
||||
assetData?: AssetQuery | null;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
restData: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
originalMarketProposalRestData?: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mostRecentlyEnactedAssociatedMarketProposal?: any;
|
||||
}
|
||||
|
||||
export const Proposal = ({
|
||||
@@ -42,6 +47,8 @@ export const Proposal = ({
|
||||
restData,
|
||||
newMarketData,
|
||||
assetData,
|
||||
originalMarketProposalRestData,
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
}: ProposalProps) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -154,6 +161,24 @@ export const Proposal = ({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{proposal.terms.change.__typename === 'UpdateMarket' && (
|
||||
<div className="mb-4">
|
||||
<ProposalMarketChanges
|
||||
originalProposal={
|
||||
originalMarketProposalRestData?.data?.proposal?.terms?.newMarket
|
||||
?.changes || {}
|
||||
}
|
||||
latestEnactedProposal={
|
||||
mostRecentlyEnactedAssociatedMarketProposal?.node?.proposal
|
||||
?.terms?.updateMarket?.changes || {}
|
||||
}
|
||||
updatedProposal={
|
||||
restData?.data?.proposal?.terms?.updateMarket?.changes || {}
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(proposal.terms.change.__typename === 'NewAsset' ||
|
||||
proposal.terms.change.__typename === 'UpdateAsset') &&
|
||||
asset && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEffect } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { Proposal } from '../components/proposal';
|
||||
@@ -16,7 +16,12 @@ import {
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const [
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
setMostRecentlyEnactedAssociatedMarketProposal,
|
||||
] = useState(undefined);
|
||||
const params = useParams<{ proposalId: string }>();
|
||||
|
||||
const {
|
||||
params: networkParams,
|
||||
loading: networkParamsLoading,
|
||||
@@ -37,9 +42,11 @@ export const ProposalContainer = () => {
|
||||
NetworkParams.governance_proposal_updateNetParam_requiredMajority,
|
||||
NetworkParams.governance_proposal_freeform_requiredMajority,
|
||||
]);
|
||||
|
||||
const {
|
||||
state: { data: restData },
|
||||
state: { data: restData, loading: restLoading, error: restError },
|
||||
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
|
||||
|
||||
const { data, loading, error, refetch } = useProposalQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
@@ -47,6 +54,35 @@ export const ProposalContainer = () => {
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const {
|
||||
state: {
|
||||
data: originalMarketProposalRestData,
|
||||
loading: originalMarketProposalRestLoading,
|
||||
error: originalMarketProposalRestError,
|
||||
},
|
||||
} = useFetch(
|
||||
`${ENV.rest}governance?proposalId=${
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
data?.proposal.terms.change.marketId
|
||||
}`,
|
||||
undefined,
|
||||
true,
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
state: {
|
||||
data: previouslyEnactedMarketProposalsRestData,
|
||||
loading: previouslyEnactedMarketProposalsRestLoading,
|
||||
error: previouslyEnactedMarketProposalsRestError,
|
||||
},
|
||||
} = useFetch(
|
||||
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
||||
undefined,
|
||||
true,
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
data: newMarketData,
|
||||
loading: newMarketLoading,
|
||||
@@ -79,6 +115,39 @@ export const ProposalContainer = () => {
|
||||
),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
previouslyEnactedMarketProposalsRestData &&
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
) {
|
||||
const change = data?.proposal?.terms?.change as { marketId: string };
|
||||
|
||||
const filteredProposals =
|
||||
// @ts-ignore rest data is not typed
|
||||
previouslyEnactedMarketProposalsRestData.connection.edges.filter(
|
||||
// @ts-ignore rest data is not typed
|
||||
({ node }) =>
|
||||
node?.proposal?.terms?.updateMarket?.marketId === change.marketId
|
||||
);
|
||||
|
||||
const sortedProposals = filteredProposals.sort(
|
||||
// @ts-ignore rest data is not typed
|
||||
(a, b) =>
|
||||
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
|
||||
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
|
||||
);
|
||||
|
||||
setMostRecentlyEnactedAssociatedMarketProposal(
|
||||
sortedProposals[sortedProposals.length - 1]
|
||||
);
|
||||
}
|
||||
}, [
|
||||
previouslyEnactedMarketProposalsRestData,
|
||||
params.proposalId,
|
||||
data?.proposal?.terms.change.__typename,
|
||||
data?.proposal?.terms.change,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(refetch, 2000);
|
||||
return () => clearInterval(interval);
|
||||
@@ -87,14 +156,39 @@ export const ProposalContainer = () => {
|
||||
return (
|
||||
<AsyncRenderer
|
||||
loading={
|
||||
loading || newMarketLoading || assetLoading || networkParamsLoading
|
||||
loading ||
|
||||
newMarketLoading ||
|
||||
assetLoading ||
|
||||
networkParamsLoading ||
|
||||
(restLoading ? (restLoading as boolean) : false) ||
|
||||
(originalMarketProposalRestLoading
|
||||
? (originalMarketProposalRestLoading as boolean)
|
||||
: false) ||
|
||||
(previouslyEnactedMarketProposalsRestLoading
|
||||
? (previouslyEnactedMarketProposalsRestLoading as boolean)
|
||||
: false)
|
||||
}
|
||||
error={
|
||||
error ||
|
||||
newMarketError ||
|
||||
assetError ||
|
||||
restError ||
|
||||
originalMarketProposalRestError ||
|
||||
previouslyEnactedMarketProposalsRestError ||
|
||||
networkParamsError
|
||||
}
|
||||
error={error || newMarketError || assetError || networkParamsError}
|
||||
data={{
|
||||
...data,
|
||||
...networkParams,
|
||||
...(newMarketData ? { newMarketData } : {}),
|
||||
...(assetData ? { assetData } : {}),
|
||||
...(restData ? { restData } : {}),
|
||||
...(originalMarketProposalRestData
|
||||
? { originalMarketProposalRestData }
|
||||
: {}),
|
||||
...(previouslyEnactedMarketProposalsRestData
|
||||
? { previouslyEnactedMarketProposalsRestData }
|
||||
: {}),
|
||||
}}
|
||||
>
|
||||
{data?.proposal ? (
|
||||
@@ -104,6 +198,10 @@ export const ProposalContainer = () => {
|
||||
restData={restData}
|
||||
newMarketData={newMarketData}
|
||||
assetData={assetData}
|
||||
originalMarketProposalRestData={originalMarketProposalRestData}
|
||||
mostRecentlyEnactedAssociatedMarketProposal={
|
||||
mostRecentlyEnactedAssociatedMarketProposal
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ProposalNotFound />
|
||||
|
||||
-15
@@ -5,7 +5,6 @@ import {
|
||||
RewardsTable,
|
||||
} from '../shared-rewards-table-assets/shared-rewards-table-assets';
|
||||
import type { EpochIndividualReward } from './generate-epoch-individual-rewards-list';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
interface EpochIndividualRewardsGridProps {
|
||||
data: EpochIndividualReward;
|
||||
@@ -22,14 +21,11 @@ interface RewardItemProps {
|
||||
const DisplayReward = ({
|
||||
reward,
|
||||
decimals,
|
||||
percentageOfTotal,
|
||||
}: {
|
||||
reward: string;
|
||||
decimals: number;
|
||||
percentageOfTotal?: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (Number(reward) === 0) {
|
||||
return <span className="text-vega-dark-300">-</span>;
|
||||
}
|
||||
@@ -39,23 +35,12 @@ const DisplayReward = ({
|
||||
description={
|
||||
<div className="flex flex-col items-start">
|
||||
<span>{formatNumber(toBigNum(reward, decimals), decimals)}</span>
|
||||
{percentageOfTotal && (
|
||||
<span className="text-vega-dark-300">
|
||||
({percentageOfTotal}% {t('ofTotalDistributed')})
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<button>
|
||||
<div className="flex flex-col items-start">
|
||||
<span>{formatNumber(toBigNum(reward, decimals), 4)}</span>
|
||||
{percentageOfTotal && (
|
||||
<span className="text-vega-dark-300">
|
||||
({formatNumber(percentageOfTotal, 4).toString()}
|
||||
%)
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
-76
@@ -168,17 +168,6 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
expect(result2[1].epoch).toEqual(1);
|
||||
});
|
||||
|
||||
it('correctly calculates the total value of rewards for an asset', () => {
|
||||
const rewards = [reward1, reward4];
|
||||
const result = generateEpochIndividualRewardsList({
|
||||
rewards,
|
||||
epochId: 1,
|
||||
epochRewardSummaries: [],
|
||||
});
|
||||
|
||||
expect(result[0].rewards[0].totalAmount).toEqual('200');
|
||||
});
|
||||
|
||||
it('returns data in the expected shape', () => {
|
||||
// Just sanity checking the whole structure here
|
||||
const rewards = [reward1, reward2, reward3, reward4];
|
||||
@@ -458,69 +447,4 @@ describe('generateEpochIndividualRewardsList', () => {
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('correctly calculates the percentage of two or more rewards by referencing the total rewards amount', () => {
|
||||
const result = generateEpochIndividualRewardsList({
|
||||
rewards: [
|
||||
// reward1 is 100 usd, which is 10% of the total rewards amount
|
||||
reward1,
|
||||
{
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
amount: '200',
|
||||
percentageOfTotal: '0.2',
|
||||
receivedAt: new Date(),
|
||||
asset: { id: 'usd', symbol: 'USD', name: 'USD', decimals: 6 },
|
||||
party: { id: 'blah' },
|
||||
epoch: { id: '1' },
|
||||
},
|
||||
],
|
||||
epochId: 1,
|
||||
epochRewardSummaries: [
|
||||
{
|
||||
__typename: 'EpochRewardSummary',
|
||||
epoch: 1,
|
||||
assetId: 'usd',
|
||||
amount: '1000',
|
||||
rewardType: AccountType.ACCOUNT_TYPE_GLOBAL_REWARD,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(result[0]).toEqual({
|
||||
epoch: 1,
|
||||
rewards: [
|
||||
{
|
||||
asset: 'USD',
|
||||
decimals: 6,
|
||||
totalAmount: '300',
|
||||
rewardTypes: {
|
||||
[AccountType.ACCOUNT_TYPE_FEES_INFRASTRUCTURE]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_GLOBAL_REWARD]: {
|
||||
amount: '300',
|
||||
percentageOfTotal: '30',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
[AccountType.ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS]: {
|
||||
amount: '0',
|
||||
percentageOfTotal: '0',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+1
-16
@@ -59,7 +59,6 @@ export const generateEpochIndividualRewardsList = ({
|
||||
const epochIndividualRewards = rewards.reduce((acc, reward) => {
|
||||
const epochId = reward.epoch.id;
|
||||
const assetName = reward.asset.name;
|
||||
const assetId = reward.asset.id;
|
||||
const assetDecimals = reward.asset.decimals;
|
||||
const rewardType = reward.rewardType;
|
||||
const amount = reward.amount;
|
||||
@@ -77,13 +76,6 @@ export const generateEpochIndividualRewardsList = ({
|
||||
const epoch = acc.get(epochId);
|
||||
|
||||
// matchingTotalReward is the total awarded for all users for the reward type in the epoch of the asset
|
||||
const matchingTotalRewardAmount = epochRewardSummaries.find(
|
||||
(summary) =>
|
||||
summary.epoch === Number(epochId) &&
|
||||
summary.assetId === assetId &&
|
||||
summary.rewardType === rewardType
|
||||
)?.amount;
|
||||
|
||||
let asset = epoch?.rewards.find((r) => r.asset === assetName);
|
||||
|
||||
if (!asset) {
|
||||
@@ -106,14 +98,7 @@ export const generateEpochIndividualRewardsList = ({
|
||||
|
||||
asset.rewardTypes[rewardType] = {
|
||||
amount: newAmount,
|
||||
percentageOfTotal: matchingTotalRewardAmount
|
||||
? new BigNumber(newAmount)
|
||||
.dividedBy(matchingTotalRewardAmount)
|
||||
.multipliedBy(100)
|
||||
.toString()
|
||||
: // this should never be reached, if there's an individual reward there should
|
||||
// always be a reward total from the api too, but set it as a fallback just in case
|
||||
percentageOfTotal,
|
||||
percentageOfTotal: percentageOfTotal,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -61,8 +61,6 @@ export const RewardsPage = () => {
|
||||
error: paramsError,
|
||||
} = useNetworkParams([NetworkParams.reward_staking_delegation_payoutDelay]);
|
||||
|
||||
console.log('params', params);
|
||||
|
||||
const payoutDuration = useMemo(() => {
|
||||
if (!params) {
|
||||
return 0;
|
||||
|
||||
@@ -8,9 +8,10 @@ import { TxState } from '../../../hooks/transaction-reducer';
|
||||
import { useTransaction } from '../../../hooks/use-transaction';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { AssociateInfo } from './associate-info';
|
||||
import { removeDecimal, toBigNum } from '@vegaprotocol/utils';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import type { EthereumConfig } from '@vegaprotocol/web3';
|
||||
import { useBalances } from '../../../lib/balances/balances-store';
|
||||
import { MaxUint256 } from '@ethersproject/constants';
|
||||
|
||||
export const WalletAssociate = ({
|
||||
perform,
|
||||
@@ -42,7 +43,7 @@ export const WalletAssociate = ({
|
||||
} = useTransaction(() => {
|
||||
return token.approve(
|
||||
ethereumConfig.staking_bridge_contract.address,
|
||||
removeDecimal('1000000', decimals).toString()
|
||||
MaxUint256.toString()
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Styles required to (effectively) un-override the
|
||||
/* Styles required to (effectively) un-override the
|
||||
* reset styles so that the Proposal description fields
|
||||
* render as you'd expect them to.
|
||||
*
|
||||
@@ -97,3 +97,33 @@
|
||||
.react-markdown-container ul li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.jsondiffpatch-delta,
|
||||
.jsondiffpatch-delta pre {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
}
|
||||
|
||||
.jsondiffpatch-delta pre {
|
||||
padding: 0 0.25em !important;
|
||||
}
|
||||
|
||||
.jsondiffpatch-added .jsondiffpatch-property-name,
|
||||
.jsondiffpatch-added .jsondiffpatch-value pre,
|
||||
.jsondiffpatch-modified .jsondiffpatch-right-value pre,
|
||||
.jsondiffpatch-textdiff-added {
|
||||
background: theme(colors.vega.green[650]) !important;
|
||||
color: theme(colors.white) !important;
|
||||
}
|
||||
|
||||
.jsondiffpatch-deleted .jsondiffpatch-property-name,
|
||||
.jsondiffpatch-deleted pre,
|
||||
.jsondiffpatch-modified .jsondiffpatch-left-value pre,
|
||||
.jsondiffpatch-textdiff-deleted {
|
||||
background: theme(colors.vega.pink[650]) !important;
|
||||
color: theme(colors.white) !important;
|
||||
}
|
||||
|
||||
.jsondiffpatch-moved .jsondiffpatch-moved-destination {
|
||||
background: theme(colors.vega.yellow[350]) !important;
|
||||
color: theme(colors.vega.dark[200]) !important;
|
||||
}
|
||||
|
||||
+1
-1
@@ -57,7 +57,7 @@ const ROWS = [
|
||||
|
||||
export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
|
||||
return (
|
||||
<Dialog size="medium" open={isOpen} onChange={onChange}>
|
||||
<Dialog size="large" open={isOpen} onChange={onChange}>
|
||||
<h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase">
|
||||
{t('Health')}
|
||||
</h1>
|
||||
|
||||
@@ -26,6 +26,7 @@ module.exports = defineConfig({
|
||||
requestTimeout: 20000,
|
||||
retries: 1,
|
||||
testIsolation: false,
|
||||
experimentalMemoryManagement: true,
|
||||
},
|
||||
env: {
|
||||
ETHERSCAN_URL: 'https://sepolia.etherscan.io',
|
||||
|
||||
@@ -535,6 +535,7 @@ function checkIfDataAndTimeOfCreationAndUpdateIsEqual(date: string) {
|
||||
// unexpected latency
|
||||
const minBefore = subSeconds(new Date(), 5);
|
||||
const maxAfter = addSeconds(new Date(), 5);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(maxAfter);
|
||||
const date = new Date($dateTime.toString());
|
||||
expect(isAfter(date, minBefore) && isBefore(date, maxAfter)).to.equal(
|
||||
|
||||
@@ -2,14 +2,14 @@ describe('charts', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
cy.getByTestId('Depth').click();
|
||||
});
|
||||
|
||||
it('can see market depth chart', () => {
|
||||
// 6006-DEPC-001
|
||||
cy.getByTestId('Depth').click();
|
||||
cy.getByTestId('tab-depth').should('be.visible');
|
||||
cy.get('.depth-chart-module_canvas__260De').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -202,6 +202,7 @@ describe('Closed markets', { tags: '@smoke' }, () => {
|
||||
const specDataConnection = createDataConnection();
|
||||
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockGQL((req) => {
|
||||
aliasGQLQuery(req, 'ChainId', chainIdQuery());
|
||||
aliasGQLQuery(req, 'Statistics', statisticsQuery());
|
||||
@@ -455,6 +456,7 @@ describe('no closed markets', { tags: '@smoke', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Closed markets"]').click();
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@ const nodeHealthTrigger = 'node-health-trigger';
|
||||
|
||||
describe('home', { tags: '@regression' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
@@ -68,22 +69,4 @@ describe('home', { tags: '@regression' }, () => {
|
||||
cy.getByTestId('connect').click();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Network switcher', () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
});
|
||||
|
||||
it('switch to fairground network and check status & incidents link', () => {
|
||||
// 0006-NETW-002
|
||||
// 0006-NETW-003
|
||||
cy.getByTestId('navigation')
|
||||
.find('[data-testid="network-switcher"]')
|
||||
.should('have.text', 'Custom')
|
||||
.click();
|
||||
cy.getByTestId('network-item').contains('Fairground testnet');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress';
|
||||
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const dialogContent = 'dialog-content';
|
||||
const dialogContent = 'welcome-dialog';
|
||||
|
||||
const generateProposal = (code: string): ProposalListFieldsFragment => ({
|
||||
__typename: 'Proposal',
|
||||
@@ -140,43 +140,43 @@ describe('home', { tags: '@regression' }, () => {
|
||||
cy.wait('@MarketsData');
|
||||
});
|
||||
|
||||
it('redirects to market/all and displays welcome notice', () => {
|
||||
it('close welcome dialog should redirect to market/all', () => {
|
||||
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
||||
cy.getByTestId('welcome-notice-title').should(
|
||||
'contain.text',
|
||||
'Welcome to Console'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('no proposal nor markets found', () => {
|
||||
it('there are welcome text and a link to propose market', () => {
|
||||
cy.mockGQL((req) => {
|
||||
const data = {
|
||||
marketsConnection: {
|
||||
__typename: 'MarketConnection',
|
||||
edges: [],
|
||||
},
|
||||
};
|
||||
aliasGQLQuery(req, 'Markets', data);
|
||||
aliasGQLQuery(req, 'MarketsData', data);
|
||||
aliasGQLQuery(req, 'ProposalsList', {
|
||||
proposalsConnection: {
|
||||
__typename: 'ProposalsConnection',
|
||||
edges: null,
|
||||
},
|
||||
});
|
||||
cy.getByTestId('welcome-dialog').should('be.visible');
|
||||
cy.getByTestId('welcome-title').should('contain.text', 'Console CUSTOM');
|
||||
cy.getByTestId('browse-markets-button').should('not.be.disabled');
|
||||
cy.getByTestId('get-started-banner').should('be.visible');
|
||||
cy.getByTestId('get-started-button').should('not.be.disabled');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
||||
cy.window().then((window) => {
|
||||
expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
|
||||
'true'
|
||||
);
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
cy.getByTestId('welcome-notice-title').should(
|
||||
'contain.text',
|
||||
'Welcome to Console'
|
||||
);
|
||||
cy.getByTestId('external-link')
|
||||
.contains('Propose a market')
|
||||
.should('exist');
|
||||
});
|
||||
|
||||
it('click browse markets button should redirect to market/all', () => {
|
||||
cy.getByTestId('welcome-dialog').should('be.visible');
|
||||
cy.getByTestId('browse-markets-button').click();
|
||||
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
||||
cy.window().then((window) => {
|
||||
expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
|
||||
'true'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('click get started button should open connect dialog', () => {
|
||||
cy.getByTestId('welcome-dialog').should('be.visible');
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
|
||||
cy.window().then((window) => {
|
||||
expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
|
||||
'true'
|
||||
);
|
||||
});
|
||||
cy.getByTestId('wallet-dialog-title').should('contain.text', 'Connect');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -185,7 +185,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem('marketId', 'market-1');
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.location('hash').should('equal', '#/markets/market-1');
|
||||
cy.getByTestId(dialogContent).should('not.exist');
|
||||
});
|
||||
@@ -199,6 +199,7 @@ describe('home', { tags: '@regression' }, () => {
|
||||
});
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.getByTestId('dialog-close').click();
|
||||
cy.location('hash').should('equal', '#/markets/market-not-existing');
|
||||
cy.getByTestId(dialogContent).should('not.exist');
|
||||
});
|
||||
|
||||
@@ -3,12 +3,13 @@ import type { MarketsQuery } from '@vegaprotocol/markets';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const rowSelector =
|
||||
'[data-testid="tab-all-markets"] .ag-center-cols-container .ag-row';
|
||||
'[data-testid="tab-open-markets"] .ag-center-cols-container .ag-row';
|
||||
const colInstrumentCode = '[col-id="tradableInstrument.instrument.code"]';
|
||||
|
||||
describe('markets all table', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearLocalStorage().then(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
@@ -34,7 +35,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
|
||||
'Settlement asset',
|
||||
'',
|
||||
];
|
||||
cy.getByTestId('tab-all-markets').within(($headers) => {
|
||||
cy.getByTestId('tab-open-markets').within(($headers) => {
|
||||
cy.wrap($headers)
|
||||
.get('.ag-header-cell-text')
|
||||
.each(($header, i) => {
|
||||
@@ -44,7 +45,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
it('markets tab should be rendered properly', () => {
|
||||
cy.get('[data-testid="All markets"]').should(
|
||||
cy.get('[data-testid="Open markets"]').should(
|
||||
'have.attr',
|
||||
'data-state',
|
||||
'active'
|
||||
@@ -166,7 +167,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
|
||||
'ETHBTC.QM21',
|
||||
'SOLUSD',
|
||||
];
|
||||
cy.get('[data-testid="All markets"]').click({ force: true });
|
||||
cy.get('[data-testid="Open markets"]').click({ force: true });
|
||||
cy.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
|
||||
cy.contains('AAPL.MF21').should('be.visible');
|
||||
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name
|
||||
@@ -191,7 +192,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('no all markets', { tags: '@smoke', testIsolation: true }, () => {
|
||||
describe('no open markets', { tags: '@smoke', testIsolation: true }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
const markets: MarketsQuery = {};
|
||||
@@ -199,11 +200,12 @@ describe('no all markets', { tags: '@smoke', testIsolation: true }, () => {
|
||||
aliasGQLQuery(req, 'Markets', markets);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
});
|
||||
|
||||
it.skip('can see no markets message', () => {
|
||||
// 6001-MARK-048
|
||||
cy.getByTestId('tab-all-markets').should('contain.text', 'No markets');
|
||||
cy.getByTestId('tab-open-markets').should('contain.text', 'No markets');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,7 @@ describe('market info is displayed', { tags: '@smoke' }, () => {
|
||||
});
|
||||
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(MarketState.STATE_ACTIVE);
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
|
||||
@@ -36,6 +36,7 @@ const headers = [
|
||||
|
||||
describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockSubscription();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
@@ -119,6 +120,7 @@ describe('liquidity table - trading', { tags: '@smoke' }, () => {
|
||||
|
||||
describe('liquidity table view', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockSubscription();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
|
||||
@@ -12,6 +12,7 @@ describe('markets selector', { tags: '@smoke' }, () => {
|
||||
cy.window().then((window) => {
|
||||
window.localStorage.setItem('marketId', 'market-1');
|
||||
});
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(
|
||||
MarketState.STATE_ACTIVE,
|
||||
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
|
||||
@@ -30,6 +30,7 @@ describe('Market trading page', () => {
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@MarketData');
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
|
||||
@@ -9,6 +9,7 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
});
|
||||
@@ -84,7 +85,7 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
|
||||
.find('button')
|
||||
.click();
|
||||
|
||||
const dropdownContent = '[data-testid="market-actions-content"]';
|
||||
const dropdownContent = '[data-testid="proposal-actions-content"]';
|
||||
const dropdownContentItem = '[role="menuitem"]';
|
||||
|
||||
// 6001-MARK-059
|
||||
@@ -100,7 +101,6 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
|
||||
'VEGA_TOKEN_URL'
|
||||
)}/proposals/e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829`
|
||||
);
|
||||
cy.getByTestId('market-actions-content').click();
|
||||
});
|
||||
|
||||
// 6001-MARK-060
|
||||
@@ -214,11 +214,13 @@ describe('no markets proposed', { tags: '@smoke', testIsolation: true }, () => {
|
||||
aliasGQLQuery(req, 'ProposalsList', proposal);
|
||||
});
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
cy.setOnBoardingViewed();
|
||||
});
|
||||
|
||||
it('can see no markets message', () => {
|
||||
cy.visit('/#/markets/all');
|
||||
cy.get('[data-testid="Proposed markets"]').click();
|
||||
|
||||
// 6001-MARK-061
|
||||
cy.getByTestId('tab-proposed-markets').should('contain.text', 'No markets');
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ describe('markets table', { tags: '@smoke' }, () => {
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/all');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import { mockConnectWallet } from '@vegaprotocol/cypress';
|
||||
|
||||
describe('Navbar', { tags: '@smoke' }, () => {
|
||||
beforeEach(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/');
|
||||
cy.wait('@Markets');
|
||||
cy.wait('@MarketsData');
|
||||
});
|
||||
|
||||
const pages = [
|
||||
{ name: 'Markets', link: '#/markets/all' },
|
||||
{ name: 'Trading', link: '#/markets' },
|
||||
{ name: 'Portfolio', link: '#/portfolio' },
|
||||
];
|
||||
|
||||
describe('desktop view', () => {
|
||||
pages.forEach(({ name, link }) => {
|
||||
it(`${name} should be correctly rendered`, () => {
|
||||
cy.get('nav')
|
||||
.find(`a[data-testid=${name}]:visible`)
|
||||
.then((element) => {
|
||||
cy.wrap(element).click();
|
||||
cy.location('hash').should('contain', link);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Resources dropdown should be correctly rendered', () => {
|
||||
const resourceSelector = 'ul li:contains(Resources)';
|
||||
['Docs', 'Give Feedback'].forEach((text, index) => {
|
||||
cy.get('nav').find(resourceSelector).contains('Resources').click();
|
||||
cy.get('nav')
|
||||
.find(resourceSelector)
|
||||
.find('.navigation-content li')
|
||||
.eq(index)
|
||||
.find('a')
|
||||
.then((element) => {
|
||||
expect(element.attr('target')).to.eq('_blank');
|
||||
expect(element.attr('href')).to.not.be.empty;
|
||||
expect(element.text()).to.eq(text);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Disclaimer should be presented after choosing from menu', () => {
|
||||
cy.get('nav')
|
||||
.find('ul li:contains(Resources)')
|
||||
.contains('Resources')
|
||||
.click();
|
||||
cy.getByTestId('Disclaimer').eq(0).click();
|
||||
cy.location('hash').should('equal', '#/disclaimer');
|
||||
cy.get('p').contains(
|
||||
'Vega is a decentralised peer-to-peer protocol that can be used to trade derivatives with cryptoassets.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mobile view', () => {
|
||||
const viewportHeight = Cypress.config('viewportHeight');
|
||||
const viewportWidth = Cypress.config('viewportWidth');
|
||||
before(() => {
|
||||
// a little hack to keep the viewport size between tests (cypress bug)
|
||||
Cypress.config({
|
||||
viewportWidth: 560,
|
||||
viewportHeight: 890,
|
||||
});
|
||||
cy.viewport(560, 890);
|
||||
});
|
||||
|
||||
describe('wallet drawer', () => {
|
||||
it('wallet drawer should be correctly rendered', () => {
|
||||
mockConnectWallet();
|
||||
cy.connectVegaWallet(true);
|
||||
cy.getByTestId('connect-vega-wallet-mobile').click();
|
||||
cy.getByTestId('wallets-drawer').should('be.visible');
|
||||
cy.getByTestId('wallets-drawer').within((el) => {
|
||||
cy.wrap(el).get('button').contains('Disconnect').click();
|
||||
});
|
||||
cy.getByTestId('wallets-drawer').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
describe('menu drawer', () => {
|
||||
pages.forEach(({ name, link }) => {
|
||||
it(`${name} should be correctly rendered`, () => {
|
||||
cy.getByTestId('button-menu-drawer').click();
|
||||
cy.getByTestId('menu-drawer').should('be.visible');
|
||||
cy.getByTestId('menu-drawer').within((el) => {
|
||||
cy.wrap(el).getByTestId(name).click();
|
||||
cy.location('hash').should('contain', link);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Menu drawer should not be visible until opened', () => {
|
||||
cy.getByTestId('menu-drawer').should('not.be.visible');
|
||||
cy.getByTestId('button-menu-drawer').click();
|
||||
cy.getByTestId('menu-drawer').should('be.visible');
|
||||
cy.getByTestId('button-menu-drawer').click();
|
||||
cy.getByTestId('menu-drawer').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
after(() => {
|
||||
// a little hack to keep the viewport size between tests (cypress bug)
|
||||
Cypress.config({
|
||||
viewportWidth,
|
||||
viewportHeight,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -6,6 +6,7 @@ const oracleFullProfile = 'oracle-full-profile';
|
||||
|
||||
describe('oracle information', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage(
|
||||
MarketState.STATE_ACTIVE,
|
||||
undefined,
|
||||
|
||||
@@ -14,6 +14,7 @@ const resPrice = 'price-990';
|
||||
|
||||
describe('order book', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.setOnBoardingViewed();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
|
||||
@@ -4,6 +4,7 @@ describe('Settings page', { tags: '@smoke' }, () => {
|
||||
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/');
|
||||
|
||||
// Only click if not already active otherwise sidebar will close
|
||||
|
||||
@@ -77,6 +77,7 @@ function getButtonSelectorByText(text: string): string {
|
||||
beforeEach(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
@@ -127,7 +128,7 @@ describe(
|
||||
{
|
||||
name: 'Price monitoring bounds',
|
||||
infoText:
|
||||
'Price Monitoring Bounds: Min 162.56291Max 182.96869Reference 172.47489',
|
||||
'Price Monitoring Bounds 1: Min 162.56291Max 182.96869Reference 172.47489',
|
||||
},
|
||||
];
|
||||
|
||||
@@ -154,7 +155,10 @@ describe(
|
||||
{ name: 'Volume', infoText: 'Volume: 55,000' },
|
||||
];
|
||||
cy.get(indicatorInfo).eq(1).realHover();
|
||||
cy.get('.close-button-module_closeButton__2ifkl').click({ force: true });
|
||||
cy.get('.chart__wrapper [data-testid="split-view-view"]')
|
||||
.last()
|
||||
.find('[role="button"][title="Close"]')
|
||||
.click({ force: true });
|
||||
cy.get(indicatorInfo).should('have.length', 1);
|
||||
|
||||
checkMenuItemCheckbox('Studies', studyInfo);
|
||||
@@ -171,6 +175,7 @@ describe(
|
||||
.invoke('text')
|
||||
.then((text) => {
|
||||
const actualDate = text.slice(0, -67);
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(actualDate);
|
||||
const actualOhlc = text.slice(-67);
|
||||
assert.isTrue(expectedDateRegex.test(actualDate));
|
||||
|
||||
@@ -14,6 +14,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.clearAllLocalStorage();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
@@ -21,37 +22,36 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
|
||||
it('must show place order button and connect wallet if wallet is not connected', () => {
|
||||
// 0003-WTXN-001
|
||||
cy.getByTestId('connect-vega-wallet'); // Not connected
|
||||
cy.getByTestId('order-connect-wallet').should('exist');
|
||||
cy.getByTestId(placeOrderBtn).should('exist');
|
||||
cy.getByTestId('deal-ticket-connect-wallet').should('exist');
|
||||
cy.getByTestId('get-started-button').should('exist');
|
||||
});
|
||||
|
||||
it('must be able to select order direction - long/short', function () {
|
||||
// 7002-SORD-004
|
||||
cy.getByTestId(toggleShort).click().children('input').should('be.checked');
|
||||
cy.getByTestId(toggleLong).click().children('input').should('be.checked');
|
||||
cy.getByTestId(toggleShort).click().next('input').should('be.checked');
|
||||
cy.getByTestId(toggleLong).click().next('input').should('be.checked');
|
||||
});
|
||||
|
||||
it('must be able to select order type - limit/market', function () {
|
||||
// 7002-SORD-005
|
||||
// 7002-SORD-006
|
||||
// 7002-SORD-007
|
||||
cy.getByTestId(toggleLimit).click().children('input').should('be.checked');
|
||||
cy.getByTestId(toggleMarket).click().children('input').should('be.checked');
|
||||
cy.getByTestId(toggleLimit).click().next('input').should('be.checked');
|
||||
cy.getByTestId(toggleMarket).click().next('input').should('be.checked');
|
||||
});
|
||||
|
||||
it('order connect vega wallet button should connect', () => {
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(toggleLimit).click();
|
||||
cy.getByTestId(orderPriceField).clear().type('101');
|
||||
cy.getByTestId('order-connect-wallet').click();
|
||||
cy.getByTestId('get-started-button').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
.click();
|
||||
cy.wait('@walletReq');
|
||||
cy.getByTestId(placeOrderBtn).should('be.visible');
|
||||
cy.getByTestId(toggleLimit).children('input').should('be.checked');
|
||||
cy.getByTestId(toggleLimit).next('input').should('be.checked');
|
||||
cy.getByTestId(orderPriceField).should('have.value', '101');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ describe('time in force validation', { tags: '@smoke' }, () => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.clearAllLocalStorage();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
@@ -7,7 +7,7 @@ const closePosition = 'close-position';
|
||||
const dialogCloseX = 'dialog-close';
|
||||
const dialogContent = 'dialog-content';
|
||||
const dropDownMenu = 'dropdown-menu';
|
||||
const marketActionsContent = 'market-actions-content';
|
||||
const marketActionsContent = 'position-actions-content';
|
||||
const positions = 'Positions';
|
||||
const tabPositions = 'tab-positions';
|
||||
const toastContent = 'toast-content';
|
||||
@@ -251,11 +251,13 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
|
||||
it('I can see warnings', () => {
|
||||
visitAndClickPositions();
|
||||
|
||||
cy.get('[col-id="openVolume"]').within(() => {
|
||||
cy.get('[aria-label="warning-sign icon"]')
|
||||
.should('be.visible')
|
||||
.realHover();
|
||||
});
|
||||
cy.get('[col-id="openVolume"]')
|
||||
.eq(3)
|
||||
.within(() => {
|
||||
cy.get('[aria-label="warning-sign icon"]')
|
||||
.should('be.visible')
|
||||
.realHover();
|
||||
});
|
||||
// 7004-POSI-011
|
||||
cy.getByTestId(tooltipContent).should('be.visible');
|
||||
});
|
||||
|
||||
@@ -9,6 +9,7 @@ describe('trades', { tags: '@smoke' }, () => {
|
||||
before(() => {
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.intercept('POST', '/graphql', (req) => {
|
||||
if (req.body.operationName === 'Trades') {
|
||||
req.alias = '@Trades';
|
||||
|
||||
@@ -17,6 +17,7 @@ describe(
|
||||
cy.visit('/#/portfolio');
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
@@ -122,6 +123,7 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
cy.visit('/#/portfolio');
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const amountField = 'input[name="amount"]';
|
||||
const includeTransferFeeRadioBtn = 'include-transfer-fee';
|
||||
const manageVegaWallet = 'manage-vega-wallet';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
const totalTransferfee = 'total-transfer-fee';
|
||||
const transferAmount = 'transfer-amount';
|
||||
const transferForm = 'transfer-form';
|
||||
const transferFee = 'transfer-fee';
|
||||
const walletTransfer = 'wallet-transfer';
|
||||
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
|
||||
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/');
|
||||
cy.getByTestId(manageVegaWallet).click();
|
||||
cy.getByTestId(walletTransfer).click();
|
||||
|
||||
cy.wait('@Assets');
|
||||
cy.wait('@Accounts');
|
||||
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('transfer fees tooltips', () => {
|
||||
// 1003-TRAN-015
|
||||
// 1003-TRAN-016
|
||||
// 1003-TRAN-017
|
||||
// 1003-TRAN-018
|
||||
// 1003-TRAN-019
|
||||
cy.getByTestId(transferForm);
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
/// Check Include Transfer Fee tooltip
|
||||
cy.get('label[for="include-transfer-fee"] div').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
|
||||
//Check Transfer Fee tooltip
|
||||
cy.contains('div', 'Transfer fee').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
|
||||
//Check Amount to be transferred tooltip
|
||||
cy.contains('div', 'Amount to be transferred').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
|
||||
//Check Total amount (with fee) tooltip
|
||||
cy.contains('div', 'Total amount (with fee)').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('transfer fees', () => {
|
||||
// 1003-TRAN-020
|
||||
// 1003-TRAN-021
|
||||
// 1003-TRAN-022
|
||||
// 1003-TRAN-023
|
||||
cy.getByTestId(transferForm);
|
||||
cy.contains('Enter manually').click();
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(includeTransferFeeRadioBtn).should('be.disabled');
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
cy.getByTestId(transferFee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.01');
|
||||
cy.getByTestId(transferAmount)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.00');
|
||||
cy.getByTestId(totalTransferfee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.01');
|
||||
cy.getByTestId(includeTransferFeeRadioBtn).click();
|
||||
cy.getByTestId(transferFee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.01');
|
||||
cy.getByTestId(transferAmount)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.99');
|
||||
cy.getByTestId(totalTransferfee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.00');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import { selectAsset } from '../support/helpers';
|
||||
|
||||
const amountField = 'input[name="amount"]';
|
||||
const transferText = 'transfer-intro-text';
|
||||
const errorText = 'input-error-text';
|
||||
const formFieldError = 'input-error-text';
|
||||
const keyID = `[data-testid="${transferText}"] > .rounded-md`;
|
||||
const manageVegaWallet = 'manage-vega-wallet';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
const transferForm = 'transfer-form';
|
||||
const walletTransfer = 'wallet-transfer';
|
||||
|
||||
const ASSET_EURO = 1;
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
|
||||
describe(
|
||||
'transfer form validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(manageVegaWallet).click();
|
||||
cy.getByTestId(walletTransfer).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
|
||||
cy.mockVegaWalletTransaction();
|
||||
});
|
||||
|
||||
it('transfer Text', () => {
|
||||
// 1003-TRAN-003
|
||||
cy.getByTestId(transferText)
|
||||
.should('exist')
|
||||
.get(keyID)
|
||||
.invoke('text')
|
||||
.should('match', /[\w.]{6}…[\w.]{6}/);
|
||||
});
|
||||
|
||||
it('invalid vega key validation', () => {
|
||||
//1003-TRAN-013
|
||||
//1003-TRAN-004
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm).find(toAddressField).type('asd');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Invalid Vega key');
|
||||
cy.contains('label', 'Vega key').should('be.visible');
|
||||
cy.contains('label', 'Asset').should('be.visible');
|
||||
cy.contains('label', 'Amount').should('be.visible');
|
||||
});
|
||||
|
||||
it('empty fields', () => {
|
||||
// 1003-TRAN-012
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||
});
|
||||
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
// 1003-TRAN-014
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'Value is below minimum'
|
||||
);
|
||||
});
|
||||
|
||||
it('max amount', () => {
|
||||
// 1003-TRAN-002
|
||||
// 1003-TRAN-011
|
||||
// 1003-TRAN-002
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -5,205 +5,15 @@ const amountShortName = 'input[name="amount"] + div + span.text-xs';
|
||||
const assetSelection = 'select-asset';
|
||||
const assetBalance = 'asset-balance';
|
||||
const assetOption = 'rich-select-option';
|
||||
const transferText = 'transfer-intro-text';
|
||||
const errorText = 'input-error-text';
|
||||
const formFieldError = 'input-error-text';
|
||||
const includeTransferFeeRadioBtn = 'include-transfer-fee';
|
||||
const keyID = `[data-testid="${transferText}"] > .rounded-md`;
|
||||
const manageVegaWallet = 'manage-vega-wallet';
|
||||
const openTransferButton = 'open-transfer';
|
||||
const submitTransferBtn = '[type="submit"]';
|
||||
const toAddressField = '[name="toAddress"]';
|
||||
const totalTransferfee = 'total-transfer-fee';
|
||||
const transferAmount = 'transfer-amount';
|
||||
const transferForm = 'transfer-form';
|
||||
const transferFee = 'transfer-fee';
|
||||
const walletTransfer = 'wallet-transfer';
|
||||
|
||||
const ASSET_EURO = 1;
|
||||
const ASSET_SEPOLIA_TBTC = 2;
|
||||
|
||||
const collateralTab = 'Collateral';
|
||||
const toastCloseBtn = 'toast-close';
|
||||
const toastContent = 'toast-content';
|
||||
|
||||
describe('transfer fees', { tags: '@regression', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.wait('@Assets');
|
||||
cy.wait('@Accounts');
|
||||
|
||||
cy.mockVegaWalletTransaction();
|
||||
|
||||
// Only click if not already active otherwise sidebar will close
|
||||
cy.get('[data-testid="sidebar-content"]').then(($sidebarContent) => {
|
||||
if ($sidebarContent.find('h2').text() !== 'Transfer') {
|
||||
cy.get('[data-testid="sidebar"] [data-testid="Transfer"]').click();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('transfer fees tooltips', () => {
|
||||
// 1003-TRAN-015
|
||||
// 1003-TRAN-016
|
||||
// 1003-TRAN-017
|
||||
// 1003-TRAN-018
|
||||
// 1003-TRAN-019
|
||||
cy.getByTestId(transferForm);
|
||||
cy.contains('Enter manually').click();
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
/// Check Include Transfer Fee tooltip
|
||||
cy.get('label[for="include-transfer-fee"] div').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
|
||||
//Check Transfer Fee tooltip
|
||||
cy.contains('div', 'Transfer fee').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
|
||||
//Check Amount to be transferred tooltip
|
||||
cy.contains('div', 'Amount to be transferred').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
|
||||
//Check Total amount (with fee) tooltip
|
||||
cy.contains('div', 'Total amount (with fee)').realHover();
|
||||
cy.get('[data-side="bottom"] div')
|
||||
.should('be.visible')
|
||||
.should('not.be.empty');
|
||||
});
|
||||
|
||||
it('transfer fees', () => {
|
||||
// 1003-TRAN-020
|
||||
// 1003-TRAN-021
|
||||
// 1003-TRAN-022
|
||||
// 1003-TRAN-023
|
||||
cy.getByTestId(transferForm);
|
||||
cy.contains('Enter manually').click();
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(toAddressField)
|
||||
.type('7f9cf07d3a9905b1a61a1069f7a758855da428bc0f4a97de87f48644bfc25535');
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.getByTestId(includeTransferFeeRadioBtn).should('be.disabled');
|
||||
|
||||
cy.getByTestId(transferForm)
|
||||
.find(amountField)
|
||||
.type('1', { delay: 100, force: true });
|
||||
|
||||
cy.getByTestId(transferFee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.01');
|
||||
cy.getByTestId(transferAmount)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.00');
|
||||
cy.getByTestId(totalTransferfee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.01');
|
||||
cy.getByTestId(includeTransferFeeRadioBtn).click();
|
||||
cy.getByTestId(transferFee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.01');
|
||||
cy.getByTestId(transferAmount)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '0.99');
|
||||
cy.getByTestId(totalTransferfee)
|
||||
.should('be.visible')
|
||||
.should('contain.text', '1.00');
|
||||
});
|
||||
});
|
||||
|
||||
describe(
|
||||
'transfer form validation',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setVegaWallet();
|
||||
|
||||
cy.visit('/#/portfolio');
|
||||
cy.getByTestId(manageVegaWallet).click();
|
||||
cy.getByTestId(walletTransfer).click();
|
||||
|
||||
cy.wait('@Accounts');
|
||||
cy.wait('@Assets');
|
||||
});
|
||||
|
||||
it('transfer Text', () => {
|
||||
// 1003-TRAN-003
|
||||
cy.getByTestId(transferText)
|
||||
.should('exist')
|
||||
.get(keyID)
|
||||
.invoke('text')
|
||||
.should('match', /[\w.]{6}…[\w.]{6}/);
|
||||
});
|
||||
|
||||
it('invalid vega key validation', () => {
|
||||
//1003-TRAN-013
|
||||
//1003-TRAN-004
|
||||
cy.getByTestId(transferForm).should('be.visible');
|
||||
cy.contains('Enter manually').click();
|
||||
cy.getByTestId(transferForm).find(toAddressField).type('asd');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Invalid Vega key');
|
||||
cy.contains('label', 'Vega key').should('be.visible');
|
||||
cy.contains('label', 'Asset').should('be.visible');
|
||||
cy.contains('label', 'Amount').should('be.visible');
|
||||
});
|
||||
|
||||
it('empty fields', () => {
|
||||
// 1003-TRAN-012
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(formFieldError).should('contain.text', 'Required');
|
||||
cy.getByTestId(formFieldError).should('have.length', 3);
|
||||
});
|
||||
|
||||
it('min amount', () => {
|
||||
// 1002-WITH-010
|
||||
// 1003-TRAN-014
|
||||
selectAsset(ASSET_SEPOLIA_TBTC);
|
||||
cy.get(amountField).clear().type('0');
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'Value is below minimum'
|
||||
);
|
||||
});
|
||||
|
||||
it('max amount', () => {
|
||||
// 1003-TRAN-002
|
||||
// 1003-TRAN-011
|
||||
// 1003-TRAN-002
|
||||
selectAsset(ASSET_EURO); // Will be above maximum because the vega wallet doesn't have any collateral
|
||||
cy.get(amountField).clear().type('1001', { delay: 100 });
|
||||
cy.getByTestId(transferForm).find(submitTransferBtn).click();
|
||||
cy.getByTestId(errorText).should(
|
||||
'contain.text',
|
||||
'You cannot transfer more than your available collateral'
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
describe('withdraw actions', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
cy.mockWeb3Provider();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { OrderType } from '@vegaprotocol/types';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
|
||||
const orderSizeField = 'order-size';
|
||||
@@ -9,7 +10,9 @@ export const createOrder = (order: OrderSubmission): void => {
|
||||
cy.log('Placing order', order);
|
||||
const { type, side, size, price, timeInForce, expiresAt } = order;
|
||||
|
||||
cy.getByTestId(`order-type-${type}`).click();
|
||||
cy.getByTestId(
|
||||
`order-type-${type === OrderType.TYPE_LIMIT ? 'Limit' : 'Market'}`
|
||||
).click();
|
||||
cy.getByTestId(`order-side-${side}`).click();
|
||||
cy.getByTestId(orderSizeField).clear().type(size);
|
||||
if (price) {
|
||||
|
||||
@@ -6,8 +6,8 @@ export const orderTIFDropDown = 'order-tif';
|
||||
export const placeOrderBtn = 'place-order';
|
||||
export const toggleShort = 'order-side-SIDE_SELL';
|
||||
export const toggleLong = 'order-side-SIDE_BUY';
|
||||
export const toggleLimit = 'order-type-TYPE_LIMIT';
|
||||
export const toggleMarket = 'order-type-TYPE_MARKET';
|
||||
export const toggleLimit = 'order-type-Limit';
|
||||
export const toggleMarket = 'order-type-Market';
|
||||
|
||||
export const TIFlist = Object.values(Schema.OrderTimeInForce).map((value) => {
|
||||
return {
|
||||
|
||||
+1
-1
@@ -16,6 +16,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -18,6 +18,6 @@ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supp
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -16,6 +16,6 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -18,6 +18,6 @@ NX_APP_VERSION=v0.20.21-core-0.71.6
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
|
||||
@@ -18,6 +18,6 @@ NX_APP_VERSION=v0.20.19-core-0.71.6
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -16,6 +16,6 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=true
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -17,6 +17,6 @@ NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
# NX_STOP_ORDERS
|
||||
# NX_ICEBERG_ORDERS
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -17,6 +17,6 @@ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=false
|
||||
# NX_STOP_ORDERS
|
||||
NX_STOP_ORDERS=false
|
||||
# NX_ICEBERG_ORDERS
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
@@ -0,0 +1,19 @@
|
||||
import { DepositContainer } from '@vegaprotocol/deposits';
|
||||
import { GetStarted } from '../../components/welcome-dialog';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
export const Deposit = () => {
|
||||
return (
|
||||
<div className="py-16 px-8 flex w-full justify-center">
|
||||
<div className="lg:min-w-[700px] min-w-[300px] max-w-[700px]">
|
||||
<h1 className="text-4xl xl:text-5xl uppercase font-alpha calt">
|
||||
{t('Deposit')}
|
||||
</h1>
|
||||
<div className="mt-10">
|
||||
<DepositContainer />
|
||||
<GetStarted />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,3 @@
|
||||
import { Deposit } from './deposit';
|
||||
|
||||
export default Deposit;
|
||||
@@ -23,7 +23,6 @@ export const Home = () => {
|
||||
replace: true,
|
||||
});
|
||||
} else if (data) {
|
||||
update({ shouldDisplayWelcomeDialog: true });
|
||||
const marketDataId = data[0]?.id;
|
||||
if (marketDataId) {
|
||||
navigate(Links[Routes.MARKET](marketDataId), {
|
||||
|
||||
@@ -10,14 +10,16 @@ import type { Market } from '@vegaprotocol/markets';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import {
|
||||
ResizableGrid,
|
||||
ResizableGridPanel,
|
||||
usePaneLayout,
|
||||
} from '../../components/resizable-grid';
|
||||
import { TradingViews } from './trade-views';
|
||||
import { MarketSuccessorBanner } from '../../components/market-banner';
|
||||
import {
|
||||
MarketSuccessorBanner,
|
||||
MarketSuccessorProposalBanner,
|
||||
} from '../../components/market-banner';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
interface TradeGridProps {
|
||||
@@ -55,7 +57,11 @@ const MainGrid = memo(
|
||||
>
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-main-left">
|
||||
<Tab id="chart" name={t('Chart')}>
|
||||
<Tab
|
||||
id="chart"
|
||||
name={t('Chart')}
|
||||
menu={<TradingViews.candles.menu />}
|
||||
>
|
||||
<TradingViews.candles.component marketId={marketId} />
|
||||
</Tab>
|
||||
<Tab id="depth" name={t('Depth')}>
|
||||
@@ -92,57 +98,48 @@ const MainGrid = memo(
|
||||
<TradeGridChild>
|
||||
<Tabs storageKey="console-trade-grid-bottom">
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.positions.component
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="open-orders" name={t('Open')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Open}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Open}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="closed-orders" name={t('Closed')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Closed}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Closed}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Rejected}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.orders.component
|
||||
marketId={marketId}
|
||||
filter={Filter.Rejected}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('All')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.orders.component marketId={marketId} />
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.orders.component marketId={marketId} />
|
||||
</Tab>
|
||||
{FLAGS.STOP_ORDERS ? (
|
||||
<Tab id="stop-orders" name={t('Stop orders')}>
|
||||
<TradingViews.stopOrders.component />
|
||||
</Tab>
|
||||
) : null}
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.fills.component
|
||||
marketId={marketId}
|
||||
onMarketClick={onMarketClick}
|
||||
/>
|
||||
</Tab>
|
||||
<Tab id="accounts" name={t('Collateral')}>
|
||||
<VegaWalletContainer>
|
||||
<TradingViews.collateral.component
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
hideButtons
|
||||
/>
|
||||
</VegaWalletContainer>
|
||||
<TradingViews.collateral.component
|
||||
pinnedAsset={pinnedAsset}
|
||||
onMarketClick={onMarketClick}
|
||||
hideButtons
|
||||
/>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</TradeGridChild>
|
||||
@@ -162,7 +159,12 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<div>
|
||||
{FLAGS.SUCCESSOR_MARKETS && <MarketSuccessorBanner market={market} />}
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
<>
|
||||
<MarketSuccessorBanner market={market} />
|
||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||
</>
|
||||
)}
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<div className="min-h-0 p-0.5">
|
||||
|
||||
@@ -12,7 +12,10 @@ import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { NO_MARKET } from './constants';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import classNames from 'classnames';
|
||||
import { MarketSuccessorBanner } from '../../components/market-banner';
|
||||
import {
|
||||
MarketSuccessorBanner,
|
||||
MarketSuccessorProposalBanner,
|
||||
} from '../../components/market-banner';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
interface TradePanelsProps {
|
||||
@@ -34,6 +37,7 @@ export const TradePanels = ({
|
||||
const onOrderTypeClick = useMarketLiquidityClickHandler();
|
||||
|
||||
const [view, setView] = useState<TradingView>('candles');
|
||||
|
||||
const renderView = () => {
|
||||
const Component = memo<{
|
||||
marketId: string;
|
||||
@@ -62,12 +66,33 @@ export const TradePanels = ({
|
||||
);
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
const viewCfg = TradingViews[view];
|
||||
|
||||
if ('menu' in viewCfg) {
|
||||
const Menu = viewCfg.menu;
|
||||
return (
|
||||
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||
<Menu />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
|
||||
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
|
||||
<div>
|
||||
{FLAGS.SUCCESSOR_MARKETS && <MarketSuccessorBanner market={market} />}
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
<>
|
||||
<MarketSuccessorBanner market={market} />
|
||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||
</>
|
||||
)}
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<div>{renderMenu()}</div>
|
||||
<div className="h-full">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
@@ -80,9 +105,12 @@ export const TradePanels = ({
|
||||
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||
{Object.keys(TradingViews).map((key) => {
|
||||
const isActive = view === key;
|
||||
const className = classNames('p-4 min-w-[100px] capitalize', {
|
||||
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
|
||||
});
|
||||
const className = classNames(
|
||||
'py-2 px-4 min-w-[100px] capitalize text-sm',
|
||||
{
|
||||
'bg-vega-clight-500 dark:bg-vega-cdark-500': isActive,
|
||||
}
|
||||
);
|
||||
return (
|
||||
<button
|
||||
data-testid={key}
|
||||
|
||||
@@ -2,7 +2,10 @@ import type { ComponentProps } from 'react';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { TradesContainer } from '@vegaprotocol/trades';
|
||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||
import { CandlesChartContainer } from '@vegaprotocol/candles-chart';
|
||||
import {
|
||||
CandlesChartContainer,
|
||||
CandlesMenu,
|
||||
} from '@vegaprotocol/candles-chart';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { NO_MARKET } from './constants';
|
||||
import { OrderbookContainer } from '../../components/orderbook-container';
|
||||
@@ -12,6 +15,7 @@ import { AccountsContainer } from '../../components/accounts-container';
|
||||
import { LiquidityContainer } from '../../components/liquidity-container';
|
||||
import type { OrderContainerProps } from '../../components/orders-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||
|
||||
type MarketDependantView =
|
||||
| typeof CandlesChartContainer
|
||||
@@ -34,6 +38,7 @@ export const TradingViews = {
|
||||
candles: {
|
||||
label: 'Candles',
|
||||
component: requiresMarket(CandlesChartContainer),
|
||||
menu: CandlesMenu,
|
||||
},
|
||||
depth: {
|
||||
label: 'Depth',
|
||||
@@ -74,6 +79,10 @@ export const TradingViews = {
|
||||
label: 'All',
|
||||
component: OrdersContainer,
|
||||
},
|
||||
stopOrders: {
|
||||
label: 'Stop',
|
||||
component: StopOrdersContainer,
|
||||
},
|
||||
collateral: { label: 'Collateral', component: AccountsContainer },
|
||||
fills: { label: 'Fills', component: FillsContainer },
|
||||
};
|
||||
|
||||
@@ -18,7 +18,7 @@ export const MarketsPage = () => {
|
||||
<div className="h-full pt-0.5 pb-3 px-1.5">
|
||||
<div className="h-full my-1 border border-default rounded-sm">
|
||||
<Tabs storageKey="console-markets">
|
||||
<Tab id="all-markets" name={t('All markets')}>
|
||||
<Tab id="open-markets" name={t('Open markets')}>
|
||||
<Markets />
|
||||
</Tab>
|
||||
<Tab id="proposed-markets" name={t('Proposed markets')}>
|
||||
|
||||
@@ -71,7 +71,7 @@ export const AccountHistoryContainer = () => {
|
||||
const { data: assets } = useAssetsDataProvider();
|
||||
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Connect wallet')}</Splash>;
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DepositsTable } from '@vegaprotocol/deposits';
|
||||
import { depositsProvider } from '@vegaprotocol/deposits';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useRef } from 'react';
|
||||
import type { AgGridReact } from 'ag-grid-react';
|
||||
import { useSidebar, ViewType } from '../../components/sidebar';
|
||||
|
||||
export const DepositsContainer = () => {
|
||||
const gridRef = useRef<AgGridReact | null>(null);
|
||||
const { pubKey, isReadOnly } = useVegaWallet();
|
||||
const { data, error } = useDataProvider({
|
||||
dataProvider: depositsProvider,
|
||||
@@ -17,11 +14,13 @@ export const DepositsContainer = () => {
|
||||
skip: !pubKey,
|
||||
});
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<div className="h-full">
|
||||
<DepositsTable
|
||||
rowData={data}
|
||||
ref={gridRef}
|
||||
overlayNoRowsTemplate={error ? error.message : t('No deposits')}
|
||||
/>
|
||||
{!isReadOnly && (
|
||||
|
||||
@@ -13,7 +13,6 @@ import { FillsContainer } from '../../components/fills-container';
|
||||
import { PositionsContainer } from '../../components/positions-container';
|
||||
import { WithdrawalsContainer } from './withdrawals-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { LedgerContainer } from '../../components/ledger-container';
|
||||
import { AccountHistoryContainer } from './account-history-container';
|
||||
import {
|
||||
@@ -62,29 +61,19 @@ export const Portfolio = () => {
|
||||
<PortfolioGridChild>
|
||||
<Tabs storageKey="console-portfolio-top">
|
||||
<Tab id="account-history" name={t('Account history')}>
|
||||
<VegaWalletContainer>
|
||||
<AccountHistoryContainer />
|
||||
</VegaWalletContainer>
|
||||
<AccountHistoryContainer />
|
||||
</Tab>
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<VegaWalletContainer>
|
||||
<PositionsContainer onMarketClick={onMarketClick} allKeys />
|
||||
</VegaWalletContainer>
|
||||
<PositionsContainer onMarketClick={onMarketClick} allKeys />
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<VegaWalletContainer>
|
||||
<OrdersContainer />
|
||||
</VegaWalletContainer>
|
||||
<OrdersContainer />
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<VegaWalletContainer>
|
||||
<FillsContainer onMarketClick={onMarketClick} />
|
||||
</VegaWalletContainer>
|
||||
<FillsContainer onMarketClick={onMarketClick} />
|
||||
</Tab>
|
||||
<Tab id="ledger-entries" name={t('Ledger entries')}>
|
||||
<VegaWalletContainer>
|
||||
<LedgerContainer />
|
||||
</VegaWalletContainer>
|
||||
<LedgerContainer />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</PortfolioGridChild>
|
||||
@@ -97,14 +86,10 @@ export const Portfolio = () => {
|
||||
<PortfolioGridChild>
|
||||
<Tabs storageKey="console-portfolio-bottom">
|
||||
<Tab id="collateral" name={t('Collateral')}>
|
||||
<VegaWalletContainer>
|
||||
<AccountsContainer />
|
||||
</VegaWalletContainer>
|
||||
<AccountsContainer />
|
||||
</Tab>
|
||||
<Tab id="deposits" name={t('Deposits')}>
|
||||
<VegaWalletContainer>
|
||||
<DepositsContainer />
|
||||
</VegaWalletContainer>
|
||||
<DepositsContainer />
|
||||
</Tab>
|
||||
<Tab
|
||||
id="withdrawals"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
withdrawalProvider,
|
||||
WithdrawalsTable,
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||
import { VegaWalletContainer } from '../../components/vega-wallet-container';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
|
||||
export const WithdrawalsContainer = () => {
|
||||
@@ -19,9 +18,11 @@ export const WithdrawalsContainer = () => {
|
||||
});
|
||||
const setView = useSidebar((store) => store.setView);
|
||||
const { ready, delayed } = useIncompleteWithdrawals();
|
||||
|
||||
if (!pubKey) {
|
||||
return <Splash>{t('Please connect Vega wallet')}</Splash>;
|
||||
}
|
||||
return (
|
||||
<VegaWalletContainer>
|
||||
<>
|
||||
<div className="h-full relative">
|
||||
<WithdrawalsTable
|
||||
data-testid="withdrawals-history"
|
||||
@@ -43,6 +44,6 @@ export const WithdrawalsContainer = () => {
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</VegaWalletContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback } from 'react';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import type { PinnedAsset } from '@vegaprotocol/accounts';
|
||||
import { AccountManager } from '@vegaprotocol/accounts';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user