Compare commits

...
Author SHA1 Message Date
JonRay15 eba8e3ca8b Update feature-epic.md 2023-08-10 11:58:36 +01:00
Mikołaj Młodzikowski 6894fe1264 fix(ci): hardcode env name 2023-08-10 12:53:35 +02:00
Edd 93e4b5fdb9 feat(explorer): successor markets (#4522) 2023-08-10 09:51:44 +01:00
Maciek 1566fc3984 chore: fulfill spec to 4323 (#4520) 2023-08-10 09:20:26 +02:00
Ben 39962a0246 chore(trading): update console-test runners (#4513) 2023-08-09 14:18:24 +01:00
Sam KeenandJoe 8d31510d5e feat(governance,markets,ui-toolkit): highlight successor market changes (#4449)
Co-authored-by: Joe <joe@vega.xyz>
2023-08-09 14:05:37 +01:00
Bartłomiej Głownia 26d5a67604 feat(trading): use custom toast content for stop order related transactions (#4476) 2023-08-09 14:47:10 +02:00
Bartłomiej Głownia 9cc8f5a377 feat(trading): use instrument code instead of name in positions, fills and accounts breakdown tables (#4478) 2023-08-09 12:12:07 +02:00
Matthew Russell 2e11cf4dfa fix(trading): fix persitance of resizable grid top section (#4508) 2023-08-09 11:04:32 +01:00
Bartłomiej Głownia 9e4ba9f275 feat(trading): merge deal ticket stores (#4494) 2023-08-09 11:54:16 +02:00
m.ray aac0c25d09 fix(trading): positions tooltips update (#4504) 2023-08-08 13:27:17 +01:00
Art d20c2a08ae chore(specs): succession line specs (#4505) 2023-08-08 11:23:28 +01:00
daro-maj 4e6ba5fe3d test(trading): update cypress tests (#4503) 2023-08-08 10:36:10 +02:00
Art 276d1d7c7b feat(governance): view as button (#4501) 2023-08-08 09:50:19 +02:00
m.ray 4990c5808d fix(trading): order iceberg fills (#4502) 2023-08-07 16:07:24 +00:00
Art cdd91c24f2 feat(markets): succession line (#4491) 2023-08-07 11:46:03 +00:00
Sam Keen e3eb13ca72 fix(explorer): footer visual tweaks (#4499) 2023-08-07 11:54:46 +01:00
Matthew Russell 3f0ebbf33d fix(trading): dont render get started if connected (#4490) 2023-08-07 11:35:55 +01:00
Matthew Russell 026aa3964b chore(trading): update pennant (#4493) 2023-08-07 11:06:49 +01:00
daro-maj aa4c5a4a57 test(trading): update cypress tests (#4496) 2023-08-07 10:28:13 +02:00
Matthew Russell 0874314d2a chore: add specs (#4497) 2023-08-07 08:41:25 +01:00
Matthew Russell a70a7bcc8e fix(trading): dont show connect wallet message in deal ticket (#4482) 2023-08-04 11:09:43 +01:00
Matthew Russell 4461a6a497 fix(trading): remove hover state from disabled view as button (#4487) 2023-08-04 11:08:48 +01:00
Matthew Russell 1041864ad8 chore(trading): remove unnecessary forwarding of grid refs (#4486) 2023-08-04 11:08:28 +01:00
Matthew Russell 015e0188ec chore: enable iceberg orders 2023-08-03 12:38:51 +01:00
Matthew Russell 228236c152 chore(deal-ticket): type dropdown icon (#4468) 2023-08-03 12:15:21 +01:00
Art cd6e906dc6 feat(trading): view as button in sidebar (#4470) 2023-08-03 12:14:35 +01:00
Maciek 8a47a92dbd fix(trading): add info splashes on deposit and withdrawals list containers (#4479) 2023-08-03 12:01:42 +01:00
Matthew Russell af14c27df9 chore(trading): responsive welcome dialog content and content update (#4475) 2023-08-03 12:01:09 +01:00
Matthew Russell 38edcd88c0 chore(trading): fix alignment of expire (#4477) 2023-08-03 12:00:12 +01:00
m.ray a74cf02030 feat(trading): order details dialog (#4281) 2023-08-03 10:05:12 +00:00
Joe Tsang 6686755763 chore(governance): fix failing tests from nightly run (#4453) 2023-08-03 10:01:00 +01:00
Joe Tsang 79cf8163be chore(governance): add e2e test for successor market (#4471) 2023-08-03 09:54:51 +01:00
Matthew Russell 1688e26f4b feat(trading): add fairground texture background to logo (#4474) 2023-08-03 09:28:01 +01:00
Art 5e101a8eaf feat(trading): deposit page (#4472) 2023-08-02 19:06:49 +02:00
Matthew Russell 65071f57d0 fix(trading,governance): eager connection for browser wallet (#4444) 2023-08-02 17:16:09 +01:00
daro-maj b87c981a90 test(trading): fix for key-to-key test (#4469) 2023-08-02 18:11:31 +02:00
Joe TsangandMatthew Russell b509e5b347 test(explorer): e2e proposal test (#4379)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-02 16:37:55 +01:00
Maciek bb9fce4831 feat(trading): 4322 get started steps (#4440) 2023-08-02 15:34:04 +00:00
Matthew Russell 918825aca8 chore: adjust spacing and text size of long/short type selectors 2023-08-02 15:36:39 +01:00
Matthew Russell 74bf183fad feat(trading): 3114 pane context (#4442) 2023-08-02 15:29:41 +01:00
m.ray 674baa1ee3 fix(trading): iceberg order fills (#4460) 2023-08-02 16:55:49 +03:00
Matthew Russell d18a0c7e15 fix(trading): lp table forward ref breaking resize grid (#4447) 2023-08-02 13:59:05 +01:00
m.ray 76a6005b77 fix(trading): replace all markets with open markets (#4452) 2023-08-02 13:58:26 +01:00
JonRay15andMatthew Russell 79414614c2 chore(trading,governance,explorer): fix issue template (#4459)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-02 12:52:51 +01:00
m.ray fde49e5446 fix(trading): order stopped misleading message (#4455) 2023-08-02 11:52:07 +01:00
JonRay15andMatthew Russell 45283161aa chore: create feature-epic issue template (#4456)
Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
2023-08-02 11:48:23 +01:00
Bartłomiej GłowniaandDariusz Majcherczyk 375d447fa8 feat(trading): add stop orders table and form (#4265)
Co-authored-by: Dariusz Majcherczyk <dariusz.majcherczyk@gmail.com>
2023-08-02 11:28:33 +01:00
Mikołaj Młodzikowski 1dd97a2bce feat(ci): add develop docker image (#4458) 2023-08-02 10:58:17 +02:00
Edd d4a1a9b193 fix(explorer): add tests and fix transactions url generation (#4454) 2023-08-01 16:35:40 +01:00
Edd 376c5241a8 feat(explorer): improve transaction pagination (#4435) 2023-08-01 14:41:16 +00:00
287 changed files with 11477 additions and 4269 deletions
+28
View File
@@ -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: 'FEATURE 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
+3 -3
View File
@@ -10,8 +10,8 @@ on:
jobs: jobs:
run-tests: run-tests:
name: run-tests name: run-tests
runs-on: ubuntu-22.04 runs-on: 8-cores
timeout-minutes: 25 timeout-minutes: 20
steps: steps:
#---------------------------------------------- #----------------------------------------------
# check-out frontend-monorepo # check-out frontend-monorepo
@@ -125,7 +125,7 @@ jobs:
#---------------------------------------------- #----------------------------------------------
- name: Run tests - name: Run tests
working-directory: ./console-test working-directory: ./console-test
run: poetry run pytest --numprocesses auto run: poetry run pytest -s --numprocesses auto
- name: Check files - name: Check files
run: | run: |
ls -al . ls -al .
+9 -3
View File
@@ -29,6 +29,12 @@ jobs:
echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV echo IS_TESTNET_RELEASE=false >> $GITHUB_ENV
echo IS_IPFS_RELEASE=false >> $GITHUB_ENV echo IS_IPFS_RELEASE=false >> $GITHUB_ENV
echo IS_S3_RELEASE=false >> $GITHUB_ENV 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 - name: Is PR
if: ${{ github.event_name == 'pull_request' }} if: ${{ github.event_name == 'pull_request' }}
@@ -75,7 +81,7 @@ jobs:
- name: Log in to the Container registry (docker hub) - name: Log in to the Container registry (docker hub)
uses: docker/login-action@v2 uses: docker/login-action@v2
if: ${{ env.IS_IPFS_RELEASE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }} password: ${{ secrets.DOCKERHUB_TOKEN }}
@@ -173,7 +179,7 @@ jobs:
uses: docker/build-push-action@v3 uses: docker/build-push-action@v3
continue-on-error: true continue-on-error: true
id: dockerhub-push id: dockerhub-push
if: ${{ env.IS_IPFS_RELEASE == 'true' }} if: ${{ env.IS_IPFS_RELEASE == 'true' || env.IS_DEV_IMAGE == 'true' }}
with: with:
context: . context: .
file: docker/node-outside-docker.Dockerfile file: docker/node-outside-docker.Dockerfile
@@ -183,7 +189,7 @@ jobs:
ENV_NAME=${{ env.ENV_NAME }} ENV_NAME=${{ env.ENV_NAME }}
tags: | tags: |
vegaprotocol/${{ matrix.app }}:${{ github.sha }} 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) - name: Publish dist as docker image (ghcr - retry)
uses: docker/build-push-action@v3 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);
});
});
});
});
@@ -23,7 +23,7 @@ export const Footer = () => {
); );
return ( 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"> <div className="flex justify-between gap-2 align-middle">
{GIT_COMMIT_HASH && ( {GIT_COMMIT_HASH && (
<div className="content-center flex border-r border-neutral-700 dark:border-neutral-300 pr-4"> <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>
)} )}
<div className="content-center flex pl-2 md:border-r border-neutral-700 dark:border-neutral-300 pr-4"> <div className="content-center flex pr-4 md:border-r border-neutral-700 dark:border-neutral-300">
{VEGA_URL && <NodeUrl url={VEGA_URL} />} <span className="pr-2">{VEGA_URL && <NodeUrl url={VEGA_URL} />}</span>
<Link className="ml-2" onClick={() => setNodeSwitcherOpen(true)}> <Link
className="ml-2 underline-offset-4"
onClick={() => setNodeSwitcherOpen(true)}
>
{t('Change')} {t('Change')}
</Link> </Link>
</div> </div>
@@ -59,7 +62,10 @@ export const Footer = () => {
) : null} ) : null}
</div> </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"> <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 Disclaimer
</RouteLink> </RouteLink>
</div> </div>
@@ -1,6 +1,9 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import type { MarketInfoWithData } from '@vegaprotocol/markets'; import type { MarketInfoWithData } from '@vegaprotocol/markets';
import { PriceMonitoringBoundsInfoPanel } from '@vegaprotocol/markets'; import {
PriceMonitoringBoundsInfoPanel,
SuccessionLineInfoPanel,
} from '@vegaprotocol/markets';
import { import {
LiquidityInfoPanel, LiquidityInfoPanel,
LiquidityMonitoringParametersInfoPanel, LiquidityMonitoringParametersInfoPanel,
@@ -103,6 +106,8 @@ export const MarketDetails = ({ market }: { market: MarketInfoWithData }) => {
<OracleInfoPanel market={market} type="settlementData" /> <OracleInfoPanel market={market} type="settlementData" />
</> </>
)} )}
<h2 className={`${headerClassName} mb-4`}>{t('Succession line')}</h2>
<SuccessionLineInfoPanel market={market} />
</div> </div>
); );
}; };
@@ -10,6 +10,8 @@ import Hash from '../../links/hash';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new'; import { ProposalSignatureBundleNewAsset } from './proposal/signature-bundle-new';
import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update'; import { ProposalSignatureBundleUpdateAsset } from './proposal/signature-bundle-update';
import { MarketLink } from '../../links';
import { formatNumber } from '@vegaprotocol/utils';
export type Proposal = components['schemas']['v1ProposalSubmission']; export type Proposal = components['schemas']['v1ProposalSubmission'];
export type ProposalTerms = components['schemas']['vegaProposalTerms']; export type ProposalTerms = components['schemas']['vegaProposalTerms'];
@@ -53,7 +55,11 @@ export function proposalTypeLabel(terms?: ProposalTerms): string {
} else if (has(terms, 'updateAsset')) { } else if (has(terms, 'updateAsset')) {
return t('Update asset proposal'); return t('Update asset proposal');
} else if (has(terms, 'newMarket')) { } else if (has(terms, 'newMarket')) {
return t('New market proposal'); if (terms?.newMarket?.changes?.successor) {
return t('Successor market proposal');
} else {
return t('New market proposal');
}
} else if (has(terms, 'updateMarket')) { } else if (has(terms, 'updateMarket')) {
return t('Update market proposal'); return t('Update market proposal');
} else if (has(terms, 'updateNetworkParameter')) { } else if (has(terms, 'updateNetworkParameter')) {
@@ -85,6 +91,13 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
} }
const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset; const tx = proposal.terms?.newAsset || proposal.terms?.updateAsset;
const isSuccessorMarketProposal =
proposal?.terms?.newMarket?.changes?.successor;
const parentMarketId =
isSuccessorMarketProposal &&
proposal?.terms.newMarket?.changes?.successor?.parentMarketId;
const insurancePoolFraction =
proposal?.terms?.newMarket?.changes?.successor?.insurancePoolFraction;
// This component is not rendered if no bundle is required // This component is not rendered if no bundle is required
const SignatureBundleComponent = proposal.terms?.newAsset const SignatureBundleComponent = proposal.terms?.newAsset
@@ -111,6 +124,30 @@ export const TxProposal = ({ txData, pubKey, blockData }: TxProposalProps) => {
<Hash text={deterministicId} /> <Hash text={deterministicId} />
</TableCell> </TableCell>
</TableRow> </TableRow>
{isSuccessorMarketProposal ? (
<>
{parentMarketId ? (
<TableRow modifier="bordered">
<TableCell>{t('Previous market')}</TableCell>
<TableCell>
<MarketLink
id={
proposal.terms?.newMarket.changes.successor.parentMarketId
}
/>
</TableCell>
</TableRow>
) : null}
{insurancePoolFraction ? (
<TableRow modifier="bordered">
<TableCell>{t('Insurance pool fraction')}</TableCell>
<TableCell>
{formatNumber(Number(insurancePoolFraction) * 100, 0)}%
</TableCell>
</TableRow>
) : null}
</>
) : null}
</TableWithTbody> </TableWithTbody>
<ProposalSummary <ProposalSummary
id={deterministicId} id={deterministicId}
@@ -10,7 +10,6 @@ import {
DropdownMenuTrigger, DropdownMenuTrigger,
DropdownMenuSubContent, DropdownMenuSubContent,
Icon, Icon,
Button,
} from '@vegaprotocol/ui-toolkit'; } from '@vegaprotocol/ui-toolkit';
import type { Dispatch, SetStateAction } from 'react'; import type { Dispatch, SetStateAction } from 'react';
import { FilterLabel } from './tx-filter-label'; import { FilterLabel } from './tx-filter-label';
@@ -100,15 +99,13 @@ export const TxsFilter = ({ filters, setFilters }: TxFilterProps) => {
<DropdownMenu <DropdownMenu
modal={false} modal={false}
trigger={ trigger={
<DropdownMenuTrigger className="ml-0"> <DropdownMenuTrigger>
<Button size="xs" data-testid="filter-trigger"> <FilterLabel filters={filters} />
<FilterLabel filters={filters} />
</Button>
</DropdownMenuTrigger> </DropdownMenuTrigger>
} }
> >
<DropdownMenuContent> <DropdownMenuContent>
{filters.size > 0 ? null : ( {filters.size > 1 ? null : (
<> <>
<DropdownMenuCheckboxItem <DropdownMenuCheckboxItem
onCheckedChange={() => setFilters(new Set(AllFilterOptions))} onCheckedChange={() => setFilters(new Set(AllFilterOptions))}
@@ -44,7 +44,7 @@ export const TxsListNavigation = ({
</Button> </Button>
<Button <Button
size="xs" size="xs"
disabled={!hasMoreTxs || loading} disabled={!hasMoreTxs}
onClick={() => { onClick={() => {
nextPage(); nextPage();
}} }}
@@ -78,7 +78,11 @@ export function getLabelForProposal(
} else if (proposal.terms?.updateAsset) { } else if (proposal.terms?.updateAsset) {
return t('Proposal: Update asset'); return t('Proposal: Update asset');
} else if (proposal.terms?.newMarket) { } else if (proposal.terms?.newMarket) {
return t('Proposal: New market'); if (proposal.terms?.newMarket.changes?.successor) {
return t('Proposal: Successor market');
} else {
return t('Proposal: New market');
}
} else if (proposal.terms?.updateMarket) { } else if (proposal.terms?.updateMarket) {
return t('Proposal: Update market'); return t('Proposal: Update market');
} else if (proposal.terms?.updateNetworkParameter) { } else if (proposal.terms?.updateNetworkParameter) {
@@ -86,14 +86,18 @@ describe('Txs infinite list item', () => {
render( render(
<MockedProvider> <MockedProvider>
<MemoryRouter> <MemoryRouter>
<TxsInfiniteListItem <table>
type="testType" <tbody>
submitter="testPubKey" <TxsInfiniteListItem
hash="testTxHash" type="testType"
block="1" submitter="testPubKey"
code={0} hash="testTxHash"
command={{}} block="1"
/> code={0}
command={{}}
/>
</tbody>
</table>
</MemoryRouter> </MemoryRouter>
</MockedProvider> </MockedProvider>
); );
@@ -14,9 +14,9 @@ const DEFAULT_TRUNCATE_LENGTH = 7;
export function getIdTruncateLength(screen: Screen): number { export function getIdTruncateLength(screen: Screen): number {
if (['xxxl', 'xxl'].includes(screen)) { if (['xxxl', 'xxl'].includes(screen)) {
return 64;
} else if (['xl', 'lg', 'md'].includes(screen)) {
return 32; return 32;
} else if (['xl', 'lg', 'md'].includes(screen)) {
return 16;
} }
return DEFAULT_TRUNCATE_LENGTH; return DEFAULT_TRUNCATE_LENGTH;
} }
@@ -5,11 +5,17 @@ import type { BlockExplorerTransactionResult } from '../../routes/types/block-ex
import { Side } from '@vegaprotocol/types'; import { Side } from '@vegaprotocol/types';
import { MockedProvider } from '@apollo/client/testing'; 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[] => { const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
return Array.from(Array(number)).map((_) => ({ return Array.from(Array(number)).map((_) => ({
block: '87901', block: '87901',
index: 2, index: 2,
hash: '0F8B98DA0923A50786B852D9CA11E051CACC4C733E1DB93D535C7D81DBD10F6F', hash: generateHash(),
submitter: submitter:
'4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964', '4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
type: 'Submit Order', type: 'Submit Order',
@@ -2,7 +2,7 @@ import { Table, TableRow } from '../table';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers'; import { useFetch } from '@vegaprotocol/react-helpers';
import type { BlockExplorerTransactions } from '../../routes/types/block-explorer-response'; 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 { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
import EmptyList from '../empty-list/empty-list'; import EmptyList from '../empty-list/empty-list';
import { TxsInfiniteListItem } from './txs-infinite-list-item'; import { TxsInfiniteListItem } from './txs-infinite-list-item';
@@ -14,7 +14,7 @@ interface TxsPerBlockProps {
export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => { export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
const filters = `filters[block.height]=${blockHeight}`; const filters = `filters[block.height]=${blockHeight}`;
const url = getTxsDataUrl({ limit: txCount.toString(), filters }); const url = getTxsDataUrl({ filters, count: txCount });
const { const {
state: { data, loading, error }, state: { data, loading, error },
} = useFetch<BlockExplorerTransactions>(url); } = 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);
});
});
+103 -92
View File
@@ -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 { useFetch } from '@vegaprotocol/react-helpers';
import type { import type {
BlockExplorerTransactionResult, BlockExplorerTransactionResult,
BlockExplorerTransactions, BlockExplorerTransactions,
} from '../routes/types/block-explorer-response'; } from '../routes/types/block-explorer-response';
import { DATA_SOURCES } from '../config';
import isNumber from 'lodash/isNumber'; 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 { export function getTypeFilters(filters?: Set<FilterOption>) {
txsData: BlockExplorerTransactionResult[]; if (!filters) {
hasMoreTxs: boolean; return '';
cursor: string; } else if (filters.size > 1) {
previousCursors: string[]; return '';
hasPreviousPage: boolean; }
const forcedSingleFilter = Array.from(filters)[0];
return `filters[cmd.type]=${forcedSingleFilter}`;
} }
export interface IUseTxsData { export interface IUseTxsData {
limit: number; count?: number;
filters?: string; before?: string;
after?: string;
party?: string;
filters?: Set<FilterOption>;
} }
interface IGetTxsDataUrl { export const useTxsData = ({
limit: string; count = 25,
filters?: string; before,
} after,
filters,
party,
}: IUseTxsData) => {
const [, setSearchParams] = useSearchParams();
let hasMoreTxs = true;
let txsData: BlockExplorerTransactionResult[] = [];
export const getTxsDataUrl = ({ limit, filters }: IGetTxsDataUrl) => { const url = getTxsDataUrl({
const url = new URL(`${DATA_SOURCES.blockExplorerUrl}/transactions`); filters: getTypeFilters(filters),
count,
if (limit) { before,
url.searchParams.append('limit', limit); after,
} party,
// 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({ limit: limit.toString(), filters });
const { const {
state: { data, error, loading }, state: { data, error, loading },
refetch, refetch,
} = useFetch<BlockExplorerTransactions>(url, {}, true); } = useFetch<BlockExplorerTransactions>(url, {}, true);
useEffect(() => { if (!loading && data && isNumber(data.transactions.length)) {
if (!loading && data && isNumber(data.transactions.length)) { hasMoreTxs = data.transactions.length >= count;
setTxsState((prev) => { txsData = data.transactions;
return { }
...prev,
txsData: data.transactions,
hasMoreTxs: data.transactions.length >= limit,
cursor: data?.transactions.at(-1)?.cursor || '',
};
});
}
}, [loading, setTxsState, data, limit]);
const nextPage = useCallback(() => { const nextPage = useCallback(() => {
const c = data?.transactions.at(0)?.cursor; const after = data?.transactions.at(-1)?.cursor || '';
const newPreviousCursors = c ? [...previousCursors, c] : previousCursors; const params: URLSearchParamsInit = { after };
if (filters) {
setTxsState((prev) => ({ params.filters = Array.from(filters).join(',');
...prev, }
hasPreviousPage: true, setSearchParams(params);
previousCursors: newPreviousCursors, }, [filters, data, setSearchParams]);
}));
return refetch({
limit,
before: cursor,
});
}, [data, previousCursors, cursor, limit, refetch]);
const previousPage = useCallback(() => { const previousPage = useCallback(() => {
const previousCursor = [...previousCursors].pop(); const before = data?.transactions[0]?.cursor || '';
const newPreviousCursors = previousCursors.slice(0, -1); const params: URLSearchParamsInit = { before };
setTxsState((prev) => ({ if (filters && filters.size > 0 && filters.size === 1) {
...prev, params.filters = Array.from(filters)[0];
hasPreviousPage: newPreviousCursors.length > 0, }
previousCursors: newPreviousCursors, setSearchParams(params);
})); }, [filters, data, setSearchParams]);
return refetch({
limit,
before: previousCursor,
});
}, [previousCursors, limit, refetch]);
const refreshTxs = useCallback(async () => { const refreshTxs = useCallback(async () => {
setTxsState(() => ({ const params: URLSearchParamsInit = {};
txsData: [], if (filters && filters.size > 0 && filters.size === 1) {
cursor: '', params.filters = Array.from(filters)[0];
previousCursors: [], }
hasMoreTxs: false, setSearchParams(params);
hasPreviousPage: false,
}));
refetch({ limit }); refetch({ count: BE_TXS_PER_REQUEST });
}, [setTxsState, limit, refetch, filters]); // eslint-disable-line react-hooks/exhaustive-deps }, [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 { return {
updateFilters,
txsData, txsData,
loading, loading,
error, error,
hasMoreTxs, hasMoreTxs,
hasPreviousPage,
previousCursors,
cursor,
refreshTxs, refreshTxs,
nextPage, nextPage,
previousPage, 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({ (useFetch as jest.Mock).mockReturnValue({
state: { data: createBlockResponse(1), loading: false, error: null }, state: { data: createBlockResponse(1), loading: false, error: null },
}); });
render(renderComponent(1)); render(renderComponent(1));
await waitFor(() => screen.getByTestId('block-header')); await waitFor(() => screen.getByTestId('block-header'));
expect(screen.getByTestId('previous-block-button')).toHaveAttribute( 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 { isValidPartyId } from './components/party-id-error';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { TxsListNavigation } from '../../../components/txs/tx-list-navigation'; 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 { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
import { useSearchParams } from 'react-router-dom';
const Party = () => { const Party = () => {
const [params] = useSearchParams();
const [filters, setFilters] = useState(new Set(AllFilterOptions)); const [filters, setFilters] = useState(new Set(AllFilterOptions));
const { party } = useParams<{ party: string }>(); const { party } = useParams<{ party: string }>();
@@ -27,24 +31,21 @@ const Party = () => {
const partyId = toNonHex(party ? party : ''); const partyId = toNonHex(party ? party : '');
const { isMobile } = useScreenDimensions(); const { isMobile } = useScreenDimensions();
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]); 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 { const {
hasMoreTxs,
nextPage, nextPage,
refreshTxs,
previousPage, previousPage,
error, error,
refreshTxs,
loading, loading,
txsData, txsData,
hasPreviousPage, hasMoreTxs,
updateFilters,
} = useTxsData({ } = useTxsData({
limit: 25, filters: filters.size === 1 ? filters : undefined,
filters: f, before: params.get('before') || undefined,
after: !params.get('before') ? params.get('after') || undefined : undefined,
party: partyId,
}); });
const variables = useMemo(() => ({ partyId }), [partyId]); const variables = useMemo(() => ({ partyId }), [partyId]);
@@ -102,15 +103,21 @@ const Party = () => {
refreshTxs={refreshTxs} refreshTxs={refreshTxs}
nextPage={nextPage} nextPage={nextPage}
previousPage={previousPage} previousPage={previousPage}
hasPreviousPage={hasPreviousPage} hasPreviousPage={true}
loading={loading} loading={loading}
hasMoreTxs={hasMoreTxs} hasMoreTxs={hasMoreTxs}
> >
<TxsFilter filters={filters} setFilters={setFilters} /> <TxsFilter
filters={filters}
setFilters={(f) => {
setFilters(f);
updateFilters(f as Set<FilterOption>);
}}
/>
</TxsListNavigation> </TxsListNavigation>
{!error && txsData ? ( {!error && txsData ? (
<TxsInfiniteList <TxsInfiniteList
hasMoreTxs={hasMoreTxs} hasMoreTxs={true}
areTxsLoading={loading} areTxsLoading={loading}
txs={txsData} txs={txsData}
loadMoreTxs={nextPage} loadMoreTxs={nextPage}
+19 -16
View File
@@ -1,14 +1,14 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../../components/route-title'; import { RouteTitle } from '../../../components/route-title';
import { TxsInfiniteList } from '../../../components/txs'; 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 { useDocumentTitle } from '../../../hooks/use-document-title';
import { useState } from 'react'; 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'; import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
import { useSearchParams } from 'react-router-dom';
const BE_TXS_PER_REQUEST = 25;
export const TxsList = () => { export const TxsList = () => {
useDocumentTitle(['Transactions']); useDocumentTitle(['Transactions']);
@@ -27,25 +27,22 @@ export const TxsList = () => {
* @returns {JSX.Element} Transaction List and controls * @returns {JSX.Element} Transaction List and controls
*/ */
export const TxsListFiltered = () => { export const TxsListFiltered = () => {
const [filters, setFilters] = useState(new Set(AllFilterOptions)); const [params] = useSearchParams();
const [filters, setFilters] = useState(getInitialFilters(params));
const f =
filters && filters.size === 1
? `filters[cmd.type]=${Array.from(filters)[0]}`
: '';
const { const {
hasMoreTxs,
nextPage, nextPage,
previousPage, previousPage,
error, error,
refreshTxs, refreshTxs,
loading, loading,
txsData, txsData,
hasPreviousPage, hasMoreTxs,
updateFilters,
} = useTxsData({ } = useTxsData({
limit: BE_TXS_PER_REQUEST, filters: filters.size === 1 ? filters : undefined,
filters: f, before: params.get('before') || undefined,
after: !params.get('before') ? params.get('after') || undefined : undefined,
}); });
return ( return (
@@ -54,11 +51,17 @@ export const TxsListFiltered = () => {
refreshTxs={refreshTxs} refreshTxs={refreshTxs}
nextPage={nextPage} nextPage={nextPage}
previousPage={previousPage} previousPage={previousPage}
hasPreviousPage={hasPreviousPage} hasPreviousPage={true}
loading={loading} loading={loading}
hasMoreTxs={hasMoreTxs} hasMoreTxs={hasMoreTxs}
> >
<TxsFilter filters={filters} setFilters={setFilters} /> <TxsFilter
filters={filters}
setFilters={(f) => {
setFilters(f);
updateFilters(f as Set<FilterOption>);
}}
/>
</TxsListNavigation> </TxsListNavigation>
<TxsInfiniteList <TxsInfiniteList
hasFilters={filters.size > 0} hasFilters={filters.size > 0}
@@ -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"
}
}
}
@@ -220,7 +220,7 @@ context(
function () { function () {
const proposalTitle = 'Test new market proposal'; const proposalTitle = 'Test new market proposal';
goToMakeNewProposal(governanceProposalType.NEW_MARKET); 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.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => { cy.fixture('/proposals/new-market').then((newMarketProposal) => {
const newMarketPayload = JSON.stringify(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 () { after('Disassociate from second wallet key if present', function () {
cy.reload(); cy.reload();
waitForSpinner(); waitForSpinner();
@@ -642,6 +730,7 @@ context(
.getByTestId('key-value-table-row') .getByTestId('key-value-table-row')
.contains(heading) .contains(heading)
.parent() .parent()
.parent()
.siblings(); .siblings();
} }
} }
@@ -96,12 +96,14 @@ context('rewards - flow', { tags: '@slow' }, function () {
.within(() => { .within(() => {
cy.get('h2').first().should('contain.text', 'EPOCH'); cy.get('h2').first().should('contain.text', 'EPOCH');
cy.getByTestId('individual-rewards-asset').should('have.text', 'Vega'); cy.getByTestId('individual-rewards-asset').should('have.text', 'Vega');
cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut) cy.getByTestId('ACCOUNT_TYPE_GLOBAL_REWARD', rewardsTimeOut).should(
.should('contain.text', '0.4415') 'contain.text',
.and('contain.text', '(44.15%)'); '0.4415'
cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE') );
.should('contain.text', '0.0004') cy.getByTestId('ACCOUNT_TYPE_FEES_INFRASTRUCTURE').should(
.and('contain.text', '(44.15%)'); 'contain.text',
'0.0004'
);
cy.getByTestId('total').should('have.text', '0.4419'); cy.getByTestId('total').should('have.text', '0.4419');
}); });
}); });
@@ -339,7 +339,7 @@ context(
cy.getByTestId(vegaWalletCurrencyTitle) cy.getByTestId(vegaWalletCurrencyTitle)
.contains(name) .contains(name)
.parent() .parent()
.siblings() .siblings(txTimeout)
.should((elementAmount) => { .should((elementAmount) => {
const displayedAmount = parseFloat(elementAmount.text()); const displayedAmount = parseFloat(elementAmount.text());
expect(displayedAmount).be.gte(expectedAmount); expect(displayedAmount).be.gte(expectedAmount);
@@ -107,7 +107,7 @@ export function dissociateFromSecondWalletKey() {
cy.getByTestId('vega-in-wallet') cy.getByTestId('vega-in-wallet')
.first() .first()
.within(() => { .within(() => {
cy.getByTestId('eth-wallet-associated-balances') cy.getByTestId('eth-wallet-associated-balances', txTimeout)
.last() .last()
.within(() => { .within(() => {
cy.getByTestId('associated-key') cy.getByTestId('associated-key')
@@ -78,13 +78,21 @@ export function stakingPageAssociateTokens(
} }
cy.get(tokenAmountInputBox, epochTimeout).type(amount); cy.get(tokenAmountInputBox, epochTimeout).type(amount);
if (approve) { if (approve) {
cy.get(tokenInputApprove, txTimeout).should('be.enabled').click(); cy.getByTestId('wallet-associate').then((walletAssociateField) => {
cy.contains('Approve $VEGA Tokens for staking on Vega').should( if (
'be.visible' walletAssociateField.find('[data-testid="token-input-approve-button"]')
); .length
cy.contains('Approve $VEGA Tokens for staking on Vega', txTimeout).should( ) {
'not.exist' 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(); cy.get(tokenSubmitButton, txTimeout).should('be.enabled').click();
@@ -1,4 +1,8 @@
import { VegaConnectDialog, VegaManageDialog } from '@vegaprotocol/wallet'; import {
VegaConnectDialog,
VegaManageDialog,
ViewAsDialog,
} from '@vegaprotocol/wallet';
import { import {
AppStateActionType, AppStateActionType,
useAppState, useAppState,
@@ -25,6 +29,8 @@ export const VegaWalletDialogs = () => {
}) })
} }
/> />
<ViewAsDialog connector={Connectors.view} />
</> </>
); );
}; };
@@ -1,17 +0,0 @@
import { Link } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { ExternalLinks } from '@vegaprotocol/environment';
export const DownloadWalletPrompt = () => {
const { t } = useTranslation();
return (
<>
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
<p>
<Link className="text-neutral-500" href={ExternalLinks.VEGA_WALLET_URL}>
{t('getWalletLink')}
</Link>
</p>
</>
);
};
@@ -0,0 +1,25 @@
import { ButtonLink, Link } from '@vegaprotocol/ui-toolkit';
import { useTranslation } from 'react-i18next';
import { ExternalLinks } from '@vegaprotocol/environment';
import { useViewAsDialog } from '@vegaprotocol/wallet';
export const VegaWalletPrompt = () => {
const { t } = useTranslation();
const setViewAsDialog = useViewAsDialog((state) => state.setOpen);
return (
<>
<h3 className="mt-4 mb-2">{t('getWallet')}</h3>
<div className="flex flex-row gap-4">
<Link className="text-neutral-500" href={ExternalLinks.VEGA_WALLET_URL}>
{t('getWalletLink')}
</Link>
<ButtonLink
className="text-neutral-500"
onClick={() => setViewAsDialog(true)}
>
{t('viewAsParty')}
</ButtonLink>
</div>
</>
);
};
@@ -20,7 +20,7 @@ import {
WalletCardHeader, WalletCardHeader,
WalletCardRow, WalletCardRow,
} from '../wallet-card'; } from '../wallet-card';
import { DownloadWalletPrompt } from './download-wallet-prompt'; import { VegaWalletPrompt } from './vega-wallet-prompt';
import { usePollForDelegations } from './hooks'; import { usePollForDelegations } from './hooks';
import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet'; import { useVegaWallet, useVegaWalletDialogStore } from '@vegaprotocol/wallet';
import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit'; import { Button, ButtonLink } from '@vegaprotocol/ui-toolkit';
@@ -85,7 +85,7 @@ const VegaWalletNotConnected = () => {
> >
{t('connectVegaWalletToUseAssociated')} {t('connectVegaWalletToUseAssociated')}
</Button> </Button>
<DownloadWalletPrompt /> <VegaWalletPrompt />
</> </>
); );
}; };
@@ -855,5 +855,6 @@
"Estimated time to upgrade": "Estimated time to upgrade", "Estimated time to upgrade": "Estimated time to upgrade",
"Upgraded at": "Upgraded at", "Upgraded at": "Upgraded at",
"dataIsIdentical": "Data is identical", "dataIsIdentical": "Data is identical",
"updatesToMarket": "Updates to market" "updatesToMarket": "Updates to market",
"viewAsParty": "View as party"
} }
@@ -45,8 +45,10 @@ export const useMarketDataDialogStore = create<MarketDataDialogState>(
export const ProposalMarketData = ({ export const ProposalMarketData = ({
marketData, marketData,
parentMarketData,
}: { }: {
marketData: MarketInfoWithData; marketData: MarketInfoWithData;
parentMarketData?: MarketInfoWithData;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { isOpen, open, close } = useMarketDataDialogStore(); const { isOpen, open, close } = useMarketDataDialogStore();
@@ -58,8 +60,21 @@ export const ProposalMarketData = ({
const settlementData = marketData.tradableInstrument.instrument.product const settlementData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForSettlementData.data as DataSourceDefinition; .dataSourceSpecForSettlementData.data as DataSourceDefinition;
const parentSettlementData =
parentMarketData?.tradableInstrument.instrument?.product
?.dataSourceSpecForSettlementData?.data;
const terminationData = marketData.tradableInstrument.instrument.product const terminationData = marketData.tradableInstrument.instrument.product
.dataSourceSpecForTradingTermination.data as DataSourceDefinition; .dataSourceSpecForTradingTermination.data as DataSourceDefinition;
const parentTerminationData =
parentMarketData?.tradableInstrument.instrument?.product
?.dataSourceSpecForTradingTermination?.data;
const isParentSettlementDataEqual =
parentSettlementData !== undefined &&
isEqual(settlementData, parentSettlementData);
const isParentTerminationDataEqual =
parentTerminationData !== undefined &&
isEqual(terminationData, parentTerminationData);
const getSigners = (data: DataSourceDefinition) => { const getSigners = (data: DataSourceDefinition) => {
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') { if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
@@ -97,12 +112,22 @@ export const ProposalMarketData = ({
<AccordionItem <AccordionItem
itemId="key-details" itemId="key-details"
title={t('Key details')} title={t('Key details')}
content={<KeyDetailsInfoPanel market={marketData} />} content={
<KeyDetailsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
<AccordionItem <AccordionItem
itemId="instrument" itemId="instrument"
title={t('Instrument')} title={t('Instrument')}
content={<InstrumentInfoPanel market={marketData} />} content={
<InstrumentInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
{isEqual( {isEqual(
getSigners(settlementData), getSigners(settlementData),
@@ -115,6 +140,11 @@ export const ProposalMarketData = ({
<OracleInfoPanel <OracleInfoPanel
market={marketData} market={marketData}
type="settlementData" type="settlementData"
parentMarket={
isParentSettlementDataEqual
? undefined
: parentMarketData
}
/> />
} }
/> />
@@ -127,6 +157,11 @@ export const ProposalMarketData = ({
<OracleInfoPanel <OracleInfoPanel
market={marketData} market={marketData}
type="settlementData" type="settlementData"
parentMarket={
isParentSettlementDataEqual
? undefined
: parentMarketData
}
/> />
} }
/> />
@@ -135,11 +170,21 @@ export const ProposalMarketData = ({
itemId="termination-oracle" itemId="termination-oracle"
title={t('Termination Oracle')} title={t('Termination Oracle')}
content={ content={
<OracleInfoPanel market={marketData} type="termination" /> <OracleInfoPanel
market={marketData}
type="termination"
parentMarket={
isParentTerminationDataEqual
? undefined
: parentMarketData
}
/>
} }
/> />
</> </>
)} )}
{/*Note: successor markets will not differ in their settlement*/}
{/*assets, so no need to pass in parent market data for comparison.*/}
<AccordionItem <AccordionItem
itemId="settlement-asset" itemId="settlement-asset"
title={t('Settlement asset')} title={t('Settlement asset')}
@@ -148,22 +193,42 @@ export const ProposalMarketData = ({
<AccordionItem <AccordionItem
itemId="metadata" itemId="metadata"
title={t('Metadata')} title={t('Metadata')}
content={<MetadataInfoPanel market={marketData} />} content={
<MetadataInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
<AccordionItem <AccordionItem
itemId="risk-model" itemId="risk-model"
title={t('Risk model')} title={t('Risk model')}
content={<RiskModelInfoPanel market={marketData} />} content={
<RiskModelInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
<AccordionItem <AccordionItem
itemId="risk-parameters" itemId="risk-parameters"
title={t('Risk parameters')} title={t('Risk parameters')}
content={<RiskParametersInfoPanel market={marketData} />} content={
<RiskParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
<AccordionItem <AccordionItem
itemId="risk-factors" itemId="risk-factors"
title={t('Risk factors')} title={t('Risk factors')}
content={<RiskFactorsInfoPanel market={marketData} />} content={
<RiskFactorsInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
{( {(
marketData.priceMonitoringSettings?.parameters?.triggers || [] marketData.priceMonitoringSettings?.parameters?.triggers || []
@@ -174,6 +239,7 @@ export const ProposalMarketData = ({
content={ content={
<PriceMonitoringBoundsInfoPanel <PriceMonitoringBoundsInfoPanel
market={marketData} market={marketData}
parentMarket={parentMarketData}
triggerIndex={triggerIndex} triggerIndex={triggerIndex}
/> />
} }
@@ -183,13 +249,21 @@ export const ProposalMarketData = ({
itemId="liqudity-monitoring-parameters" itemId="liqudity-monitoring-parameters"
title={t('Liquidity monitoring parameters')} title={t('Liquidity monitoring parameters')}
content={ content={
<LiquidityMonitoringParametersInfoPanel market={marketData} /> <LiquidityMonitoringParametersInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
} }
/> />
<AccordionItem <AccordionItem
itemId="liquidity-price-range" itemId="liquidity-price-range"
title={t('Liquidity price range')} title={t('Liquidity price range')}
content={<LiquidityPriceRangeInfoPanel market={marketData} />} content={
<LiquidityPriceRangeInfoPanel
market={marketData}
parentMarket={parentMarketData}
/>
}
/> />
</Accordion> </Accordion>
</div> </div>
@@ -200,7 +274,7 @@ export const ProposalMarketData = ({
title={marketData.tradableInstrument.instrument.code} title={marketData.tradableInstrument.instrument.code}
open={isOpen} open={isOpen}
onChange={(isOpen) => (isOpen ? open() : close())} onChange={(isOpen) => (isOpen ? open() : close())}
size="medium" size="large"
dataTestId="market-json-dialog" dataTestId="market-json-dialog"
> >
<CopyWithTooltip text={JSON.stringify(marketData)}> <CopyWithTooltip text={JSON.stringify(marketData)}>
@@ -32,6 +32,7 @@ export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal']; proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
networkParams: Partial<NetworkParamsResult>; networkParams: Partial<NetworkParamsResult>;
newMarketData?: MarketInfoWithData | null; newMarketData?: MarketInfoWithData | null;
parentMarketData?: MarketInfoWithData | null;
assetData?: AssetQuery | null; assetData?: AssetQuery | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any; restData: any;
@@ -46,6 +47,7 @@ export const Proposal = ({
networkParams, networkParams,
restData, restData,
newMarketData, newMarketData,
parentMarketData,
assetData, assetData,
originalMarketProposalRestData, originalMarketProposalRestData,
mostRecentlyEnactedAssociatedMarketProposal, mostRecentlyEnactedAssociatedMarketProposal,
@@ -157,7 +159,10 @@ export const Proposal = ({
{newMarketData && ( {newMarketData && (
<div className="mb-4"> <div className="mb-4">
<ProposalMarketData marketData={newMarketData} /> <ProposalMarketData
marketData={newMarketData}
parentMarketData={parentMarketData ? parentMarketData : undefined}
/>
</div> </div>
)} )}
@@ -14,6 +14,9 @@ import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
} from '@vegaprotocol/network-parameters'; } from '@vegaprotocol/network-parameters';
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
import { FLAGS } from '@vegaprotocol/environment';
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
export const ProposalContainer = () => { export const ProposalContainer = () => {
const [ const [
@@ -54,6 +57,10 @@ export const ProposalContainer = () => {
skip: !params.proposalId, skip: !params.proposalId,
}); });
const successor = useSuccessorMarketProposalDetails(params.proposalId);
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
const { const {
state: { state: {
data: originalMarketProposalRestData, data: originalMarketProposalRestData,
@@ -96,6 +103,36 @@ export const ProposalContainer = () => {
}, },
}); });
const {
data: parentMarketId,
loading: parentMarketIdLoading,
error: parentMarketIdError,
} = useParentMarketIdQuery({
variables: {
marketId: newMarketData?.data?.market?.id || '',
},
skip:
!FLAGS.SUCCESSOR_MARKETS ||
!isSuccessor ||
!newMarketData?.data?.market?.id,
});
const {
data: parentMarketData,
loading: parentMarketLoading,
error: parentMarketError,
} = useDataProvider({
dataProvider: marketInfoWithDataProvider,
skipUpdates: true,
variables: {
marketId: parentMarketId?.market?.parentMarketID || '',
skip:
!FLAGS.SUCCESSOR_MARKETS ||
!isSuccessor ||
!parentMarketId?.market?.parentMarketID,
},
});
const { const {
data: assetData, data: assetData,
loading: assetLoading, loading: assetLoading,
@@ -160,6 +197,8 @@ export const ProposalContainer = () => {
newMarketLoading || newMarketLoading ||
assetLoading || assetLoading ||
networkParamsLoading || networkParamsLoading ||
parentMarketIdLoading ||
parentMarketLoading ||
(restLoading ? (restLoading as boolean) : false) || (restLoading ? (restLoading as boolean) : false) ||
(originalMarketProposalRestLoading (originalMarketProposalRestLoading
? (originalMarketProposalRestLoading as boolean) ? (originalMarketProposalRestLoading as boolean)
@@ -172,15 +211,18 @@ export const ProposalContainer = () => {
error || error ||
newMarketError || newMarketError ||
assetError || assetError ||
networkParamsError ||
parentMarketIdError ||
parentMarketError ||
restError || restError ||
originalMarketProposalRestError || originalMarketProposalRestError ||
previouslyEnactedMarketProposalsRestError || previouslyEnactedMarketProposalsRestError
networkParamsError
} }
data={{ data={{
...data, ...data,
...networkParams, ...networkParams,
...(newMarketData ? { newMarketData } : {}), ...(newMarketData ? { newMarketData } : {}),
...(parentMarketData ? { parentMarketData } : {}),
...(assetData ? { assetData } : {}), ...(assetData ? { assetData } : {}),
...(restData ? { restData } : {}), ...(restData ? { restData } : {}),
...(originalMarketProposalRestData ...(originalMarketProposalRestData
@@ -197,6 +239,7 @@ export const ProposalContainer = () => {
networkParams={networkParams} networkParams={networkParams}
restData={restData} restData={restData}
newMarketData={newMarketData} newMarketData={newMarketData}
parentMarketData={parentMarketData}
assetData={assetData} assetData={assetData}
originalMarketProposalRestData={originalMarketProposalRestData} originalMarketProposalRestData={originalMarketProposalRestData}
mostRecentlyEnactedAssociatedMarketProposal={ mostRecentlyEnactedAssociatedMarketProposal={
@@ -57,7 +57,7 @@ const ROWS = [
export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => { export const HealthDialog = ({ onChange, isOpen }: HealthDialogProps) => {
return ( 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"> <h1 className="text-2xl mb-5 pr-2 font-medium font-alpha uppercase">
{t('Health')} {t('Health')}
</h1> </h1>
@@ -2,14 +2,14 @@ describe('charts', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@Markets'); cy.wait('@Markets');
cy.getByTestId('Depth').click();
}); });
it('can see market depth chart', () => { it('can see market depth chart', () => {
// 6006-DEPC-001 // 6006-DEPC-001
cy.getByTestId('Depth').click();
cy.getByTestId('tab-depth').should('be.visible'); 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(); const specDataConnection = createDataConnection();
before(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockGQL((req) => { cy.mockGQL((req) => {
aliasGQLQuery(req, 'ChainId', chainIdQuery()); aliasGQLQuery(req, 'ChainId', chainIdQuery());
aliasGQLQuery(req, 'Statistics', statisticsQuery()); aliasGQLQuery(req, 'Statistics', statisticsQuery());
@@ -455,6 +456,7 @@ describe('no closed markets', { tags: '@smoke', testIsolation: true }, () => {
before(() => { before(() => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/all'); cy.visit('/#/markets/all');
cy.get('[data-testid="Closed markets"]').click(); cy.get('[data-testid="Closed markets"]').click();
}); });
@@ -104,11 +104,11 @@ describe('deposit actions', { tags: '@smoke' }, () => {
cy.visit('/#/markets/market-1'); cy.visit('/#/markets/market-1');
}); });
it('Deposit to trade is visble', () => { it('Deposit to trade is visible', () => {
cy.getByTestId('Collateral').click(); cy.getByTestId('Collateral').click();
cy.contains('[data-testid="deposit"]', 'Deposit') cy.get('[row-id="asset-id"]').contains('tEURO').should('be.visible');
.should('be.visible') cy.contains('[data-testid="deposit"]', 'Deposit').should('be.visible');
.click(); cy.contains('[data-testid="deposit"]', 'Deposit').click();
cy.getByTestId('deposit-submit').should('be.visible'); cy.getByTestId('deposit-submit').should('be.visible');
}); });
}); });
@@ -4,6 +4,7 @@ const nodeHealthTrigger = 'node-health-trigger';
describe('home', { tags: '@regression' }, () => { describe('home', { tags: '@regression' }, () => {
before(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.visit('/'); cy.visit('/');
+38 -37
View File
@@ -2,7 +2,7 @@ import { aliasGQLQuery } from '@vegaprotocol/cypress';
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals'; import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
const dialogContent = 'dialog-content'; const dialogContent = 'welcome-dialog';
const generateProposal = (code: string): ProposalListFieldsFragment => ({ const generateProposal = (code: string): ProposalListFieldsFragment => ({
__typename: 'Proposal', __typename: 'Proposal',
@@ -140,43 +140,43 @@ describe('home', { tags: '@regression' }, () => {
cy.wait('@MarketsData'); 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.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
cy.getByTestId('welcome-notice-title').should( cy.getByTestId('welcome-dialog').should('be.visible');
'contain.text', cy.getByTestId('welcome-title').should('contain.text', 'Console CUSTOM');
'Welcome to Console' 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`);
describe('no proposal nor markets found', () => { cy.window().then((window) => {
it('there are welcome text and a link to propose market', () => { expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
cy.mockGQL((req) => { 'true'
const data = { );
marketsConnection: {
__typename: 'MarketConnection',
edges: [],
},
};
aliasGQLQuery(req, 'Markets', data);
aliasGQLQuery(req, 'MarketsData', data);
aliasGQLQuery(req, 'ProposalsList', {
proposalsConnection: {
__typename: 'ProposalsConnection',
edges: null,
},
});
}); });
cy.visit('/'); });
cy.wait('@Markets');
cy.wait('@MarketsData'); it('click browse markets button should redirect to market/all', () => {
cy.getByTestId('welcome-notice-title').should( cy.getByTestId('welcome-dialog').should('be.visible');
'contain.text', cy.getByTestId('browse-markets-button').click();
'Welcome to Console' cy.url().should('eq', Cypress.config().baseUrl + `/#/markets/all`);
); cy.window().then((window) => {
cy.getByTestId('external-link') expect(window.localStorage.getItem('vega_onboarding_viewed')).to.equal(
.contains('Propose a market') 'true'
.should('exist'); );
});
});
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) => { cy.window().then((window) => {
window.localStorage.setItem('marketId', 'market-1'); window.localStorage.setItem('marketId', 'market-1');
cy.visit('/'); cy.visit('/');
cy.wait('@Markets'); cy.getByTestId('dialog-close').click();
cy.location('hash').should('equal', '#/markets/market-1'); cy.location('hash').should('equal', '#/markets/market-1');
cy.getByTestId(dialogContent).should('not.exist'); cy.getByTestId(dialogContent).should('not.exist');
}); });
@@ -199,6 +199,7 @@ describe('home', { tags: '@regression' }, () => {
}); });
cy.visit('/'); cy.visit('/');
cy.wait('@Markets'); cy.wait('@Markets');
cy.getByTestId('dialog-close').click();
cy.location('hash').should('equal', '#/markets/market-not-existing'); cy.location('hash').should('equal', '#/markets/market-not-existing');
cy.getByTestId(dialogContent).should('not.exist'); cy.getByTestId(dialogContent).should('not.exist');
}); });
@@ -3,12 +3,13 @@ import type { MarketsQuery } from '@vegaprotocol/markets';
import * as Schema from '@vegaprotocol/types'; import * as Schema from '@vegaprotocol/types';
const rowSelector = 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"]'; const colInstrumentCode = '[col-id="tradableInstrument.instrument.code"]';
describe('markets all table', { tags: '@smoke' }, () => { describe('markets all table', { tags: '@smoke' }, () => {
beforeEach(() => { beforeEach(() => {
cy.clearLocalStorage().then(() => { cy.clearLocalStorage().then(() => {
cy.setOnBoardingViewed();
cy.mockTradingPage( cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE, Schema.MarketState.STATE_ACTIVE,
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION, Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
@@ -34,7 +35,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
'Settlement asset', 'Settlement asset',
'', '',
]; ];
cy.getByTestId('tab-all-markets').within(($headers) => { cy.getByTestId('tab-open-markets').within(($headers) => {
cy.wrap($headers) cy.wrap($headers)
.get('.ag-header-cell-text') .get('.ag-header-cell-text')
.each(($header, i) => { .each(($header, i) => {
@@ -44,7 +45,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
}); });
it('markets tab should be rendered properly', () => { it('markets tab should be rendered properly', () => {
cy.get('[data-testid="All markets"]').should( cy.get('[data-testid="Open markets"]').should(
'have.attr', 'have.attr',
'data-state', 'data-state',
'active' 'active'
@@ -166,7 +167,7 @@ describe('markets all table', { tags: '@smoke' }, () => {
'ETHBTC.QM21', 'ETHBTC.QM21',
'SOLUSD', '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.url().should('eq', Cypress.config('baseUrl') + '/#/markets/all');
cy.contains('AAPL.MF21').should('be.visible'); cy.contains('AAPL.MF21').should('be.visible');
cy.get('.ag-header-cell-label').contains('Market').click(); // sort by market name 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(() => { before(() => {
cy.mockTradingPage(); cy.mockTradingPage();
const markets: MarketsQuery = {}; const markets: MarketsQuery = {};
@@ -199,11 +200,12 @@ describe('no all markets', { tags: '@smoke', testIsolation: true }, () => {
aliasGQLQuery(req, 'Markets', markets); aliasGQLQuery(req, 'Markets', markets);
}); });
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/all'); cy.visit('/#/markets/all');
}); });
it.skip('can see no markets message', () => { it.skip('can see no markets message', () => {
// 6001-MARK-048 // 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(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockTradingPage(MarketState.STATE_ACTIVE); cy.mockTradingPage(MarketState.STATE_ACTIVE);
cy.mockSubscription(); cy.mockSubscription();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
@@ -36,6 +36,7 @@ const headers = [
describe('liquidity table - trading', { tags: '@smoke' }, () => { describe('liquidity table - trading', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockSubscription(); cy.mockSubscription();
cy.mockTradingPage( cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE, Schema.MarketState.STATE_ACTIVE,
@@ -119,6 +120,7 @@ describe('liquidity table - trading', { tags: '@smoke' }, () => {
describe('liquidity table view', { tags: '@smoke' }, () => { describe('liquidity table view', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockSubscription(); cy.mockSubscription();
cy.mockTradingPage( cy.mockTradingPage(
Schema.MarketState.STATE_ACTIVE, Schema.MarketState.STATE_ACTIVE,
@@ -12,6 +12,7 @@ describe('markets selector', { tags: '@smoke' }, () => {
cy.window().then((window) => { cy.window().then((window) => {
window.localStorage.setItem('marketId', 'market-1'); window.localStorage.setItem('marketId', 'market-1');
}); });
cy.setOnBoardingViewed();
cy.mockTradingPage( cy.mockTradingPage(
MarketState.STATE_ACTIVE, MarketState.STATE_ACTIVE,
MarketTradingMode.TRADING_MODE_MONITORING_AUCTION, MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
@@ -30,6 +30,7 @@ describe('Market trading page', () => {
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
); );
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@MarketData'); cy.wait('@MarketData');
cy.getByTestId(marketSummaryBlock).should('be.visible'); cy.getByTestId(marketSummaryBlock).should('be.visible');
@@ -9,6 +9,7 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/all'); cy.visit('/#/markets/all');
cy.get('[data-testid="Proposed markets"]').click(); cy.get('[data-testid="Proposed markets"]').click();
}); });
@@ -100,7 +101,6 @@ describe('markets proposed table', { tags: '@smoke' }, () => {
'VEGA_TOKEN_URL' 'VEGA_TOKEN_URL'
)}/proposals/e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829` )}/proposals/e9ec6d5c46a7e7bcabf9ba7a893fa5a5eeeec08b731f06f7a6eb7bf0e605b829`
); );
cy.getByTestId('proposal-actions-content').click();
}); });
// 6001-MARK-060 // 6001-MARK-060
@@ -214,11 +214,13 @@ describe('no markets proposed', { tags: '@smoke', testIsolation: true }, () => {
aliasGQLQuery(req, 'ProposalsList', proposal); aliasGQLQuery(req, 'ProposalsList', proposal);
}); });
cy.mockSubscription(); cy.mockSubscription();
cy.visit('/#/markets/all'); cy.setOnBoardingViewed();
cy.get('[data-testid="Proposed markets"]').click();
}); });
it('can see no markets message', () => { it('can see no markets message', () => {
cy.visit('/#/markets/all');
cy.get('[data-testid="Proposed markets"]').click();
// 6001-MARK-061 // 6001-MARK-061
cy.getByTestId('tab-proposed-markets').should('contain.text', 'No markets'); 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 Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
); );
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/all'); cy.visit('/#/markets/all');
}); });
}); });
@@ -6,6 +6,7 @@ const oracleFullProfile = 'oracle-full-profile';
describe('oracle information', { tags: '@smoke' }, () => { describe('oracle information', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockTradingPage( cy.mockTradingPage(
MarketState.STATE_ACTIVE, MarketState.STATE_ACTIVE,
undefined, undefined,
@@ -14,6 +14,7 @@ const resPrice = 'price-990';
describe('order book', { tags: '@smoke' }, () => { describe('order book', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.setOnBoardingViewed();
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
@@ -4,6 +4,7 @@ describe('Settings page', { tags: '@smoke' }, () => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/'); cy.visit('/');
// Only click if not already active otherwise sidebar will close // Only click if not already active otherwise sidebar will close
@@ -77,6 +77,7 @@ function getButtonSelectorByText(text: string): string {
beforeEach(() => { beforeEach(() => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@Markets'); cy.wait('@Markets');
}); });
@@ -154,7 +155,10 @@ describe(
{ name: 'Volume', infoText: 'Volume: 55,000' }, { name: 'Volume', infoText: 'Volume: 55,000' },
]; ];
cy.get(indicatorInfo).eq(1).realHover(); 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); cy.get(indicatorInfo).should('have.length', 1);
checkMenuItemCheckbox('Studies', studyInfo); checkMenuItemCheckbox('Studies', studyInfo);
@@ -14,6 +14,7 @@ describe('deal ticket basics', { tags: '@smoke' }, () => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.clearAllLocalStorage(); cy.clearAllLocalStorage();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@Markets'); 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', () => { it('must show place order button and connect wallet if wallet is not connected', () => {
// 0003-WTXN-001 // 0003-WTXN-001
cy.getByTestId('connect-vega-wallet'); // Not connected cy.getByTestId('connect-vega-wallet'); // Not connected
cy.getByTestId('order-connect-wallet').should('exist');
cy.getByTestId(placeOrderBtn).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 () { it('must be able to select order direction - long/short', function () {
// 7002-SORD-004 // 7002-SORD-004
cy.getByTestId(toggleShort).click().children('input').should('be.checked'); cy.getByTestId(toggleShort).click().next('input').should('be.checked');
cy.getByTestId(toggleLong).click().children('input').should('be.checked'); cy.getByTestId(toggleLong).click().next('input').should('be.checked');
}); });
it('must be able to select order type - limit/market', function () { it('must be able to select order type - limit/market', function () {
// 7002-SORD-005 // 7002-SORD-005
// 7002-SORD-006 // 7002-SORD-006
// 7002-SORD-007 // 7002-SORD-007
cy.getByTestId(toggleLimit).click().children('input').should('be.checked'); cy.getByTestId(toggleLimit).click().next('input').should('be.checked');
cy.getByTestId(toggleMarket).click().children('input').should('be.checked'); cy.getByTestId(toggleMarket).click().next('input').should('be.checked');
}); });
it('order connect vega wallet button should connect', () => { it('order connect vega wallet button should connect', () => {
mockConnectWallet(); mockConnectWallet();
cy.getByTestId(toggleLimit).click(); cy.getByTestId(toggleLimit).click();
cy.getByTestId(orderPriceField).clear().type('101'); 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('dialog-content').should('be.visible');
cy.getByTestId('connectors-list') cy.getByTestId('connectors-list')
.find('[data-testid="connector-jsonRpc"]') .find('[data-testid="connector-jsonRpc"]')
.click(); .click();
cy.wait('@walletReq'); cy.wait('@walletReq');
cy.getByTestId(placeOrderBtn).should('be.visible'); 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'); cy.getByTestId(orderPriceField).should('have.value', '101');
}); });
}); });
@@ -63,6 +63,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTC'); cy.getByTestId(orderTIFDropDown).select('TIME_IN_FORCE_GTC');
cy.getByTestId(orderSizeField).clear().type('1'); cy.getByTestId(orderSizeField).clear().type('1');
cy.getByTestId(orderPriceField).clear().type('1.123456'); cy.getByTestId(orderPriceField).clear().type('1.123456');
cy.getByTestId(placeOrderBtn).click();
cy.getByTestId('deal-ticket-error-message-price-limit').should( cy.getByTestId('deal-ticket-error-message-price-limit').should(
'have.text', 'have.text',
'Price accepts up to 5 decimal places' 'Price accepts up to 5 decimal places'
@@ -73,6 +74,7 @@ describe('deal ticker order validation', { tags: '@smoke' }, () => {
describe('market order', () => { describe('market order', () => {
before(() => { before(() => {
cy.getByTestId(toggleMarket).click(); cy.getByTestId(toggleMarket).click();
cy.getByTestId(placeOrderBtn).click();
}); });
it('must not see the price unit', function () { it('must not see the price unit', function () {
@@ -48,6 +48,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
cy.getByTestId(orderPriceField).clear().type('0.1'); cy.getByTestId(orderPriceField).clear().type('0.1');
cy.getByTestId(orderSizeField).clear().type('1'); cy.getByTestId(orderSizeField).clear().type('1');
cy.getByTestId(placeOrderBtn).should('be.enabled'); cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId(placeOrderBtn).click();
cy.getByTestId('deal-ticket-warning-auction').should( cy.getByTestId('deal-ticket-warning-auction').should(
'have.text', 'have.text',
'Any orders placed now will not trade until the auction ends' 'Any orders placed now will not trade until the auction ends'
@@ -60,6 +61,7 @@ describe('suspended market validation', { tags: '@regression' }, () => {
TIFlist.filter((item) => item.code === 'FOK')[0].value TIFlist.filter((item) => item.code === 'FOK')[0].value
); );
cy.getByTestId(placeOrderBtn).should('be.enabled'); cy.getByTestId(placeOrderBtn).should('be.enabled');
cy.getByTestId(placeOrderBtn).click();
cy.getByTestId('deal-ticket-error-message-tif').should( cy.getByTestId('deal-ticket-error-message-tif').should(
'have.text', 'have.text',
'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders' 'This market is in auction until it reaches sufficient liquidity. Until the auction ends, you can only place GFA, GTT, or GTC limit orders'
@@ -15,6 +15,7 @@ describe('time in force validation', { tags: '@smoke' }, () => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.clearAllLocalStorage(); cy.clearAllLocalStorage();
cy.setOnBoardingViewed();
cy.visit('/#/markets/market-0'); cy.visit('/#/markets/market-0');
cy.wait('@Markets'); cy.wait('@Markets');
}); });
@@ -53,7 +53,7 @@ describe('fills', { tags: '@regression' }, () => {
cy.getByTestId(tabFills).contains('Market'); cy.getByTestId(tabFills).contains('Market');
cy.getByTestId(tabFills) cy.getByTestId(tabFills)
.get( .get(
'[role="gridcell"][col-id="market.tradableInstrument.instrument.name"]' '[role="gridcell"][col-id="market.tradableInstrument.instrument.code"]'
) )
.each(($marketSymbol) => { .each(($marketSymbol) => {
cy.wrap($marketSymbol).invoke('text').should('not.be.empty'); cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
@@ -142,22 +142,22 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
it('sorting by Market', () => { it('sorting by Market', () => {
visitAndClickPositions(); visitAndClickPositions();
const marketsSortedDefault = [ const marketsSortedDefault = [
'ACTIVE MARKET', 'AAPL.MF21',
'Apple Monthly (30 Jun 2022)', 'BTCUSD.MF21',
'ETHBTC Quarterly (30 Jun 2022)', 'ETHBTC.QM21',
'SUSPENDED MARKET', 'SOLUSD',
]; ];
const marketsSortedAsc = [ const marketsSortedAsc = [
'ACTIVE MARKET', 'AAPL.MF21',
'Apple Monthly (30 Jun 2022)', 'BTCUSD.MF21',
'ETHBTC Quarterly (30 Jun 2022)', 'ETHBTC.QM21',
'SUSPENDED MARKET', 'SOLUSD',
]; ];
const marketsSortedDesc = [ const marketsSortedDesc = [
'SUSPENDED MARKET', 'SOLUSD',
'ETHBTC Quarterly (30 Jun 2022)', 'ETHBTC.QM21',
'Apple Monthly (30 Jun 2022)', 'BTCUSD.MF21',
'ACTIVE MARKET', 'AAPL.MF21',
]; ];
cy.getByTestId(positions).click(); cy.getByTestId(positions).click();
// 7004-POSI-003 // 7004-POSI-003
@@ -251,11 +251,13 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
it('I can see warnings', () => { it('I can see warnings', () => {
visitAndClickPositions(); visitAndClickPositions();
cy.get('[col-id="openVolume"]').within(() => { cy.get('[col-id="openVolume"]')
cy.get('[aria-label="warning-sign icon"]') .eq(3)
.should('be.visible') .within(() => {
.realHover(); cy.get('[aria-label="warning-sign icon"]')
}); .should('be.visible')
.realHover();
});
// 7004-POSI-011 // 7004-POSI-011
cy.getByTestId(tooltipContent).should('be.visible'); cy.getByTestId(tooltipContent).should('be.visible');
}); });
@@ -289,9 +291,11 @@ describe('positions', { tags: '@regression', testIsolation: true }, () => {
it('View settlement asset', () => { it('View settlement asset', () => {
visitAndClickPositions(); visitAndClickPositions();
cy.get('[col-id="asset"]').within(() => { cy.get('[col-id="asset"]')
cy.get('button[type="button"]').first().click(); .eq(3)
}); .within(() => {
cy.get('button[type="button"]').click();
});
// 7004-POSI-008 // 7004-POSI-008
cy.getByTestId(dialogContent).should('be.visible'); cy.getByTestId(dialogContent).should('be.visible');
cy.getByTestId(dialogCloseX).click(); cy.getByTestId(dialogCloseX).click();
@@ -306,7 +310,7 @@ function validatePositionsDisplayed(multiKey = false) {
cy.getByTestId('tab-positions').should('be.visible'); cy.getByTestId('tab-positions').should('be.visible');
cy.getByTestId('tab-positions') cy.getByTestId('tab-positions')
.get('.ag-center-cols-container .ag-row') .get('.ag-center-cols-container .ag-row')
.first() .eq(multiKey ? 3 : 1)
.within(() => { .within(() => {
cy.get('[col-id="marketName"]') cy.get('[col-id="marketName"]')
.should('be.visible') .should('be.visible')
@@ -9,6 +9,7 @@ describe('trades', { tags: '@smoke' }, () => {
before(() => { before(() => {
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.intercept('POST', '/graphql', (req) => { cy.intercept('POST', '/graphql', (req) => {
if (req.body.operationName === 'Trades') { if (req.body.operationName === 'Trades') {
req.alias = '@Trades'; req.alias = '@Trades';
@@ -17,6 +17,7 @@ describe(
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('[data-testid="pathname-/portfolio"]').should('exist');
}); });
@@ -122,6 +123,7 @@ describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
cy.visit('/#/portfolio'); cy.visit('/#/portfolio');
cy.mockTradingPage(); cy.mockTradingPage();
cy.mockSubscription(); cy.mockSubscription();
cy.setOnBoardingViewed();
cy.get('[data-testid="pathname-/portfolio"]').should('exist'); cy.get('[data-testid="pathname-/portfolio"]').should('exist');
}); });
@@ -0,0 +1,119 @@
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.skip(
'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 assetSelection = 'select-asset';
const assetBalance = 'asset-balance'; const assetBalance = 'asset-balance';
const assetOption = 'rich-select-option'; 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 openTransferButton = 'open-transfer';
const submitTransferBtn = '[type="submit"]'; const submitTransferBtn = '[type="submit"]';
const toAddressField = '[name="toAddress"]'; const toAddressField = '[name="toAddress"]';
const totalTransferfee = 'total-transfer-fee';
const transferAmount = 'transfer-amount';
const transferForm = 'transfer-form'; const transferForm = 'transfer-form';
const transferFee = 'transfer-fee';
const walletTransfer = 'wallet-transfer';
const ASSET_EURO = 1;
const ASSET_SEPOLIA_TBTC = 2; const ASSET_SEPOLIA_TBTC = 2;
const collateralTab = 'Collateral'; const collateralTab = 'Collateral';
const toastCloseBtn = 'toast-close'; const toastCloseBtn = 'toast-close';
const toastContent = 'toast-content'; 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 }, () => { describe('withdraw actions', { tags: '@smoke', testIsolation: true }, () => {
beforeEach(() => { beforeEach(() => {
cy.mockWeb3Provider(); cy.mockWeb3Provider();
+4 -1
View File
@@ -1,3 +1,4 @@
import { OrderType } from '@vegaprotocol/types';
import type { OrderSubmission } from '@vegaprotocol/wallet'; import type { OrderSubmission } from '@vegaprotocol/wallet';
const orderSizeField = 'order-size'; const orderSizeField = 'order-size';
@@ -9,7 +10,9 @@ export const createOrder = (order: OrderSubmission): void => {
cy.log('Placing order', order); cy.log('Placing order', order);
const { type, side, size, price, timeInForce, expiresAt } = 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(`order-side-${side}`).click();
cy.getByTestId(orderSizeField).clear().type(size); cy.getByTestId(orderSizeField).clear().type(size);
if (price) { if (price) {
+2 -2
View File
@@ -6,8 +6,8 @@ export const orderTIFDropDown = 'order-tif';
export const placeOrderBtn = 'place-order'; export const placeOrderBtn = 'place-order';
export const toggleShort = 'order-side-SIDE_SELL'; export const toggleShort = 'order-side-SIDE_SELL';
export const toggleLong = 'order-side-SIDE_BUY'; export const toggleLong = 'order-side-SIDE_BUY';
export const toggleLimit = 'order-type-TYPE_LIMIT'; export const toggleLimit = 'order-type-Limit';
export const toggleMarket = 'order-type-TYPE_MARKET'; export const toggleMarket = 'order-type-Market';
export const TIFlist = Object.values(Schema.OrderTimeInForce).map((value) => { export const TIFlist = Object.values(Schema.OrderTimeInForce).map((value) => {
return { return {
+1 -1
View File
@@ -16,6 +16,6 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
# NX_STOP_ORDERS NX_STOP_ORDERS=true
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+1 -1
View File
@@ -18,6 +18,6 @@ NX_ETH_WALLET_MNEMONIC="ozone access unlock valid olympic save include omit supp
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false NX_SUCCESSOR_MARKETS=false
# NX_STOP_ORDERS NX_STOP_ORDERS=false
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+1 -1
View File
@@ -16,6 +16,6 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
# NX_STOP_ORDERS NX_STOP_ORDERS=true
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+2 -2
View File
@@ -18,6 +18,6 @@ NX_APP_VERSION=v0.20.21-core-0.71.6
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false NX_SUCCESSOR_MARKETS=false
# NX_STOP_ORDERS NX_STOP_ORDERS=false
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+1 -1
View File
@@ -18,6 +18,6 @@ NX_APP_VERSION=v0.20.19-core-0.71.6
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false NX_SUCCESSOR_MARKETS=false
# NX_STOP_ORDERS NX_STOP_ORDERS=false
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+1 -1
View File
@@ -16,6 +16,6 @@ NX_VEGA_INCIDENT_URL=https://blog.vega.xyz/tagged/vega-incident-reports
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
# NX_STOP_ORDERS NX_STOP_ORDERS=true
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+2 -2
View File
@@ -17,6 +17,6 @@ NX_VEGA_CONSOLE_URL=https://console.fairground.wtf
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true NX_SUCCESSOR_MARKETS=true
# NX_STOP_ORDERS NX_STOP_ORDERS=true
# NX_ICEBERG_ORDERS NX_ICEBERG_ORDERS=true
# NX_PRODUCT_PERPETUALS # NX_PRODUCT_PERPETUALS
+1 -1
View File
@@ -17,6 +17,6 @@ NX_VEGA_CONSOLE_URL=https://trading.validators-testnet.vega.rocks
# Cosmic elevator flags # Cosmic elevator flags
NX_SUCCESSOR_MARKETS=false NX_SUCCESSOR_MARKETS=false
# NX_STOP_ORDERS NX_STOP_ORDERS=false
# NX_ICEBERG_ORDERS # NX_ICEBERG_ORDERS
# NX_PRODUCT_PERPETUALS # 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;
-1
View File
@@ -23,7 +23,6 @@ export const Home = () => {
replace: true, replace: true,
}); });
} else if (data) { } else if (data) {
update({ shouldDisplayWelcomeDialog: true });
const marketDataId = data[0]?.id; const marketDataId = data[0]?.id;
if (marketDataId) { if (marketDataId) {
navigate(Links[Routes.MARKET](marketDataId), { navigate(Links[Routes.MARKET](marketDataId), {
+44 -50
View File
@@ -10,7 +10,6 @@ import type { Market } from '@vegaprotocol/markets';
import { Filter } from '@vegaprotocol/orders'; import { Filter } from '@vegaprotocol/orders';
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit'; import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler'; import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { import {
ResizableGrid, ResizableGrid,
ResizableGridPanel, ResizableGridPanel,
@@ -45,20 +44,24 @@ const MainGrid = memo(
return ( return (
<ResizableGrid vertical onChange={handleOnLayoutChange}> <ResizableGrid vertical onChange={handleOnLayoutChange}>
<ResizableGridPanel minSize={75} priority={LayoutPriority.High}> <ResizableGridPanel
<ResizableGrid preferredSize={sizes[0]}
proportionalLayout={false} priority={LayoutPriority.High}
minSize={200} minSize={200}
onChange={handleOnMiddleLayoutChange} >
> <ResizableGrid onChange={handleOnMiddleLayoutChange}>
<ResizableGridPanel <ResizableGridPanel
priority={LayoutPriority.High} priority={LayoutPriority.High}
minSize={200} minSize={200}
preferredSize={sizesMiddle[1] || '50%'} preferredSize={sizesMiddle[0] || '75%'}
> >
<TradeGridChild> <TradeGridChild>
<Tabs storageKey="console-trade-grid-main-left"> <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} /> <TradingViews.candles.component marketId={marketId} />
</Tab> </Tab>
<Tab id="depth" name={t('Depth')}> <Tab id="depth" name={t('Depth')}>
@@ -71,8 +74,8 @@ const MainGrid = memo(
</TradeGridChild> </TradeGridChild>
</ResizableGridPanel> </ResizableGridPanel>
<ResizableGridPanel <ResizableGridPanel
preferredSize={sizesMiddle[2] || 300}
minSize={200} minSize={200}
preferredSize={sizesMiddle[1] || 300}
> >
<TradeGridChild> <TradeGridChild>
<Tabs storageKey="console-trade-grid-main-right"> <Tabs storageKey="console-trade-grid-main-right">
@@ -88,64 +91,55 @@ const MainGrid = memo(
</ResizableGrid> </ResizableGrid>
</ResizableGridPanel> </ResizableGridPanel>
<ResizableGridPanel <ResizableGridPanel
priority={LayoutPriority.Low}
preferredSize={sizes[1] || '25%'} preferredSize={sizes[1] || '25%'}
minSize={50} minSize={50}
priority={LayoutPriority.Low}
> >
<TradeGridChild> <TradeGridChild>
<Tabs storageKey="console-trade-grid-bottom"> <Tabs storageKey="console-trade-grid-bottom">
<Tab id="positions" name={t('Positions')}> <Tab id="positions" name={t('Positions')}>
<VegaWalletContainer> <TradingViews.positions.component
<TradingViews.positions.component onMarketClick={onMarketClick}
onMarketClick={onMarketClick} />
/>
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="open-orders" name={t('Open')}> <Tab id="open-orders" name={t('Open')}>
<VegaWalletContainer> <TradingViews.orders.component
<TradingViews.orders.component marketId={marketId}
marketId={marketId} filter={Filter.Open}
filter={Filter.Open} />
/>
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="closed-orders" name={t('Closed')}> <Tab id="closed-orders" name={t('Closed')}>
<VegaWalletContainer> <TradingViews.orders.component
<TradingViews.orders.component marketId={marketId}
marketId={marketId} filter={Filter.Closed}
filter={Filter.Closed} />
/>
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="rejected-orders" name={t('Rejected')}> <Tab id="rejected-orders" name={t('Rejected')}>
<VegaWalletContainer> <TradingViews.orders.component
<TradingViews.orders.component marketId={marketId}
marketId={marketId} filter={Filter.Rejected}
filter={Filter.Rejected} />
/>
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="orders" name={t('All')}> <Tab id="orders" name={t('All')}>
<VegaWalletContainer> <TradingViews.orders.component marketId={marketId} />
<TradingViews.orders.component marketId={marketId} />
</VegaWalletContainer>
</Tab> </Tab>
{FLAGS.STOP_ORDERS ? (
<Tab id="stop-orders" name={t('Stop orders')}>
<TradingViews.stopOrders.component />
</Tab>
) : null}
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
<VegaWalletContainer> <TradingViews.fills.component
<TradingViews.fills.component marketId={marketId}
marketId={marketId} onMarketClick={onMarketClick}
onMarketClick={onMarketClick} />
/>
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="accounts" name={t('Collateral')}> <Tab id="accounts" name={t('Collateral')}>
<VegaWalletContainer> <TradingViews.collateral.component
<TradingViews.collateral.component pinnedAsset={pinnedAsset}
pinnedAsset={pinnedAsset} onMarketClick={onMarketClick}
onMarketClick={onMarketClick} hideButtons
hideButtons />
/>
</VegaWalletContainer>
</Tab> </Tab>
</Tabs> </Tabs>
</TradeGridChild> </TradeGridChild>
@@ -37,6 +37,7 @@ export const TradePanels = ({
const onOrderTypeClick = useMarketLiquidityClickHandler(); const onOrderTypeClick = useMarketLiquidityClickHandler();
const [view, setView] = useState<TradingView>('candles'); const [view, setView] = useState<TradingView>('candles');
const renderView = () => { const renderView = () => {
const Component = memo<{ const Component = memo<{
marketId: string; marketId: string;
@@ -65,8 +66,23 @@ 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 ( 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> <div>
{FLAGS.SUCCESSOR_MARKETS && ( {FLAGS.SUCCESSOR_MARKETS && (
<> <>
@@ -76,6 +92,7 @@ export const TradePanels = ({
)} )}
<OracleBanner marketId={market?.id || ''} /> <OracleBanner marketId={market?.id || ''} />
</div> </div>
<div>{renderMenu()}</div>
<div className="h-full"> <div className="h-full">
<AutoSizer> <AutoSizer>
{({ width, height }) => ( {({ width, height }) => (
@@ -2,7 +2,10 @@ import type { ComponentProps } from 'react';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { TradesContainer } from '@vegaprotocol/trades'; import { TradesContainer } from '@vegaprotocol/trades';
import { DepthChartContainer } from '@vegaprotocol/market-depth'; 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 { Filter } from '@vegaprotocol/orders';
import { NO_MARKET } from './constants'; import { NO_MARKET } from './constants';
import { OrderbookContainer } from '../../components/orderbook-container'; import { OrderbookContainer } from '../../components/orderbook-container';
@@ -12,6 +15,7 @@ import { AccountsContainer } from '../../components/accounts-container';
import { LiquidityContainer } from '../../components/liquidity-container'; import { LiquidityContainer } from '../../components/liquidity-container';
import type { OrderContainerProps } from '../../components/orders-container'; import type { OrderContainerProps } from '../../components/orders-container';
import { OrdersContainer } from '../../components/orders-container'; import { OrdersContainer } from '../../components/orders-container';
import { StopOrdersContainer } from '../../components/stop-orders-container';
type MarketDependantView = type MarketDependantView =
| typeof CandlesChartContainer | typeof CandlesChartContainer
@@ -34,6 +38,7 @@ export const TradingViews = {
candles: { candles: {
label: 'Candles', label: 'Candles',
component: requiresMarket(CandlesChartContainer), component: requiresMarket(CandlesChartContainer),
menu: CandlesMenu,
}, },
depth: { depth: {
label: 'Depth', label: 'Depth',
@@ -74,6 +79,10 @@ export const TradingViews = {
label: 'All', label: 'All',
component: OrdersContainer, component: OrdersContainer,
}, },
stopOrders: {
label: 'Stop',
component: StopOrdersContainer,
},
collateral: { label: 'Collateral', component: AccountsContainer }, collateral: { label: 'Collateral', component: AccountsContainer },
fills: { label: 'Fills', component: FillsContainer }, 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 pt-0.5 pb-3 px-1.5">
<div className="h-full my-1 border border-default rounded-sm"> <div className="h-full my-1 border border-default rounded-sm">
<Tabs storageKey="console-markets"> <Tabs storageKey="console-markets">
<Tab id="all-markets" name={t('All markets')}> <Tab id="open-markets" name={t('Open markets')}>
<Markets /> <Markets />
</Tab> </Tab>
<Tab id="proposed-markets" name={t('Proposed markets')}> <Tab id="proposed-markets" name={t('Proposed markets')}>
@@ -71,7 +71,7 @@ export const AccountHistoryContainer = () => {
const { data: assets } = useAssetsDataProvider(); const { data: assets } = useAssetsDataProvider();
if (!pubKey) { if (!pubKey) {
return <Splash>{t('Connect wallet')}</Splash>; return <Splash>{t('Please connect Vega wallet')}</Splash>;
} }
return ( return (
@@ -1,15 +1,12 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button, Splash } from '@vegaprotocol/ui-toolkit';
import { DepositsTable } from '@vegaprotocol/deposits'; import { DepositsTable } from '@vegaprotocol/deposits';
import { depositsProvider } from '@vegaprotocol/deposits'; import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useSidebar, ViewType } from '../../components/sidebar'; import { useSidebar, ViewType } from '../../components/sidebar';
export const DepositsContainer = () => { export const DepositsContainer = () => {
const gridRef = useRef<AgGridReact | null>(null);
const { pubKey, isReadOnly } = useVegaWallet(); const { pubKey, isReadOnly } = useVegaWallet();
const { data, error } = useDataProvider({ const { data, error } = useDataProvider({
dataProvider: depositsProvider, dataProvider: depositsProvider,
@@ -17,11 +14,13 @@ export const DepositsContainer = () => {
skip: !pubKey, skip: !pubKey,
}); });
const setView = useSidebar((store) => store.setView); const setView = useSidebar((store) => store.setView);
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return ( return (
<div className="h-full"> <div className="h-full">
<DepositsTable <DepositsTable
rowData={data} rowData={data}
ref={gridRef}
overlayNoRowsTemplate={error ? error.message : t('No deposits')} overlayNoRowsTemplate={error ? error.message : t('No deposits')}
/> />
{!isReadOnly && ( {!isReadOnly && (
@@ -13,7 +13,6 @@ import { FillsContainer } from '../../components/fills-container';
import { PositionsContainer } from '../../components/positions-container'; import { PositionsContainer } from '../../components/positions-container';
import { WithdrawalsContainer } from './withdrawals-container'; import { WithdrawalsContainer } from './withdrawals-container';
import { OrdersContainer } from '../../components/orders-container'; import { OrdersContainer } from '../../components/orders-container';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { LedgerContainer } from '../../components/ledger-container'; import { LedgerContainer } from '../../components/ledger-container';
import { AccountHistoryContainer } from './account-history-container'; import { AccountHistoryContainer } from './account-history-container';
import { import {
@@ -62,29 +61,19 @@ export const Portfolio = () => {
<PortfolioGridChild> <PortfolioGridChild>
<Tabs storageKey="console-portfolio-top"> <Tabs storageKey="console-portfolio-top">
<Tab id="account-history" name={t('Account history')}> <Tab id="account-history" name={t('Account history')}>
<VegaWalletContainer> <AccountHistoryContainer />
<AccountHistoryContainer />
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="positions" name={t('Positions')}> <Tab id="positions" name={t('Positions')}>
<VegaWalletContainer> <PositionsContainer onMarketClick={onMarketClick} allKeys />
<PositionsContainer onMarketClick={onMarketClick} allKeys />
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="orders" name={t('Orders')}> <Tab id="orders" name={t('Orders')}>
<VegaWalletContainer> <OrdersContainer />
<OrdersContainer />
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="fills" name={t('Fills')}> <Tab id="fills" name={t('Fills')}>
<VegaWalletContainer> <FillsContainer onMarketClick={onMarketClick} />
<FillsContainer onMarketClick={onMarketClick} />
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}> <Tab id="ledger-entries" name={t('Ledger entries')}>
<VegaWalletContainer> <LedgerContainer />
<LedgerContainer />
</VegaWalletContainer>
</Tab> </Tab>
</Tabs> </Tabs>
</PortfolioGridChild> </PortfolioGridChild>
@@ -97,14 +86,10 @@ export const Portfolio = () => {
<PortfolioGridChild> <PortfolioGridChild>
<Tabs storageKey="console-portfolio-bottom"> <Tabs storageKey="console-portfolio-bottom">
<Tab id="collateral" name={t('Collateral')}> <Tab id="collateral" name={t('Collateral')}>
<VegaWalletContainer> <AccountsContainer />
<AccountsContainer />
</VegaWalletContainer>
</Tab> </Tab>
<Tab id="deposits" name={t('Deposits')}> <Tab id="deposits" name={t('Deposits')}>
<VegaWalletContainer> <DepositsContainer />
<DepositsContainer />
</VegaWalletContainer>
</Tab> </Tab>
<Tab <Tab
id="withdrawals" id="withdrawals"
@@ -1,4 +1,4 @@
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button, Splash } from '@vegaprotocol/ui-toolkit';
import { import {
withdrawalProvider, withdrawalProvider,
WithdrawalsTable, WithdrawalsTable,
@@ -7,7 +7,6 @@ import {
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useDataProvider } from '@vegaprotocol/data-provider'; import { useDataProvider } from '@vegaprotocol/data-provider';
import { VegaWalletContainer } from '../../components/vega-wallet-container';
import { ViewType, useSidebar } from '../../components/sidebar'; import { ViewType, useSidebar } from '../../components/sidebar';
export const WithdrawalsContainer = () => { export const WithdrawalsContainer = () => {
@@ -19,9 +18,11 @@ export const WithdrawalsContainer = () => {
}); });
const setView = useSidebar((store) => store.setView); const setView = useSidebar((store) => store.setView);
const { ready, delayed } = useIncompleteWithdrawals(); const { ready, delayed } = useIncompleteWithdrawals();
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return ( return (
<VegaWalletContainer> <>
<div className="h-full relative"> <div className="h-full relative">
<WithdrawalsTable <WithdrawalsTable
data-testid="withdrawals-history" data-testid="withdrawals-history"
@@ -43,6 +44,6 @@ export const WithdrawalsContainer = () => {
</Button> </Button>
</div> </div>
)} )}
</VegaWalletContainer> </>
); );
}; };
@@ -1,8 +1,8 @@
import { useCallback } from 'react'; import { useCallback } from 'react';
import { Button } from '@vegaprotocol/ui-toolkit'; import { Button } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import type { PinnedAsset } from '@vegaprotocol/accounts'; import type { PinnedAsset } from '@vegaprotocol/accounts';
import { AccountManager } from '@vegaprotocol/accounts'; import { AccountManager } from '@vegaprotocol/accounts';
+1 -1
View File
@@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
export const THROTTLE_UPDATE_TIME = 500; export const THROTTLE_UPDATE_TIME = 500;
export const RISK_ACCEPTED_KEY = 'vega_risk_accepted'; export const ONBOARDING_VIEWED_KEY = 'vega_onboarding_viewed';
export const MAINNET_WELCOME_HEADER = t( export const MAINNET_WELCOME_HEADER = t(
'Trade cash settled futures on the fully decentralised Vega network.' 'Trade cash settled futures on the fully decentralised Vega network.'
); );
@@ -1,10 +1,10 @@
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { FillsManager } from '@vegaprotocol/fills'; import { FillsManager } from '@vegaprotocol/fills';
import { create } from 'zustand'; import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { useDataGridEvents } from '@vegaprotocol/datagrid'; import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { Splash } from '@vegaprotocol/ui-toolkit';
import type { DataGridSlice } from '../../stores/datagrid-store-slice'; import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice'; import { createDataGridSlice } from '../../stores/datagrid-store-slice';
@@ -12,10 +12,9 @@ import {
NetworkParams, NetworkParams,
useNetworkParams, useNetworkParams,
} from '@vegaprotocol/network-parameters'; } from '@vegaprotocol/network-parameters';
import type { AgGridReact } from 'ag-grid-react';
import type { DataGridSlice } from '../../stores/datagrid-store-slice'; import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice'; import { createDataGridSlice } from '../../stores/datagrid-store-slice';
import { useEffect, useRef } from 'react'; import { useEffect } from 'react';
import { create } from 'zustand'; import { create } from 'zustand';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
@@ -26,8 +25,6 @@ export const LiquidityContainer = ({
marketId: string | undefined; marketId: string | undefined;
filter?: Filter; filter?: Filter;
}) => { }) => {
const gridRef = useRef<AgGridReact | null>(null);
const gridStore = useLiquidityStore((store) => store.gridStore); const gridStore = useLiquidityStore((store) => store.gridStore);
const updateGridStore = useLiquidityStore((store) => store.updateGridStore); const updateGridStore = useLiquidityStore((store) => store.updateGridStore);
@@ -60,7 +57,6 @@ export const LiquidityContainer = ({
return ( return (
<div className="h-full relative"> <div className="h-full relative">
<LiquidityTable <LiquidityTable
ref={gridRef}
rowData={data} rowData={data}
symbol={symbol} symbol={symbol}
assetDecimalPlaces={assetDecimalPlaces} assetDecimalPlaces={assetDecimalPlaces}
@@ -52,6 +52,9 @@ export const Navbar = ({
'bg-vega-yellow text-vega-clight-50': theme === 'yellow', 'bg-vega-yellow text-vega-clight-50': theme === 'yellow',
'text-default': theme === 'system', 'text-default': theme === 'system',
})} })}
style={{
background: theme === 'yellow' ? 'url(/testnet-logo-bg.png' : 'none',
}}
> >
<VLogo className="w-4" /> <VLogo className="w-4" />
</NavLink> </NavLink>
@@ -1,21 +1,15 @@
import { OrderbookManager } from '@vegaprotocol/market-depth'; import { OrderbookManager } from '@vegaprotocol/market-depth';
import { useCreateOrderStore } from '@vegaprotocol/orders';
import { ViewType, useSidebar } from '../sidebar'; import { ViewType, useSidebar } from '../sidebar';
import { useDealTicketFormValues } from '@vegaprotocol/deal-ticket';
export const OrderbookContainer = ({ marketId }: { marketId: string }) => { export const OrderbookContainer = ({ marketId }: { marketId: string }) => {
const useOrderStoreRef = useCreateOrderStore(); const update = useDealTicketFormValues((state) => state.updateAll);
const updateOrder = useOrderStoreRef((store) => store.update);
const setView = useSidebar((store) => store.setView); const setView = useSidebar((store) => store.setView);
return ( return (
<OrderbookManager <OrderbookManager
marketId={marketId} marketId={marketId}
onClick={({ price, size }) => { onClick={(values) => {
if (price) { update(marketId, values);
updateOrder(marketId, { price });
}
if (size) {
updateOrder(marketId, { size });
}
setView({ type: ViewType.Order }); setView({ type: ViewType.Order });
}} }}
/> />
@@ -1,7 +1,6 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid'; import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { Filter, OrderListManager } from '@vegaprotocol/orders';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { Filter } from '@vegaprotocol/orders';
import { OrderListManager } from '@vegaprotocol/orders';
import { Splash } from '@vegaprotocol/ui-toolkit'; import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet'; import { useVegaWallet } from '@vegaprotocol/wallet';
import { import {
@@ -1,7 +1,15 @@
import { act, render, screen } from '@testing-library/react'; import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { Sidebar, SidebarContent, ViewType, useSidebar } from './sidebar'; import {
Sidebar,
SidebarButton,
SidebarContent,
ViewType,
useSidebar,
} from './sidebar';
import { MemoryRouter, Route, Routes } from 'react-router-dom'; import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { VegaWalletProvider } from '@vegaprotocol/wallet';
jest.mock('../node-health', () => ({ jest.mock('../node-health', () => ({
NodeHealthContainer: () => <span data-testid="node-health" />, NodeHealthContainer: () => <span data-testid="node-health" />,
@@ -19,16 +27,23 @@ jest.mock('../settings', () => ({
Settings: () => <div data-testid="settings" />, Settings: () => <div data-testid="settings" />,
})); }));
jest.mock('../welcome-dialog', () => ({
GetStarted: () => <div data-testid="get-started" />,
}));
describe('Sidebar', () => { describe('Sidebar', () => {
it.each(['/markets/all', '/portfolio'])( it.each(['/markets/all', '/portfolio'])(
'does not render ticket and info', 'does not render ticket and info',
(path) => { (path) => {
render( render(
<MemoryRouter initialEntries={[path]}> <VegaWalletProvider>
<Sidebar /> <MemoryRouter initialEntries={[path]}>
</MemoryRouter> <Sidebar />
</MemoryRouter>
</VegaWalletProvider>
); );
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument(); expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument(); expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument(); expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
@@ -43,11 +58,14 @@ describe('Sidebar', () => {
it('renders ticket and info on market pages', () => { it('renders ticket and info on market pages', () => {
render( render(
<MemoryRouter initialEntries={['/markets/ABC']}> <VegaWalletProvider>
<Sidebar /> <MemoryRouter initialEntries={['/markets/ABC']}>
</MemoryRouter> <Sidebar />
</MemoryRouter>
</VegaWalletProvider>
); );
expect(screen.getByTestId(ViewType.ViewAs)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument(); expect(screen.getByTestId(ViewType.Settings)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument(); expect(screen.getByTestId(ViewType.Transfer)).toBeInTheDocument();
expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument(); expect(screen.getByTestId(ViewType.Deposit)).toBeInTheDocument();
@@ -61,9 +79,11 @@ describe('Sidebar', () => {
it('renders selected state', async () => { it('renders selected state', async () => {
render( render(
<MemoryRouter initialEntries={['/markets/ABC']}> <VegaWalletProvider>
<Sidebar /> <MemoryRouter initialEntries={['/markets/ABC']}>
</MemoryRouter> <Sidebar />
</MemoryRouter>
</VegaWalletProvider>
); );
const settingsButton = screen.getByTestId(ViewType.Settings); const settingsButton = screen.getByTestId(ViewType.Settings);
@@ -87,11 +107,13 @@ describe('Sidebar', () => {
describe('SidebarContent', () => { describe('SidebarContent', () => {
it('renders the correct content', () => { it('renders the correct content', () => {
const { container } = render( const { container } = render(
<MemoryRouter initialEntries={['/markets/ABC']}> <VegaWalletProvider>
<Routes> <MemoryRouter initialEntries={['/markets/ABC']}>
<Route path="/markets/:marketId" element={<SidebarContent />} /> <Routes>
</Routes> <Route path="/markets/:marketId" element={<SidebarContent />} />
</MemoryRouter> </Routes>
</MemoryRouter>
</VegaWalletProvider>
); );
expect(container).toBeEmptyDOMElement(); expect(container).toBeEmptyDOMElement();
@@ -111,11 +133,13 @@ describe('SidebarContent', () => {
it('closes sidebar if market id is required but not present', () => { it('closes sidebar if market id is required but not present', () => {
const { container } = render( const { container } = render(
<MemoryRouter initialEntries={['/portfolio']}> <VegaWalletProvider>
<Routes> <MemoryRouter initialEntries={['/portfolio']}>
<Route path="/portfolio" element={<SidebarContent />} /> <Routes>
</Routes> <Route path="/portfolio" element={<SidebarContent />} />
</MemoryRouter> </Routes>
</MemoryRouter>
</VegaWalletProvider>
); );
act(() => { act(() => {
@@ -137,3 +161,25 @@ describe('SidebarContent', () => {
expect(container).toBeEmptyDOMElement(); expect(container).toBeEmptyDOMElement();
}); });
}); });
describe('SidebarButton', () => {
it.each([ViewType.Info, ViewType.Deposit, ViewType.ViewAs])(
'runs given callback regardless of requested view (%s)',
async (view) => {
const onClick = jest.fn();
render(
<SidebarButton
icon={VegaIconNames.INFO}
tooltip="INFO"
onClick={onClick}
view={view}
/>
);
const btn = screen.getByTestId(view);
await userEvent.click(btn);
expect(onClick).toBeCalled();
}
);
});
+48 -16
View File
@@ -15,6 +15,8 @@ import { Tooltip } from '../../components/tooltip';
import { WithdrawContainer } from '../withdraw-container'; import { WithdrawContainer } from '../withdraw-container';
import { Routes as AppRoutes } from '../../pages/client-router'; import { Routes as AppRoutes } from '../../pages/client-router';
import { persist } from 'zustand/middleware'; import { persist } from 'zustand/middleware';
import { GetStarted } from '../welcome-dialog';
import { useVegaWallet, useViewAsDialog } from '@vegaprotocol/wallet';
const STORAGE_KEY = 'vega_sidebar_store'; const STORAGE_KEY = 'vega_sidebar_store';
@@ -25,6 +27,7 @@ export enum ViewType {
Withdraw = 'Withdraw', Withdraw = 'Withdraw',
Transfer = 'Transfer', Transfer = 'Transfer',
Settings = 'Settings', Settings = 'Settings',
ViewAs = 'ViewAs',
} }
type SidebarView = type SidebarView =
@@ -52,6 +55,8 @@ type SidebarView =
export const Sidebar = () => { export const Sidebar = () => {
const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1'; const navClasses = 'flex lg:flex-col items-center gap-2 lg:gap-4 p-1';
const setViewAsDialogOpen = useViewAsDialog((state) => state.setOpen);
const { pubKeys } = useVegaWallet();
return ( return (
<div className="flex lg:flex-col gap-2 h-full p-1" data-testid="sidebar"> <div className="flex lg:flex-col gap-2 h-full p-1" data-testid="sidebar">
<nav className={navClasses}> <nav className={navClasses}>
@@ -104,6 +109,16 @@ export const Sidebar = () => {
</Routes> </Routes>
</nav> </nav>
<nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}> <nav className={classNames(navClasses, 'ml-auto lg:mt-auto lg:ml-0')}>
<SidebarButton
view={ViewType.ViewAs}
onClick={() => {
setViewAsDialogOpen(true);
}}
icon={VegaIconNames.EYE}
tooltip={t('View as party')}
disabled={Boolean(pubKeys)}
/>
<SidebarButton <SidebarButton
view={ViewType.Settings} view={ViewType.Settings}
icon={VegaIconNames.COG} icon={VegaIconNames.COG}
@@ -115,25 +130,43 @@ export const Sidebar = () => {
); );
}; };
const SidebarButton = ({ export const SidebarButton = ({
view, view,
icon, icon,
tooltip, tooltip,
disabled = false,
onClick,
}: { }: {
view: ViewType; view?: ViewType;
icon: VegaIconNames; icon: VegaIconNames;
tooltip: string; tooltip: string;
disabled?: boolean;
onClick?: () => void;
}) => { }) => {
const { currView, setView } = useSidebar((store) => ({ const { currView, setView } = useSidebar((store) => ({
currView: store.view, currView: store.view,
setView: store.setView, setView: store.setView,
})); }));
const buttonClasses = classNames('flex items-center p-1 rounded', {
'text-vega-clight-200 dark:text-vega-cdark-200 hover:bg-vega-clight-500 dark:hover:bg-vega-cdark-500': const onSelect = (view: SidebarView['type']) => {
view !== currView?.type, if (view === currView?.type) {
'bg-vega-yellow hover:bg-vega-yellow-550 text-black': setView(null);
view === currView?.type, } else {
}); setView({ type: view });
}
};
const buttonClasses = classNames(
'flex items-center p-1 rounded',
'disabled:cursor-not-allowed disabled:text-vega-clight-500 dark:disabled:text-vega-cdark-500',
{
'text-vega-clight-200 dark:text-vega-cdark-200 enabled:hover:bg-vega-clight-500 dark:enabled:hover:bg-vega-cdark-500':
!view || view !== currView?.type,
'bg-vega-yellow enabled:hover:bg-vega-yellow-550 text-black':
view && view === currView?.type,
}
);
return ( return (
<Tooltip <Tooltip
description={tooltip} description={tooltip}
@@ -145,13 +178,8 @@ const SidebarButton = ({
<button <button
className={buttonClasses} className={buttonClasses}
data-testid={view} data-testid={view}
onClick={() => { onClick={onClick || (() => onSelect(view as SidebarView['type']))}
if (view === currView?.type) { disabled={disabled}
setView(null);
} else {
setView({ type: view });
}
}}
> >
<VegaIcon name={icon} size={20} /> <VegaIcon name={icon} size={20} />
</button> </button>
@@ -184,6 +212,7 @@ export const SidebarContent = () => {
setView({ type: ViewType.Deposit, assetId }) setView({ type: ViewType.Deposit, assetId })
} }
/> />
<GetStarted />
</ContentWrapper> </ContentWrapper>
); );
} else { } else {
@@ -207,6 +236,7 @@ export const SidebarContent = () => {
return ( return (
<ContentWrapper title={t('Deposit')}> <ContentWrapper title={t('Deposit')}>
<DepositContainer assetId={view.assetId} /> <DepositContainer assetId={view.assetId} />
<GetStarted />
</ContentWrapper> </ContentWrapper>
); );
} }
@@ -215,6 +245,7 @@ export const SidebarContent = () => {
return ( return (
<ContentWrapper title={t('Withdraw')}> <ContentWrapper title={t('Withdraw')}>
<WithdrawContainer assetId={view.assetId} /> <WithdrawContainer assetId={view.assetId} />
<GetStarted />
</ContentWrapper> </ContentWrapper>
); );
} }
@@ -223,6 +254,7 @@ export const SidebarContent = () => {
return ( return (
<ContentWrapper title={t('Transfer')}> <ContentWrapper title={t('Transfer')}>
<TransferContainer assetId={view.assetId} /> <TransferContainer assetId={view.assetId} />
<GetStarted />
</ContentWrapper> </ContentWrapper>
); );
} }
@@ -277,7 +309,7 @@ export const useSidebar = create<{
view: null, view: null,
setView: (x) => setView: (x) =>
set(() => { set(() => {
if (x === null) { if (x == null) {
return { view: null, init: false }; return { view: null, init: false };
} }
@@ -0,0 +1 @@
export * from './stop-orders-container';
@@ -0,0 +1,41 @@
import { useDataGridEvents } from '@vegaprotocol/datagrid';
import { t } from '@vegaprotocol/i18n';
import { StopOrdersManager } from '@vegaprotocol/orders';
import { Splash } from '@vegaprotocol/ui-toolkit';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { DataGridSlice } from '../../stores/datagrid-store-slice';
import { createDataGridSlice } from '../../stores/datagrid-store-slice';
export const StopOrdersContainer = () => {
const { pubKey, isReadOnly } = useVegaWallet();
const onMarketClick = useMarketClickHandler(true);
const gridStore = useStopOrdersStore((store) => store.gridStore);
const updateGridStore = useStopOrdersStore((store) => store.updateGridStore);
const gridStoreCallbacks = useDataGridEvents(gridStore, (colState) => {
updateGridStore(colState);
});
if (!pubKey) {
return <Splash>{t('Please connect Vega wallet')}</Splash>;
}
return (
<StopOrdersManager
partyId={pubKey}
onMarketClick={onMarketClick}
isReadOnly={isReadOnly}
gridProps={gridStoreCallbacks}
/>
);
};
const useStopOrdersStore = create<DataGridSlice>()(
persist(createDataGridSlice, {
name: 'vega_fills_store',
})
);
@@ -27,6 +27,16 @@ describe('VegaWalletConnectButton', () => {
it('should fire dialog when not connected', () => { it('should fire dialog when not connected', () => {
render(generateJsx({ pubKey: null } as VegaWalletContextShape)); render(generateJsx({ pubKey: null } as VegaWalletContextShape));
const button = screen.getByTestId('connect-vega-wallet');
expect(button).toHaveTextContent('Get started');
fireEvent.click(button);
expect(mockUpdateDialogOpen).toHaveBeenCalled();
});
it('should render "Connect" when browser wallet is detected', () => {
window.vega = window.vega || ({} as Vega);
render(generateJsx({ pubKey: null } as VegaWalletContextShape));
const button = screen.getByTestId('connect-vega-wallet'); const button = screen.getByTestId('connect-vega-wallet');
expect(button).toHaveTextContent('Connect'); expect(button).toHaveTextContent('Connect');
fireEvent.click(button); fireEvent.click(button);
@@ -1,5 +1,6 @@
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import CopyToClipboard from 'react-copy-to-clipboard'; import CopyToClipboard from 'react-copy-to-clipboard';
import { isBrowserWalletInstalled } from '@vegaprotocol/wallet';
import { truncateByChars } from '@vegaprotocol/utils'; import { truncateByChars } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n'; import { t } from '@vegaprotocol/i18n';
import { import {
@@ -37,7 +38,7 @@ export const VegaWalletConnectButton = () => {
fetchPubKeys, fetchPubKeys,
} = useVegaWallet(); } = useVegaWallet();
const isConnected = pubKey !== null; const isConnected = pubKey !== null;
const walletInstalled = isBrowserWalletInstalled();
const activeKey = useMemo(() => { const activeKey = useMemo(() => {
return pubKeys?.find((pk) => pk.publicKey === pubKey); return pubKeys?.find((pk) => pk.publicKey === pubKey);
}, [pubKey, pubKeys]); }, [pubKey, pubKeys]);
@@ -118,7 +119,9 @@ export const VegaWalletConnectButton = () => {
intent={Intent.None} intent={Intent.None}
icon={<VegaIcon name={VegaIconNames.ARROW_RIGHT} size={14} />} icon={<VegaIcon name={VegaIconNames.ARROW_RIGHT} size={14} />}
> >
<span className="whitespace-nowrap uppercase">{t('Connect')}</span> <span className="whitespace-nowrap uppercase">
{walletInstalled ? t('Connect') : t('Get started')}
</span>
</Button> </Button>
); );
}; };
@@ -0,0 +1,31 @@
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { GetStarted } from './get-started';
import { render, screen } from '@testing-library/react';
describe('GetStarted', () => {
const renderComponent = (context: Partial<VegaWalletContextShape> = {}) => {
return render(
<VegaWalletContext.Provider value={context as VegaWalletContextShape}>
<GetStarted />
</VegaWalletContext.Provider>
);
};
it('renders full get started content if not connected and no browser wallet detected', () => {
renderComponent();
expect(screen.getByTestId('get-started-banner')).toBeInTheDocument();
});
it('renders connect prompt if no pubKey but wallet installed', () => {
globalThis.window.vega = {} as Vega;
renderComponent();
expect(screen.getByTestId('order-connect-wallet')).toBeInTheDocument();
globalThis.window.vega = undefined as unknown as Vega;
});
it('renders nothing if connected', () => {
const { container } = renderComponent({ pubKey: 'my-pubkey' });
expect(container).toBeEmptyDOMElement();
});
});
@@ -0,0 +1,107 @@
import classNames from 'classnames';
import { t } from '@vegaprotocol/i18n';
import { ExternalLink, Intent, TradingButton } from '@vegaprotocol/ui-toolkit';
import {
useVegaWallet,
useVegaWalletDialogStore,
isBrowserWalletInstalled,
} from '@vegaprotocol/wallet';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { useLocalStorage } from '@vegaprotocol/react-helpers';
import * as constants from '../constants';
interface Props {
lead?: string;
}
export const GetStarted = ({ lead }: Props) => {
const { pubKey } = useVegaWallet();
const { VEGA_ENV, VEGA_NETWORKS } = useEnvironment();
const CANONICAL_URL = VEGA_NETWORKS[VEGA_ENV] || 'https://console.vega.xyz';
const [, setOnboardingViewed] = useLocalStorage(
constants.ONBOARDING_VIEWED_KEY
);
const openVegaWalletDialog = useVegaWalletDialogStore(
(store) => store.openVegaWalletDialog
);
const onButtonClick = () => {
openVegaWalletDialog();
setOnboardingViewed('true');
};
const wrapperClasses = classNames(
'flex flex-col py-4 px-6 gap-4 rounded',
'bg-vega-blue-300 dark:bg-vega-blue-700',
'border border-vega-blue-350 dark:border-vega-blue-650',
{ 'mt-8': !lead }
);
if (!pubKey && !isBrowserWalletInstalled()) {
return (
<div className={wrapperClasses} data-testid="get-started-banner">
{lead && <h2>{lead}</h2>}
<h3 className="text-lg">{t('Get started')}</h3>
<div>
<ul className="list-decimal list-inside">
<li>{t('Get a Vega wallet')}</li>
<li>{t('Connect')}</li>
<li>{t('Deposit funds')}</li>
<li>{t('Open a position')}</li>
</ul>
</div>
<div>
<TradingButton
intent={Intent.Info}
onClick={onButtonClick}
data-testid="get-started-button"
>
{t('Get started')}
</TradingButton>
</div>
{VEGA_ENV === Networks.MAINNET && (
<p className="text-sm">
{t('Experiment for free with virtual assets on')}{' '}
<ExternalLink href={CANONICAL_URL}>
{t('Fairground Testnet')}
</ExternalLink>
</p>
)}
{VEGA_ENV === Networks.TESTNET && (
<p className="text-sm">
{t('Ready to trade with real funds?')}{' '}
<ExternalLink href={CANONICAL_URL}>
{t('Switch to Mainnet')}
</ExternalLink>
</p>
)}
</div>
);
}
if (!pubKey) {
return (
<div className={wrapperClasses}>
<p className="text-sm mb-1">
You need a{' '}
<ExternalLink href="https://vega.xyz/wallet">
Vega wallet
</ExternalLink>{' '}
to start trading in this market.
</p>
<TradingButton
onClick={openVegaWalletDialog}
size="small"
data-testid="order-connect-wallet"
intent={Intent.Info}
>
{t('Connect wallet')}
</TradingButton>
</div>
);
}
return null;
};
@@ -1,2 +1,3 @@
export * from './welcome-dialog'; export * from './welcome-dialog';
export * from './risk-message'; export * from './risk-message';
export * from './get-started';
@@ -1,50 +0,0 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { Networks, useEnvironment } from '@vegaprotocol/environment';
import { RiskNoticeDialog } from './risk-notice-dialog';
jest.mock('@vegaprotocol/environment');
const mockEnvDefinitions = {
VEGA_CONFIG_URL: 'https://config.url',
VEGA_URL: 'https://test.url',
VEGA_NETWORKS: JSON.stringify({}),
};
describe('Risk notice dialog', () => {
const mockOnClose = jest.fn();
afterEach(() => {
jest.clearAllMocks();
});
it.each`
assertion | network
${'displays'} | ${Networks.CUSTOM}
${'displays'} | ${Networks.DEVNET}
${'displays'} | ${Networks.TESTNET}
`(
'$assertion a generic message on $network',
async ({ assertion, network }) => {
// @ts-ignore ignore mock implementation
useEnvironment.mockImplementation(() => ({
...mockEnvDefinitions,
VEGA_ENV: network,
}));
render(<RiskNoticeDialog onClose={mockOnClose} network={network} />);
expect(
screen.getByText(
new RegExp(
`This application for trading on Vega is connected to ${network}`
)
)
).toBeInTheDocument();
const button = screen.getByRole('button', {
name: 'Continue',
});
fireEvent.click(button);
expect(mockOnClose).toHaveBeenCalled();
}
);
});

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