Compare commits

..
Author SHA1 Message Date
Madalina Raicu aebf956e00 fix(trading): lp data provider reload - updating book 2024-02-22 14:39:31 +00:00
30 changed files with 399 additions and 719 deletions
@@ -1,7 +1,7 @@
import { getNewAssetTxBody } from '../support/governance.functions';
context('Proposal page', { tags: '@smoke' }, function () {
describe.skip('Verify elements on page', 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;
@@ -7,7 +7,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
describe('Links and buttons', function () {
it('should have link for proposal page', function () {
it.skip('should have link for proposal page', function () {
cy.getByTestId('home-proposals').within(() => {
cy.get('[href="/proposals"]')
.should('exist')
@@ -51,7 +51,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
});
it('should have external link for governance', function () {
it.skip('should have external link for governance', function () {
cy.getByTestId('home-proposals').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
@@ -59,7 +59,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
});
it('should have link for validator page', function () {
it.skip('should have link for validator page', function () {
cy.getByTestId('home-validators').within(() => {
cy.get('[href="/validators"]')
.first()
@@ -68,7 +68,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
});
it('should have external link for validators', function () {
it.skip('should have external link for validators', function () {
cy.getByTestId('home-validators').within(() => {
cy.getByTestId('external-link')
.should('have.attr', 'href')
@@ -79,29 +79,29 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
});
it('should have information on active nodes', function () {
it.skip('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '1')
.should('contain.text', '2')
.and('contain.text', 'active nodes');
});
it('should have information on consensus nodes', function () {
it.skip('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '1')
.should('contain.text', '2')
.and('contain.text', 'consensus nodes');
});
it('should contain link to specific validators', function () {
it.skip('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '1')
.should('have.length', '2')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
});
it('should have link for rewards page', function () {
it.skip('should have link for rewards page', function () {
cy.getByTestId('home-rewards').within(() => {
cy.get('[href="/rewards"]')
.first()
@@ -110,7 +110,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
});
it('should have link for withdrawal page', function () {
it.skip('should have link for withdrawal page', function () {
cy.getByTestId('home-vega-token').within(() => {
cy.get('[href="/token/withdraw"]')
.first()
@@ -132,7 +132,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
// 0006-NETW-003 0006-NETW-008 0006-NETW-009 0006-NETW-010 0006-NETW-012 0006-NETW-013 0006-NETW-017 0006-NETW-018 0006-NETW-019 0006-NETW-020
it('should have option to switch to different network node', function () {
it.skip('should have option to switch to different network node', function () {
cy.getByTestId('git-network-data').within(() => {
cy.getByTestId('link').click();
});
@@ -159,7 +159,7 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
cy.getByTestId('node-url-custom').click({ force: true });
cy.get('input').should('exist');
cy.getByTestId('connect').should('be.disabled');
cy.getByTestId('dialog-close').click();
cy.getByTestId('icon-cross').click();
});
it('should display eth data', function () {
@@ -33,12 +33,12 @@ context(
verifyTabHighlighted(navigation.proposals);
});
it('should have GOVERNANCE header visible', function () {
it.skip('should have GOVERNANCE header visible', function () {
verifyPageHeader('Proposals');
});
// 3002-PROP-023 3004-PMAC-002 3005-PASN-002 3006-PASC-002 3007-PNEC-002 3008-PFRO-003
it('new proposal page should have button for link to more information on proposals', function () {
it.skip('new proposal page should have button for link to more information on proposals', function () {
cy.getByTestId('new-proposal-link').click();
cy.url().should('include', '/proposals/propose/raw');
cy.contains('To see Explorer data on proposals visit').within(() => {
@@ -73,7 +73,7 @@ context(
navigateTo(navigation.proposals);
});
it('should be able to see a working link for - find out more about Vega governance', function () {
it.skip('should be able to see a working link for - find out more about Vega governance', function () {
// 3001-VOTE-001 // 3002-PROP-001
cy.getByTestId(proposalDocumentationLink)
.should('be.visible')
@@ -40,10 +40,10 @@ context('View functionality with public key', { tags: '@smoke' }, function () {
verifyConnectedToPubKey();
});
it.skip('Able to connect public key via wallet and view assets in wallet', function () {
it('Able to connect public key via wallet and view assets in wallet', function () {
verifyConnectedToPubKey();
cy.getByTestId('currency-title', { timeout: 10000 })
.should('have.length.at.least', 2)
.should('have.length.at.least', 4)
.and('contain.text', 'USDC (fake)');
});
@@ -46,11 +46,11 @@ context('Validators Page - verify elements on page', function () {
// @ts-ignore clash between jest and cypress
describe('with wallets disconnected', { tags: '@smoke' }, function () {
it('Should have validators tab highlighted', function () {
it.skip('Should have validators tab highlighted', function () {
verifyTabHighlighted(navigation.validators);
});
it('Should have validators ON VEGA header visible', function () {
it.skip('Should have validators ON VEGA header visible', function () {
verifyPageHeader('Validators');
});
@@ -192,7 +192,7 @@ context('Validators Page - verify elements on page', function () {
});
// 1002-STKE-006
it('Should be able to see validator name', function () {
it.skip('Should be able to see validator name', function () {
cy.getByTestId(validatorTitle).should('not.be.empty');
});
+3 -3
View File
@@ -33,7 +33,7 @@ LC_ALL="en_US.UTF-8"
# Cosmic elevator flags
NX_SUCCESSOR_MARKETS=true
NX_METAMASK_SNAPS=true
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_PRODUCT_PERPETUALS=false
NX_UPDATE_MARKET_STATE=false
NX_REFERRALS=true
NX_GOVERNANCE_TRANSFERS=true
NX_GOVERNANCE_TRANSFERS=false
@@ -1,8 +1,7 @@
import classNames from 'classnames';
import { type ReactNode } from 'react';
interface HeadingProps {
title?: ReactNode;
title?: string;
centerContent?: boolean;
marginTop?: boolean;
marginBottom?: boolean;
@@ -54,8 +54,11 @@ const ProposalTypeTags = ({
if (proposal.__typename === 'BatchProposal') {
return (
<div data-testid="proposal-type">
<ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
<div data-testid="proposal-type" className="flex gap-1">
{proposal.subProposals?.map((subProposal, i) => {
if (!subProposal?.terms) return null;
return <ProposalTypeTag key={i} terms={subProposal.terms} />;
})}
</div>
);
}
@@ -312,7 +315,7 @@ const ProposalDetails = ({
{proposal.subProposals.map((p, i) => {
if (!p?.terms) return null;
return (
<li key={i} className="flex gap-3 items-center">
<li key={i} className="flex gap-3">
<span className={getIndicatorStyle(i + 1)}>{i + 1}</span>
<span>
<div>{renderDetails(p.terms)}</div>
@@ -3,8 +3,16 @@ import { useTranslation } from 'react-i18next';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import {
type BatchProposalFieldsFragment,
type ProposalFieldsFragment,
} from '../../__generated__/Proposals';
export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
export const ProposalJson = ({
proposal,
}: {
proposal: ProposalFieldsFragment | BatchProposalFieldsFragment;
}) => {
const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false);
@@ -5,11 +5,6 @@ import {
} from './proposal-market-changes';
import type { JsonValue } from '../../../../components/json-diff';
jest.mock('../proposal/market-name.tsx', () => ({
...jest.requireActual('../proposal/market-name.tsx'),
MarketName: jest.fn(),
}));
describe('applyImmutableKeysFromEarlierVersion', () => {
it('returns an empty object if any argument is not an object or null', () => {
const earlierVersion: JsonValue = null;
@@ -62,21 +57,21 @@ describe('applyImmutableKeysFromEarlierVersion', () => {
describe('ProposalMarketChanges', () => {
it('renders correctly', () => {
const { getByTestId } = render(
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
);
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
});
it('JsonDiff is not visible when showChanges is false', () => {
const { queryByTestId } = render(
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
);
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
});
it('JsonDiff is visible when showChanges is true', async () => {
const { getByTestId } = render(
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
);
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
expect(getByTestId('json-diff')).toBeInTheDocument();
@@ -1,24 +1,14 @@
import cloneDeep from 'lodash/cloneDeep';
import set from 'lodash/set';
import get from 'lodash/get';
import compact from 'lodash/compact';
import orderBy from 'lodash/orderBy';
import { JsonDiff, type JsonValue } from '../../../../components/json-diff';
import { JsonDiff } from '../../../../components/json-diff';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import {
useFetchProposal,
useFetchProposals,
flatten,
isBatchProposalNode,
isSingleProposalNode,
type ProposalNode,
type SingleProposalData,
type SubProposalData,
} from '../proposal/proposal-utils';
import { MarketName } from '../proposal/market-name';
import type { JsonValue } from '../../../../components/json-diff';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../../config';
const immutableKeys = [
'decimalPlaces',
@@ -28,8 +18,8 @@ const immutableKeys = [
];
export const applyImmutableKeysFromEarlierVersion = (
earlierVersion: unknown,
updatedVersion: unknown
earlierVersion: JsonValue,
updatedVersion: JsonValue
) => {
if (
typeof earlierVersion !== 'object' ||
@@ -45,8 +35,7 @@ export const applyImmutableKeysFromEarlierVersion = (
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
immutableKeys.forEach((key) => {
const earlier = get(earlierVersion, key);
if (earlier) set(updatedVersionCopy, key, earlier);
set(updatedVersionCopy, key, get(earlierVersion, key));
});
return updatedVersionCopy;
@@ -54,84 +43,49 @@ export const applyImmutableKeysFromEarlierVersion = (
interface ProposalMarketChangesProps {
marketId: string;
/** This are the changes from proposal */
updateProposalNode: ProposalNode | null;
indicator?: number;
updatedProposal: JsonValue;
}
export const ProposalMarketChanges = ({
marketId,
updateProposalNode,
indicator,
updatedProposal,
}: ProposalMarketChangesProps) => {
const { t } = useTranslation();
const [showChanges, setShowChanges] = useState(false);
const { data: originalProposalData } = useFetchProposal({
proposalId: marketId,
});
const {
state: { data },
} = useFetch(`${ENV.rest}governance?proposalId=${marketId}`, undefined, true);
const { data: enactedProposalsData } = useFetchProposals({
proposalState: 'STATE_ENACTED',
proposalType: 'TYPE_UPDATE_MARKET',
});
let updateProposal: SingleProposalData | SubProposalData | undefined;
if (isBatchProposalNode(updateProposalNode)) {
updateProposal = updateProposalNode.proposals.find(
(p, i) =>
p.terms.updateMarket?.marketId === marketId &&
(indicator != null ? i === indicator - 1 : true)
);
}
if (isSingleProposalNode(updateProposalNode)) {
updateProposal = updateProposalNode.proposal;
}
// this should get the proposal before the current one
const enactedUpdateMarketProposals = orderBy(
compact(
flatten(enactedProposalsData).filter((enacted) => {
const related = enacted.terms.updateMarket?.marketId === marketId;
const notCurrent =
enacted.id !== updateProposal?.id ||
('batchId' in enacted && enacted.batchId !== updateProposal.id);
const beforeCurrent =
Number(enacted.terms.enactmentTimestamp) <
Number(updateProposal?.terms.enactmentTimestamp);
return related && notCurrent && beforeCurrent;
})
),
[(proposal) => Number(proposal.terms.enactmentTimestamp)],
'desc'
const {
state: { data: enactedProposalData },
} = useFetch(
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
undefined,
true
);
const latestEnactedProposal =
enactedUpdateMarketProposals.length > 0
? enactedUpdateMarketProposals[0]
: undefined;
// @ts-ignore no types here :-/
const enacted = enactedProposalData?.connection?.edges
.filter(
// @ts-ignore no type here
({ node }) => node?.proposal?.terms?.updateMarket?.marketId === marketId
)
// @ts-ignore no type here
.sort((a, b) => {
return (
new Date(a?.node?.terms?.enactmentTimestamp).getTime() -
new Date(b?.node?.terms?.enactmentTimestamp).getTime()
);
});
let originalProposal;
if (isBatchProposalNode(originalProposalData)) {
originalProposal = originalProposalData.proposals.find(
(proposal) => proposal.id === marketId && proposal.terms.newMarket != null
);
}
if (isSingleProposalNode(originalProposalData)) {
originalProposal = originalProposalData.proposal;
}
const latestEnactedProposal = enacted?.length
? enacted[enacted.length - 1]
: undefined;
// LEFT SIDE: update market proposal enacted just before this one
// or original new market proposal
const left =
latestEnactedProposal?.terms.updateMarket?.changes ||
originalProposal?.terms.newMarket?.changes;
// RIGHT SIDE: this update market proposal
const right = applyImmutableKeysFromEarlierVersion(
left,
updateProposal?.terms.updateMarket?.changes
);
const originalProposal =
// @ts-ignore no types with useFetch TODO: check this is good
data?.data?.proposal?.terms?.newMarket?.changes;
return (
<section data-testid="proposal-market-changes">
@@ -140,18 +94,25 @@ export const ProposalMarketChanges = ({
setToggleState={setShowChanges}
dataTestId={'proposal-market-changes-toggle'}
>
<SubHeading
title={
<>
{t('UpdateToMarket')}: <MarketName marketId={marketId} />
</>
}
/>
<SubHeading title={t('updatesToMarket')} />
</CollapsibleToggle>
{showChanges && (
<div className="mb-6">
<JsonDiff left={left as JsonValue} right={right as JsonValue} />
<JsonDiff
left={latestEnactedProposal || originalProposal}
right={
latestEnactedProposal
? applyImmutableKeysFromEarlierVersion(
latestEnactedProposal,
updatedProposal
)
: applyImmutableKeysFromEarlierVersion(
originalProposal,
updatedProposal
)
}
/>
</div>
)}
</section>
@@ -2,11 +2,6 @@ import { fireEvent, render, screen } from '@testing-library/react';
import { ProposalUpdateMarketState } from './proposal-update-market-state';
import { MarketUpdateType } from '@vegaprotocol/types';
jest.mock('../proposal/market-name.tsx', () => ({
...jest.requireActual('../proposal/market-name.tsx'),
MarketName: jest.fn(),
}));
describe('<ProposalUpdateMarketState />', () => {
const suspendProposal = {
__typename: 'UpdateMarketState' as const,
@@ -10,7 +10,6 @@ import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import { type UpdateMarketStatesFragment } from '../../__generated__/Proposals';
import { MarketUpdateTypeMapping } from '@vegaprotocol/types';
import { MarketName } from '../proposal/market-name';
interface ProposalUpdateMarketStateProps {
change: UpdateMarketStatesFragment | null;
@@ -21,18 +20,16 @@ export const ProposalUpdateMarketState = ({
}: ProposalUpdateMarketStateProps) => {
const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false);
let market;
let isTerminate = false;
if (!change || change.__typename !== 'UpdateMarketState') {
if (!change) {
return null;
}
const market = change?.market;
const isTerminate =
change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
let toggleTitle = t(change.updateType);
if (toggleTitle.length === 0) {
toggleTitle = t('MarketDetails');
if (change.__typename === 'UpdateMarketState') {
market = change?.market;
isTerminate = change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
}
return (
@@ -42,13 +39,7 @@ export const ProposalUpdateMarketState = ({
setToggleState={setShowDetails}
dataTestId="proposal-market-data-toggle"
>
<SubHeading
title={
<>
{toggleTitle}: <MarketName marketId={market?.id} />
</>
}
/>
<SubHeading title={t('MarketDetails')} />
</CollapsibleToggle>
{showDetails && (
@@ -16,31 +16,18 @@ const getColour = (indicator: number, max = COLOURS.length) => {
export const getStyle = (indicator: number, max = COLOURS.length) =>
classNames({
'bg-vega-yellow-400 after:bg-vega-yellow-400':
'yellow' === getColour(indicator, max),
'bg-vega-green-400 after:bg-vega-green-400':
'green' === getColour(indicator, max),
'bg-vega-blue-400 after:bg-vega-blue-400':
'blue' === getColour(indicator, max),
'bg-vega-purple-400 after:bg-vega-purple-400':
'purple' === getColour(indicator, max),
'bg-vega-pink-400 after:bg-vega-pink-400':
'pink' === getColour(indicator, max),
'bg-vega-orange-400 after:bg-vega-orange-400':
'orange' === getColour(indicator, max),
'bg-vega-red-400 after:bg-vega-red-400':
'red' === getColour(indicator, max),
'bg-vega-clight-600 after:bg-vega-clight-600':
'none' === getColour(indicator, max),
'bg-vega-yellow-400': 'yellow' === getColour(indicator, max),
'bg-vega-green-400': 'green' === getColour(indicator, max),
'bg-vega-blue-400': 'blue' === getColour(indicator, max),
'bg-vega-purple-400': 'purple' === getColour(indicator, max),
'bg-vega-pink-400': 'pink' === getColour(indicator, max),
'bg-vega-orange-400': 'orange' === getColour(indicator, max),
'bg-vega-red-400': 'red' === getColour(indicator, max),
'bg-vega-clight-600': 'none' === getColour(indicator, max),
});
export const getIndicatorStyle = (indicator: number) =>
classNames(
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8 w-7 text-center',
'text-border-1',
getStyle(indicator),
// Comment below if you want to remove the "chevron"
'relative mr-[11px]',
'after:absolute after:z-[-1] after:top-1 after:right-[-11px] after:rounded-sm',
"after:w-[22.62px] after:h-[22.62px] after:rotate-45 after:content-['']"
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8',
getStyle(indicator)
);
@@ -13,7 +13,6 @@ import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
import { ProposalVolumeDiscountProgramDetails } from '../proposal-volume-discount-program-details';
import { getIndicatorStyle } from './colours';
import { type ProposalNode } from './proposal-utils';
export const ProposalChangeDetails = ({
proposal,
@@ -23,7 +22,8 @@ export const ProposalChangeDetails = ({
}: {
proposal: Proposal | BatchProposal;
terms: ProposalTermsFieldsFragment;
restData: ProposalNode | null;
// eslint-disable-next-line
restData: any;
indicator?: number;
}) => {
let details = null;
@@ -63,13 +63,30 @@ export const ProposalChangeDetails = ({
}
case 'UpdateMarket': {
if (proposal.id) {
const marketId = terms.change.marketId;
const proposalData = restData?.data?.proposal;
let updatedProposal = null;
// single proposal
if ('terms' in proposalData) {
updatedProposal = proposalData?.terms?.updateMarket?.changes;
}
// batch proposal - need to fish for the actual changes
if (
'batchTerms' in proposalData &&
Array.isArray(proposalData.batchTerms?.changes)
) {
updatedProposal = proposalData?.batchTerms?.changes.find(
(ch: { updateMarket?: { marketId: string } }) =>
ch?.updateMarket?.marketId === marketId
)?.updateMarket?.changes;
}
details = (
<div className="flex flex-col gap-4">
<ProposalMarketData proposalId={proposal.id} />
<ProposalMarketChanges
indicator={indicator}
marketId={terms.change.marketId}
updateProposalNode={restData}
updatedProposal={updatedProposal}
/>
</div>
);
@@ -1,261 +0,0 @@
import compact from 'lodash/compact';
import { ENV } from '../../../../config';
import { useEffect, useState } from 'react';
type Maybe<T> = T | null | undefined;
type ProposalState =
| 'STATE_UNSPECIFIED'
| 'STATE_FAILED'
| 'STATE_OPEN'
| 'STATE_PASSED'
| 'STATE_REJECTED'
| 'STATE_DECLINED'
| 'STATE_ENACTED'
| 'STATE_WAITING_FOR_NODE_VOTE';
type ProposalType =
| 'TYPE_UNSPECIFIED'
| 'TYPE_ALL'
| 'TYPE_NEW_MARKET'
| 'TYPE_UPDATE_MARKET'
| 'TYPE_NETWORK_PARAMETERS'
| 'TYPE_NEW_ASSET'
| 'TYPE_NEW_FREE_FORM'
| 'TYPE_UPDATE_ASSET'
| 'TYPE_NEW_SPOT_MARKET'
| 'TYPE_UPDATE_SPOT_MARKET'
| 'TYPE_NEW_TRANSFER'
| 'TYPE_CANCEL_TRANSFER'
| 'TYPE_UPDATE_MARKET_STATE'
| 'TYPE_UPDATE_REFERRAL_PROGRAM'
| 'TYPE_UPDATE_VOLUME_DISCOUNT_PROGRAM';
type ProposalNodeType = 'TYPE_SINGLE_OR_UNSPECIFIED' | 'TYPE_BATCH';
type ProposalData = {
id: string;
rationale: {
description: string;
title: string;
};
state: ProposalState;
timestamp: string;
};
type Terms = {
cancelTransfer?: { changes: unknown };
enactmentTimestamp: string;
newAsset?: { changes: unknown };
newFreeform: object;
newMarket?: { changes: unknown };
newSpotMarket?: { changes: unknown };
newTransfer?: { changes: unknown };
updateAsset?: { assetId: string; changes: unknown };
updateMarket?: { marketId: string; changes: unknown };
updateMarketState?: {
changes: {
marketId: string;
price: string;
updateType:
| 'MARKET_STATE_UPDATE_TYPE_UNSPECIFIED'
| 'MARKET_STATE_UPDATE_TYPE_TERMINATE'
| 'MARKET_STATE_UPDATE_TYPE_SUSPEND'
| 'MARKET_STATE_UPDATE_TYPE_RESUME';
};
};
updateNetworkParameter?: { changes: unknown };
updateReferralProgram?: { changes: unknown };
updateSpotMarket?: { marketId: string; changes: unknown };
updateVolumeDiscountProgram?: { changes: unknown };
};
export type SingleProposalData = ProposalData & {
terms: Terms & {
closingTimestamp: string;
validationTimestamp: string;
};
};
type BatchProposalData = ProposalData & {
batchTerms: {
changes: Terms[];
};
};
export type SubProposalData = SingleProposalData & {
batchId: string;
};
export type ProposalNode = {
proposal: ProposalData;
proposalType: ProposalNodeType;
proposals: SubProposalData[];
};
type SingleProposalNode = ProposalNode & {
proposal: SingleProposalData;
proposalType: 'TYPE_SINGLE_OR_UNSPECIFIED';
proposals: [];
};
type BatchProposalNode = ProposalNode & {
proposal: BatchProposalData;
proposalType: 'TYPE_BATCH';
};
export const isProposalNode = (node: unknown): node is ProposalNode =>
Boolean(
typeof node === 'object' &&
node &&
'proposal' in node &&
typeof node.proposal === 'object' &&
node?.proposal &&
'id' in node.proposal &&
node?.proposal?.id
);
export const isSingleProposalNode = (
node: Maybe<ProposalNode>
): node is SingleProposalNode =>
Boolean(
node &&
node?.proposalType === 'TYPE_SINGLE_OR_UNSPECIFIED' &&
node?.proposal
);
export const isBatchProposalNode = (
node: Maybe<ProposalNode>
): node is BatchProposalNode =>
Boolean(
node &&
node?.proposalType === 'TYPE_BATCH' &&
node?.proposal &&
'batchTerms' in node.proposal &&
node?.proposals?.length > 0
);
// this includes also batch proposals with `updateMarket`s 👍
const PROPOSALS_ENDPOINT = `${ENV.rest}governances?proposalState=:proposalState&proposalType=:proposalType`;
// this can be queried also by sub proposal id as `proposalId` and it will
// return full batch proposal data with all of its sub proposals including
// the requested one inside `proposals` array.
const PROPOSAL_ENDPOINT = `${ENV.rest}governance?proposalId=:proposalId`;
export const getProposals = async ({
proposalState,
proposalType,
}: {
proposalState: ProposalState;
proposalType: ProposalType;
}) => {
try {
const response = await fetch(
PROPOSALS_ENDPOINT.replace(':proposalState', proposalState).replace(
':proposalType',
proposalType
)
);
if (response.ok) {
const data = await response.json();
if (
data &&
'connection' in data &&
data.connection &&
'edges' in data.connection &&
data.connection.edges?.length > 0
) {
const nodes = compact(
data.connection.edges.map((e: { node?: object }) => e?.node)
).filter(isProposalNode);
return nodes;
}
}
} catch {
// NOOP - ignore errors
}
return [];
};
export const getProposal = async ({ proposalId }: { proposalId: string }) => {
try {
const response = await fetch(
PROPOSAL_ENDPOINT.replace(':proposalId', proposalId)
);
if (response.ok) {
const data = await response.json();
if (data && 'data' in data && isProposalNode(data.data)) {
return data.data as ProposalNode;
}
}
} catch (err) {
// NOOP - ignore errors
}
return null;
};
export const useFetchProposal = ({ proposalId }: { proposalId?: string }) => {
const [data, setData] = useState<ProposalNode | null>(null);
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
const cb = async () => {
if (!proposalId) return;
setLoading(true);
const data = await getProposal({ proposalId });
setLoading(false);
if (data) {
setData(data);
}
};
cb();
}, [proposalId]);
return { data, loading };
};
export const useFetchProposals = ({
proposalState,
proposalType,
}: {
proposalState: ProposalState;
proposalType: ProposalType;
}) => {
const [data, setData] = useState<ProposalNode[]>([]);
const [loading, setLoading] = useState<boolean>(false);
useEffect(() => {
const cb = async () => {
setLoading(true);
const data = await getProposals({ proposalState, proposalType });
setLoading(false);
if (data) {
setData(data);
}
};
cb();
}, [proposalState, proposalType]);
return { data, loading };
};
export const flatten = (
nodes: ProposalNode[]
): (SingleProposalData | SubProposalData)[] => {
const flattenNodes = [];
for (const node of nodes) {
if (isSingleProposalNode(node)) {
flattenNodes.push(node.proposal);
}
if (isBatchProposalNode(node)) {
for (const sub of node.proposals) {
flattenNodes.push(sub);
}
}
}
return flattenNodes;
};
@@ -61,7 +61,7 @@ const renderComponent = (proposal: IProposal) => {
<MemoryRouter>
<MockedProvider>
<VegaWalletProvider config={vegaWalletConfig}>
<Proposal restData={null} proposal={proposal} />
<Proposal restData={{}} proposal={proposal} />
</VegaWalletProvider>
</MockedProvider>
</MemoryRouter>
@@ -8,17 +8,15 @@ import { ProposalJson } from '../proposal-json';
import { UserVote } from '../vote-details';
import Routes from '../../../routes';
import { ProposalState } from '@vegaprotocol/types';
import { type ProposalNode } from './proposal-utils';
import { useVoteSubmit } from '@vegaprotocol/proposals';
import { useUserVote } from '../vote-details/use-user-vote';
import { type Proposal as IProposal, type BatchProposal } from '../../types';
import { ProposalChangeDetails } from './proposal-change-details';
import { type JsonValue } from 'type-fest';
export interface ProposalProps {
proposal: IProposal | BatchProposal;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: ProposalNode | null;
restData: any;
}
export const Proposal = ({ proposal, restData }: ProposalProps) => {
@@ -97,7 +95,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
</div>
<div className="mb-6">
<ProposalJson proposal={restData?.proposal as unknown as JsonValue} />
<ProposalJson proposal={restData?.data?.proposal} />
</div>
</section>
);
@@ -16,7 +16,6 @@ import {
} from '../../__generated__/Proposals';
import { useBatchVoteInformation } from '../../hooks/use-vote-information';
import { getIndicatorStyle } from '../proposal/colours';
import { MarketName } from '../proposal/market-name';
export const CompactVotes = ({ number }: { number: BigNumber }) => (
<CompactNumber
@@ -41,9 +40,8 @@ const VoteProgress = ({
children,
}: VoteProgressProps) => {
const containerClasses = classNames(
'relative h-2 rounded-md overflow-hidden',
// 'border border-vega-dark-300',
colourfulBg ? 'bg-vega-red' : 'bg-vega-dark-200'
'relative h-10 rounded-md border border-vega-dark-300 overflow-hidden',
colourfulBg ? 'bg-vega-pink' : 'bg-vega-dark-400'
);
const progressClasses = classNames(
@@ -52,19 +50,17 @@ const VoteProgress = ({
);
const textClasses = classNames(
'w-full flex items-center justify-start text-white text-sm pb-1'
'absolute top-0 left-0 w-full h-full flex items-center justify-start px-3 text-black'
);
return (
<div>
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
<div className={textClasses}>{children}</div>
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
</div>
</div>
);
};
@@ -83,22 +79,14 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
<div data-testid={testId}>
{reached ? (
<div className="flex items-center gap-2">
<VegaIcon
name={VegaIconNames.TICK}
className="text-vega-green"
size={20}
/>
<VegaIcon name={VegaIconNames.TICK} size={20} />
<span>
{threshold.toString()}% {text} {t('met')}
</span>
</div>
) : (
<div className="flex items-center gap-2">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-red"
size={20}
/>
<VegaIcon name={VegaIconNames.CROSS} size={20} />
<span>
{threshold.toString()}% {text} {t('not met')}
</span>
@@ -164,7 +152,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-red"
className="text-vega-pink"
size={20}
/>
{t(
@@ -227,7 +215,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-red"
className="text-vega-pink"
size={20}
/>
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
@@ -249,7 +237,6 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
if (!p?.terms) return null;
return (
<VoteBreakdownBatchSubProposal
indicator={i + 1}
key={i}
proposal={proposal}
votes={proposal.votes}
@@ -286,39 +273,21 @@ const VoteBreakdownBatchSubProposal = ({
const isProposalOpen = proposal?.state === ProposalState.STATE_OPEN;
const isUpdateMarket = terms?.change?.__typename === 'UpdateMarket';
let marketId = undefined;
if (terms?.change?.__typename === 'UpdateMarket') {
marketId = terms.change.marketId;
}
if (terms?.change?.__typename === 'UpdateMarketState') {
marketId = terms.change.market.id;
}
const marketName = marketId ? (
<>
: <MarketName marketId={marketId} />
</>
) : null;
const indicatorElement = indicator && (
<span className={getIndicatorStyle(indicator)}>{indicator}</span>
);
return (
<div className="mb-6">
<div className="flex items-baseline gap-3 mb-3">
<div>
<div className="flex items-baseline gap-3">
{indicatorElement}
<h4>
{t(terms.change.__typename)} {marketName}
</h4>
</div>
<div className="rounded-sm bg-vega-dark-100 p-3">
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
<h4>{t(terms.change.__typename)}</h4>
</div>
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
);
};
@@ -333,13 +302,11 @@ const VoteBreakdownNormal = ({ proposal }: { proposal: Proposal }) => {
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
return (
<div className="mb-6">
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
);
};
@@ -403,13 +370,13 @@ const VoteBreakDownUI = ({
'flex justify-between flex-wrap gap-6'
);
const sectionClasses = classNames('min-w-[300px] flex-1 flex-grow');
const headingClasses = classNames('mb-2 text-sm text-white font-bold');
const headingClasses = classNames('mb-2 text-vega-dark-400');
const progressDetailsClasses = classNames(
'flex justify-between flex-wrap mt-2 text-sm'
);
return (
<div>
<div className="mb-6">
{isProposalOpen && (
<div
data-testid="vote-status"
@@ -426,7 +393,7 @@ const VoteBreakDownUI = ({
<VegaIcon
name={VegaIconNames.CROSS}
size={20}
className="text-vega-red"
className="text-vega-pink"
/>
)}
</span>
@@ -442,13 +409,103 @@ const VoteBreakDownUI = ({
<p className="m-0">
<Trans
i18nKey={'Currently expected to <0>fail</0>'}
components={[<span className="text-vega-red" />]}
components={[<span className="text-vega-pink" />]}
/>
</p>
)}
</div>
)}
{isUpdateMarket && (
<div className="mb-4">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={lpVoteWeight}
colourfulBg={true}
testId="lp-majority-progress"
>
<Status
reached={majorityLPMet}
threshold={requiredMajorityLPPercentage}
text={t('majorityThreshold')}
testId={
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
}
/>
</VoteProgress>
<div className={progressDetailsClasses}>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
}
>
<button>{lpVoteWeight.toFixed(1)}%</button>
</Tooltip>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<span>
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
</span>
</div>
</div>
</section>
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
}
testId="lp-participation-progress"
>
<Status
reached={participationLPMet}
threshold={requiredParticipationLP || new BigNumber(1)}
text={t('participationThreshold')}
testId={
participationLPMet
? 'lp-participation-met'
: 'lp-participation-not-met'
}
/>
</VoteProgress>
<div className="flex mt-2 text-sm">
<div className="flex items-center gap-1">
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
<Tooltip
description={formatNumber(
totalEquityLikeShareWeight,
defaultDP
)}
>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
</div>
</div>
</section>
</div>
</div>
)}
{isUpdateMarket && <h3 className={headingClasses}>{t('tokenVote')}</h3>}
<div className={sectionWrapperClasses}>
<section
@@ -548,97 +605,6 @@ const VoteBreakDownUI = ({
</div>
</section>
</div>
{/** Liquidity provider vote */}
{isUpdateMarket && (
<div className="mt-3">
<h3 className={headingClasses}>{t('liquidityProviderVote')}</h3>
<div className={sectionWrapperClasses}>
<section
className={sectionClasses}
data-testid="lp-majority-breakdown"
>
<VoteProgress
percentageFor={lpVoteWeight}
colourfulBg={true}
testId="lp-majority-progress"
>
<Status
reached={majorityLPMet}
threshold={requiredMajorityLPPercentage}
text={t('majorityThreshold')}
testId={
majorityLPMet ? 'lp-majority-met' : 'lp-majority-not-met'
}
/>
</VoteProgress>
<div className={progressDetailsClasses}>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesFor')}:</span>
<Tooltip
description={
<span>{lpVoteWeight.toFixed(defaultDP)}%</span>
}
>
<button>{lpVoteWeight.toFixed(1)}%</button>
</Tooltip>
</div>
<div className="flex items-center gap-1">
<span>{t('liquidityProviderVotesAgainst')}:</span>
<span>
<Tooltip
description={
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
}
>
<button>{noLPPercentage.toFixed(1)}%</button>
</Tooltip>
</span>
</div>
</div>
</section>
<section
className={sectionClasses}
data-testid="lp-participation-breakdown"
>
<VoteProgress
percentageFor={
lpParticipationThresholdProgress || new BigNumber(0)
}
testId="lp-participation-progress"
>
<Status
reached={participationLPMet}
threshold={requiredParticipationLP || new BigNumber(1)}
text={t('participationThreshold')}
testId={
participationLPMet
? 'lp-participation-met'
: 'lp-participation-not-met'
}
/>
</VoteProgress>
<div className="flex mt-2 text-sm">
<div className="flex items-center gap-1">
<span>{t('totalLiquidityProviderTokensVoted')}:</span>
<Tooltip
description={formatNumber(
totalEquityLikeShareWeight,
defaultDP
)}
>
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
</Tooltip>
</div>
</div>
</section>
</div>
</div>
)}
</div>
);
};
@@ -1,16 +1,17 @@
import { useParams } from 'react-router-dom';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
import { Proposal } from '../components/proposal';
import { ProposalNotFound } from '../components/proposal-not-found';
import { useProposalQuery } from '../__generated__/Proposals';
import { useFetchProposal } from '../components/proposal/proposal-utils';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
const { data: restData, loading: restLoading } = useFetchProposal({
proposalId: params.proposalId,
});
const {
state: { data: restData, loading: restLoading, error: restError },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
const { data, loading, error } = useProposalQuery({
fetchPolicy: 'network-only',
@@ -25,7 +26,7 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer
loading={Boolean(loading || restLoading)}
error={error}
error={error || restError}
data={{
...data,
...(restData ? { restData } : {}),
@@ -2,7 +2,7 @@ import { type ReactNode } from 'react';
import sortBy from 'lodash/sortBy';
import { format, formatDuration, intervalToDuration } from 'date-fns';
import { type MarketViewProposalFieldsFragment } from '@vegaprotocol/proposals';
import { MarketUpdateType, ProposalState } from '@vegaprotocol/types';
import { ProposalState } from '@vegaprotocol/types';
import {
DApp,
TOKEN_PROPOSAL,
@@ -24,16 +24,6 @@ export const MarketUpdateStateBanner = ({
const t = useT();
const governanceLink = useLinks(DApp.Governance);
const openTradingProposals = sortBy(
proposals.filter(
(p) =>
p.terms.change.__typename === 'UpdateMarketState' &&
p.terms.change.updateType ===
MarketUpdateType.MARKET_STATE_UPDATE_TYPE_RESUME
),
(p) => p.terms.enactmentDatetime
);
const openProposals = sortBy(
proposals.filter((p) => p.state === ProposalState.STATE_OPEN),
(p) => p.terms.enactmentDatetime
@@ -55,32 +45,12 @@ export const MarketUpdateStateBanner = ({
? governanceLink(TOKEN_PROPOSAL.replace(':id', openProposals[0]?.id))
: undefined;
const openTradingProposalsLink = openTradingProposals[0]?.id
? governanceLink(TOKEN_PROPOSAL.replace(':id', openTradingProposals[0]?.id))
: undefined;
const proposalsLink =
openProposals.length > 1 ? governanceLink(TOKEN_PROPOSALS) : undefined;
let content: ReactNode;
if (openTradingProposals.length >= 1) {
content = (
<>
<p className="mb-1">
{t(
'Trading on market {{name}} was suspended by governance. There are open proposals to resume trading on this market.',
{ name }
)}
</p>
<p>
<ExternalLink href={openTradingProposalsLink}>
{t('View proposals')}
</ExternalLink>
</p>
</>
);
} else if (passedProposals.length) {
if (passedProposals.length) {
const { date, duration, price } = getMessageVariables(passedProposals[0]);
content = (
<>
+120 -64
View File
@@ -1,100 +1,156 @@
# Trading Market-Sim End-To-End Tests
This directory contains end-to-end tests for the Trading application using Vega-market-sim. This guide will help you set up your environment and run the tests efficiently.
This direcotry contains end-to-end tests for the trading application using vega-market-sim. This README will guide you through setting up your environment and running the tests.
## Prerequisites
Ensure you have the following installed:
- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer)
- [Docker](https://www.docker.com/)
- [Python versions ">=3.9,<3.11"](https://www.python.org/)
- [Poetry](https://python-poetry.org/docs/#installing-with-the-official-installer) for dependency management.
- [Docker](https://www.docker.com/) for running isolated application containers.
- Python, versions ">=3.9,<3.11". Install from the [official Python website](https://www.python.org/).
## Getting Started
## Setup
1. **Install Poetry**: Follow the instructions on the [official Poetry website](https://python-poetry.org/docs/#installing-with-the-official-installer).
2. **Install Docker**: Follow the instructions on the [official Docker website](https://docs.docker.com/desktop/).
3. **Install Python**: Follow the instructions on the [official Python website](https://www.python.org/)
**ensure you install a version between 3.9 and 3.11.**
4. **Start up a Poetry environment**: Execute the commands below to configure the Poetry environment.
### 1. Install Dependencies
- **Poetry**: Follow the installation guide on the [official Poetry website](https://python-poetry.org/docs/#installing-with-the-official-installer).
- **Docker**: Installation instructions are available on the [official Docker website](https://docs.docker.com/desktop/).
- **Python**: Install a version between 3.9 and 3.11, as detailed on the [official Python website](https://www.python.org/).
### 2. Configure Your Environment
Ensure you're in the tests folder before executing commands.
### Ensure you are in the tests folder before running commands
```bash
poetry shell
poetry update vega-sim # Updates to the latest version of the market-sim branch
poetry install
playwright install chromium # Installs necessary browsers for Playwright
```
### 3. Prepare Binaries and Docker Images
5. **Install python dependencies**
Download necessary binaries for the desired Vega version:
To make sure you are on the latest version of our market-sim branch.
```bash
poetry update vega-sim
```
```bash
poetry install
```
6. **Install Playwright Browsers**: Execute the command below to browsers for Playwright.
```bash
playwright install chromium
```
7. **Download necessary binaries**:
Use the following command within your Python environment. The `--force` flag ensures the binaries are overwritten, and the `--version` specifies the desired version. e.g. `v0.73.4`
```bash
python -m vega_sim.tools.load_binaries --force --version $VEGA_VERSION
```
Pull Docker images for your environment:
8. **Pull the desired Docker image**
- **Development**: `docker pull vegaprotocol/trading:develop`
- **Production**: `docker pull vegaprotocol/trading:main`
```bash
docker pull vegaprotocol/trading:develop
```
### 4. Build a Docker Image of Your Locally Built Trading App
9. **Run tests**: Poetry/Python will serve the app from docker
### Update the .env file with the correct trading image.
```bash
poetry run pytest
```
### Docker images
Pull the desired image:
**Testnet**
```bash
docker pull vegaprotocol/trading:develop
```
**Mainnet**
```bash
docker pull vegaprotocol/trading:main
```
Find all available images on [Docker Hub](https://hub.docker.com/r/vegaprotocol/trading/tags).
#### Create a Docker Image of Your Locally Built Trading App
To build your Docker image, use the following commands:
```bash
yarn nx build trading ./docker/prepare-dist.sh
```
```bash
./docker/prepare-dist.sh
docker build -f docker/node-outside-docker.Dockerfile --build-arg APP=trading --build-arg ENV_NAME=stagnet1 -t vegaprotocol/trading:latest .
```
## Running Tests
## Running Tests 🧪
Ensure the Docker daemon is running. Update the `.env` file with the correct trading image before proceeding.
Before running make sure the docker daemon is running.
- **Run all tests**: `poetry run pytest`
- **Run a specific test**: `poetry run pytest -k "test_name" -s --headed`
- **Run tests using your locally served console**:
To run a specific test, use the `-k` option followed by the name of the test.
Run all tests:
In one terminal window, build and serve the trading console:
```bash
yarn nx build trading
yarn nx serve trading
```
Once the console is served, update the `.env` file to set `local_server=true`. You can then run your tests using the same commands as above.
NOTE: Parallel running of tests will not work against locally served console.
## Test Strategy and Container Cleanup
### Strategy
We aim for each test file to use a single Vega instance to ensure test isolation and manage resources efficiently. This approach helps in maintaining test performance and reliability.
### Cleanup Procedure
To ensure proper cleanup of containers after each test, use the following fixture pattern:
```python
@pytest.fixture
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
```bash
poetry run pytest
```
## Running Tests in Parallel
Run a targeted test:
For running tests in parallel:
```bash
poetry run pytest -k "test_name" -s --headed
```
- **Within the e2e folder**: `poetry run pytest -s --numprocesses auto --dist loadfile`
- **From anywhere**: `yarn trading:test:all`
Run from anywhere:
## Troubleshooting
```bash
yarn trading:test -- "test_name" -s --headed
```
If IntelliSense is not working in VSCode, follow these steps:
Run using your locally served console:
1. Find the Poetry environment's Python binary: `poetry run which python`
2. In VSCode, open the command menu (`cmd + shift + p`), search for `Python: Select Interpreter`, select `Enter interpreter path`, and paste the path from step 1.
Within one terminal
```bash
yarn nx build trading
```
```bash
yarn nx serve trading
```
Once console is served you can update the .env file to have local_server to true.
## Running Tests in Parallel 🔢
To run tests in parallel, use the `--numprocesses auto` option. The `--dist loadfile` setting ensures that multiple runners are not assigned to a single test file.
### From within the e2e folder:
```bash
poetry run pytest -s --numprocesses auto --dist loadfile
```
### From anywhere:
```bash
yarn trading:test:all
```
# Things to know
If you "intellisense" isn't working follow these steps:
1. ```bash
poetry run which python
```
2. Then open the command menu in vscode (cmd + shift + p) and type `select interpreter` , press enter, select enter interpreter path press enter then paste in the output from that above command you should get the right python again
-8
View File
@@ -1,8 +0,0 @@
import { ENV, Networks } from '@vegaprotocol/environment';
const TEAMS_STATS_EPOCHS_MAINNET = 30;
const TEAMS_STATS_EPOCHS_TESTNET = 192;
export const TEAMS_STATS_EPOCHS =
ENV.VEGA_ENV === Networks.MAINNET
? TEAMS_STATS_EPOCHS_MAINNET
: TEAMS_STATS_EPOCHS_TESTNET;
+3 -2
View File
@@ -7,7 +7,8 @@ import orderBy from 'lodash/orderBy';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useEpochInfoQuery } from './__generated__/Epoch';
import { type ApolloError } from '@apollo/client';
import { TEAMS_STATS_EPOCHS } from './constants';
const TAKE_EPOCHS = 30; // TODO: should this be DEFAULT_AGGREGATION_EPOCHS?
const findTeam = (entities: GameFieldsFragment['entities'], teamId: string) => {
const team = entities.find(
@@ -44,7 +45,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
let from = epochFrom;
if (!from && epochData) {
from = Number(epochData.epoch.id) - TEAMS_STATS_EPOCHS;
from = Number(epochData.epoch.id) - TAKE_EPOCHS;
if (from < 1) from = 1; // make sure it's not negative
}
+2 -2
View File
@@ -5,7 +5,7 @@ import {
type TeamRefereeFieldsFragment,
type TeamMemberStatsFieldsFragment,
} from './__generated__/Team';
import { TEAMS_STATS_EPOCHS } from './constants';
import { DEFAULT_AGGREGATION_EPOCHS } from './use-teams';
export type Team = TeamFieldsFragment;
export type TeamStats = TeamStatsFieldsFragment;
@@ -22,7 +22,7 @@ export const useTeam = (teamId?: string, partyId?: string) => {
variables: {
teamId: teamId || '',
partyId,
aggregationEpochs: TEAMS_STATS_EPOCHS,
aggregationEpochs: DEFAULT_AGGREGATION_EPOCHS,
},
skip: !teamId,
fetchPolicy: 'cache-and-network',
+4 -2
View File
@@ -4,7 +4,9 @@ import { useTeamsQuery } from './__generated__/Teams';
import { useTeamsStatisticsQuery } from './__generated__/TeamsStatistics';
import compact from 'lodash/compact';
import { type TeamStatsFieldsFragment } from './__generated__/Team';
import { TEAMS_STATS_EPOCHS } from './constants';
// 192
export const DEFAULT_AGGREGATION_EPOCHS = 192;
const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = {
totalQuantumVolume: '0',
@@ -14,7 +16,7 @@ const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = {
quantumRewards: [],
};
export const useTeams = (aggregationEpochs = TEAMS_STATS_EPOCHS) => {
export const useTeams = (aggregationEpochs = DEFAULT_AGGREGATION_EPOCHS) => {
const {
data: teamsData,
loading: teamsLoading,
@@ -71,7 +71,7 @@ function waitForStake(vegaPublicKey: string) {
let tick = 1;
const interval = setInterval(async () => {
log(`confirming stake (attempt: ${tick})`);
if (tick >= 90) {
if (tick >= 30) {
clearInterval(interval);
reject(new Error('stake link never seen'));
}
@@ -25,7 +25,7 @@ export const addCreateMarket = () => {
cy.highlight('creating market on capsule environment');
cy.wrap(createMarket(config), {
timeout: 9 * 60 * 1000,
timeout: 5 * 60 * 1000,
})
// register market list result so it can be retrieved in tests later
.as('markets');
@@ -111,7 +111,7 @@ export const addValidatorsSelfDelegate = () => {
String(node0ApiToken),
String(node0Id)
),
{ timeout: 90000 }
{ timeout: 60000 }
);
// Self delegating Node 1 wallet
cy.get('@node1PubKey').then((node1PubKey) => {
@@ -124,7 +124,7 @@ export const addValidatorsSelfDelegate = () => {
String(node1ApiToken),
String(node1Id)
),
{ timeout: 90000 }
{ timeout: 60000 }
);
});
});
-1
View File
@@ -175,7 +175,6 @@
},
"network_parameters": {
"blockchains.ethereumConfig": "{\"network_id\": \"{{ .NetworkID }}\", \"chain_id\": \"{{ .ChainID }}\", \"collateral_bridge_contract\": { \"address\": \"{{.GetEthContractAddr "erc20_bridge_1"}}\" }, \"confirmations\": 3, \"staking_bridge_contract\": { \"address\": \"{{.GetEthContractAddr "staking_bridge"}}\", \"deployment_block_height\": 0}, \"token_vesting_contract\": { \"address\": \"{{.GetEthContractAddr "erc20_vesting"}}\", \"deployment_block_height\": 0 }, \"multisig_control_contract\": { \"address\": \"{{.GetEthContractAddr "MultisigControl"}}\", \"deployment_block_height\": 0 }}",
"blockchains.ethereumRpcAndEvmCompatDataSourcesConfig": "{\"configs\": []}",
"governance.proposal.asset.minClose": "2s",
"governance.proposal.asset.minEnact": "2s",
"governance.proposal.asset.requiredParticipation": "0.00000000000000000000000015",