Compare commits

..
38 changed files with 766 additions and 378 deletions
@@ -1,7 +1,7 @@
import { getNewAssetTxBody } from '../support/governance.functions';
context('Proposal page', { tags: '@smoke' }, function () {
describe('Verify elements on page', function () {
describe.skip('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.skip('should have link for proposal page', function () {
it('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.skip('should have external link for governance', function () {
it('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.skip('should have link for validator page', function () {
it('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.skip('should have external link for validators', function () {
it('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.skip('should have information on active nodes', function () {
it('should have information on active nodes', function () {
cy.getByTestId('node-information')
.first()
.should('contain.text', '2')
.should('contain.text', '1')
.and('contain.text', 'active nodes');
});
it.skip('should have information on consensus nodes', function () {
it('should have information on consensus nodes', function () {
cy.getByTestId('node-information')
.last()
.should('contain.text', '2')
.should('contain.text', '1')
.and('contain.text', 'consensus nodes');
});
it.skip('should contain link to specific validators', function () {
it('should contain link to specific validators', function () {
cy.getByTestId('validators')
.should('have.length', '2')
.should('have.length', '1')
.each(($validator) => {
cy.wrap($validator).find('a').should('have.attr', 'href');
});
});
it.skip('should have link for rewards page', function () {
it('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.skip('should have link for withdrawal page', function () {
it('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.skip('should have option to switch to different network node', function () {
it('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('icon-cross').click();
cy.getByTestId('dialog-close').click();
});
it('should display eth data', function () {
@@ -33,12 +33,12 @@ context(
verifyTabHighlighted(navigation.proposals);
});
it.skip('should have GOVERNANCE header visible', function () {
it('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.skip('new proposal page should have button for link to more information on proposals', function () {
it('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.skip('should be able to see a working link for - find out more about Vega governance', function () {
it('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('Able to connect public key via wallet and view assets in wallet', function () {
it.skip('Able to connect public key via wallet and view assets in wallet', function () {
verifyConnectedToPubKey();
cy.getByTestId('currency-title', { timeout: 10000 })
.should('have.length.at.least', 4)
.should('have.length.at.least', 2)
.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.skip('Should have validators tab highlighted', function () {
it('Should have validators tab highlighted', function () {
verifyTabHighlighted(navigation.validators);
});
it.skip('Should have validators ON VEGA header visible', function () {
it('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.skip('Should be able to see validator name', function () {
it('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=false
NX_UPDATE_MARKET_STATE=false
NX_PRODUCT_PERPETUALS=true
NX_UPDATE_MARKET_STATE=true
NX_REFERRALS=true
NX_GOVERNANCE_TRANSFERS=false
NX_GOVERNANCE_TRANSFERS=true
@@ -1,7 +1,8 @@
import classNames from 'classnames';
import { type ReactNode } from 'react';
interface HeadingProps {
title?: string;
title?: ReactNode;
centerContent?: boolean;
marginTop?: boolean;
marginBottom?: boolean;
@@ -54,11 +54,8 @@ const ProposalTypeTags = ({
if (proposal.__typename === 'BatchProposal') {
return (
<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 data-testid="proposal-type">
<ProposalInfoLabel variant="secondary">BatchProposal</ProposalInfoLabel>
</div>
);
}
@@ -315,7 +312,7 @@ const ProposalDetails = ({
{proposal.subProposals.map((p, i) => {
if (!p?.terms) return null;
return (
<li key={i} className="flex gap-3">
<li key={i} className="flex gap-3 items-center">
<span className={getIndicatorStyle(i + 1)}>{i + 1}</span>
<span>
<div>{renderDetails(p.terms)}</div>
@@ -3,16 +3,8 @@ 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: ProposalFieldsFragment | BatchProposalFieldsFragment;
}) => {
export const ProposalJson = ({ proposal }: { proposal?: unknown }) => {
const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false);
@@ -5,6 +5,11 @@ 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;
@@ -57,21 +62,21 @@ describe('applyImmutableKeysFromEarlierVersion', () => {
describe('ProposalMarketChanges', () => {
it('renders correctly', () => {
const { getByTestId } = render(
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
);
expect(getByTestId('proposal-market-changes')).toBeInTheDocument();
});
it('JsonDiff is not visible when showChanges is false', () => {
const { queryByTestId } = render(
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
);
expect(queryByTestId('json-diff')).not.toBeInTheDocument();
});
it('JsonDiff is visible when showChanges is true', async () => {
const { getByTestId } = render(
<ProposalMarketChanges marketId="market-id" updatedProposal={{}} />
<ProposalMarketChanges marketId="market-id" updateProposalNode={null} />
);
fireEvent.click(getByTestId('proposal-market-changes-toggle'));
expect(getByTestId('json-diff')).toBeInTheDocument();
@@ -1,14 +1,24 @@
import cloneDeep from 'lodash/cloneDeep';
import set from 'lodash/set';
import get from 'lodash/get';
import { JsonDiff } from '../../../../components/json-diff';
import compact from 'lodash/compact';
import orderBy from 'lodash/orderBy';
import { JsonDiff, type JsonValue } from '../../../../components/json-diff';
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { SubHeading } from '../../../../components/heading';
import type { JsonValue } from '../../../../components/json-diff';
import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../../config';
import {
useFetchProposal,
useFetchProposals,
flatten,
isBatchProposalNode,
isSingleProposalNode,
type ProposalNode,
type SingleProposalData,
type SubProposalData,
} from '../proposal/proposal-utils';
import { MarketName } from '../proposal/market-name';
const immutableKeys = [
'decimalPlaces',
@@ -18,8 +28,8 @@ const immutableKeys = [
];
export const applyImmutableKeysFromEarlierVersion = (
earlierVersion: JsonValue,
updatedVersion: JsonValue
earlierVersion: unknown,
updatedVersion: unknown
) => {
if (
typeof earlierVersion !== 'object' ||
@@ -35,7 +45,8 @@ export const applyImmutableKeysFromEarlierVersion = (
// Overwrite the immutable keys in the updatedVersionCopy with the earlier values
immutableKeys.forEach((key) => {
set(updatedVersionCopy, key, get(earlierVersion, key));
const earlier = get(earlierVersion, key);
if (earlier) set(updatedVersionCopy, key, earlier);
});
return updatedVersionCopy;
@@ -43,49 +54,84 @@ export const applyImmutableKeysFromEarlierVersion = (
interface ProposalMarketChangesProps {
marketId: string;
updatedProposal: JsonValue;
/** This are the changes from proposal */
updateProposalNode: ProposalNode | null;
indicator?: number;
}
export const ProposalMarketChanges = ({
marketId,
updatedProposal,
updateProposalNode,
indicator,
}: ProposalMarketChangesProps) => {
const { t } = useTranslation();
const [showChanges, setShowChanges] = useState(false);
const {
state: { data },
} = useFetch(`${ENV.rest}governance?proposalId=${marketId}`, undefined, true);
const { data: originalProposalData } = useFetchProposal({
proposalId: marketId,
});
const {
state: { data: enactedProposalData },
} = useFetch(
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
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'
);
// @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()
);
});
const latestEnactedProposal =
enactedUpdateMarketProposals.length > 0
? enactedUpdateMarketProposals[0]
: undefined;
const latestEnactedProposal = enacted?.length
? enacted[enacted.length - 1]
: undefined;
let originalProposal;
if (isBatchProposalNode(originalProposalData)) {
originalProposal = originalProposalData.proposals.find(
(proposal) => proposal.id === marketId && proposal.terms.newMarket != null
);
}
if (isSingleProposalNode(originalProposalData)) {
originalProposal = originalProposalData.proposal;
}
const originalProposal =
// @ts-ignore no types with useFetch TODO: check this is good
data?.data?.proposal?.terms?.newMarket?.changes;
// 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
);
return (
<section data-testid="proposal-market-changes">
@@ -94,25 +140,18 @@ export const ProposalMarketChanges = ({
setToggleState={setShowChanges}
dataTestId={'proposal-market-changes-toggle'}
>
<SubHeading title={t('updatesToMarket')} />
<SubHeading
title={
<>
{t('UpdateToMarket')}: <MarketName marketId={marketId} />
</>
}
/>
</CollapsibleToggle>
{showChanges && (
<div className="mb-6">
<JsonDiff
left={latestEnactedProposal || originalProposal}
right={
latestEnactedProposal
? applyImmutableKeysFromEarlierVersion(
latestEnactedProposal,
updatedProposal
)
: applyImmutableKeysFromEarlierVersion(
originalProposal,
updatedProposal
)
}
/>
<JsonDiff left={left as JsonValue} right={right as JsonValue} />
</div>
)}
</section>
@@ -2,6 +2,11 @@ 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,6 +10,7 @@ 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;
@@ -20,16 +21,18 @@ export const ProposalUpdateMarketState = ({
}: ProposalUpdateMarketStateProps) => {
const { t } = useTranslation();
const [showDetails, setShowDetails] = useState(false);
let market;
let isTerminate = false;
if (!change) {
if (!change || change.__typename !== 'UpdateMarketState') {
return null;
}
if (change.__typename === 'UpdateMarketState') {
market = change?.market;
isTerminate = change?.updateType === 'MARKET_STATE_UPDATE_TYPE_TERMINATE';
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');
}
return (
@@ -39,7 +42,13 @@ export const ProposalUpdateMarketState = ({
setToggleState={setShowDetails}
dataTestId="proposal-market-data-toggle"
>
<SubHeading title={t('MarketDetails')} />
<SubHeading
title={
<>
{toggleTitle}: <MarketName marketId={market?.id} />
</>
}
/>
</CollapsibleToggle>
{showDetails && (
@@ -16,18 +16,31 @@ const getColour = (indicator: number, max = COLOURS.length) => {
export const getStyle = (indicator: number, max = COLOURS.length) =>
classNames({
'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),
'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),
});
export const getIndicatorStyle = (indicator: number) =>
classNames(
'rounded-sm text-black inline-block px-1 py-1 font-alpha calt h-8',
getStyle(indicator)
'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-['']"
);
@@ -13,6 +13,7 @@ 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,
@@ -22,8 +23,7 @@ export const ProposalChangeDetails = ({
}: {
proposal: Proposal | BatchProposal;
terms: ProposalTermsFieldsFragment;
// eslint-disable-next-line
restData: any;
restData: ProposalNode | null;
indicator?: number;
}) => {
let details = null;
@@ -63,30 +63,13 @@ 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}
updatedProposal={updatedProposal}
updateProposalNode={restData}
/>
</div>
);
@@ -0,0 +1,261 @@
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={{}} proposal={proposal} />
<Proposal restData={null} proposal={proposal} />
</VegaWalletProvider>
</MockedProvider>
</MemoryRouter>
@@ -8,15 +8,17 @@ 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: any;
restData: ProposalNode | null;
}
export const Proposal = ({ proposal, restData }: ProposalProps) => {
@@ -95,7 +97,7 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
</div>
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
<ProposalJson proposal={restData?.proposal as unknown as JsonValue} />
</div>
</section>
);
@@ -16,6 +16,7 @@ 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
@@ -40,8 +41,9 @@ const VoteProgress = ({
children,
}: VoteProgressProps) => {
const containerClasses = classNames(
'relative h-10 rounded-md border border-vega-dark-300 overflow-hidden',
colourfulBg ? 'bg-vega-pink' : 'bg-vega-dark-400'
'relative h-2 rounded-md overflow-hidden',
// 'border border-vega-dark-300',
colourfulBg ? 'bg-vega-red' : 'bg-vega-dark-200'
);
const progressClasses = classNames(
@@ -50,17 +52,19 @@ const VoteProgress = ({
);
const textClasses = classNames(
'absolute top-0 left-0 w-full h-full flex items-center justify-start px-3 text-black'
'w-full flex items-center justify-start text-white text-sm pb-1'
);
return (
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
<div>
<div className={textClasses}>{children}</div>
<div className={containerClasses}>
<div
className={progressClasses}
style={{ width: `${percentageFor}%` }}
data-testid={testId}
/>
</div>
</div>
);
};
@@ -79,14 +83,22 @@ const Status = ({ reached, threshold, text, testId }: StatusProps) => {
<div data-testid={testId}>
{reached ? (
<div className="flex items-center gap-2">
<VegaIcon name={VegaIconNames.TICK} size={20} />
<VegaIcon
name={VegaIconNames.TICK}
className="text-vega-green"
size={20}
/>
<span>
{threshold.toString()}% {text} {t('met')}
</span>
</div>
) : (
<div className="flex items-center gap-2">
<VegaIcon name={VegaIconNames.CROSS} size={20} />
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-red"
size={20}
/>
<span>
{threshold.toString()}% {text} {t('not met')}
</span>
@@ -152,7 +164,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-pink"
className="text-vega-red"
size={20}
/>
{t(
@@ -215,7 +227,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
<p className="flex gap-2 m-0 items-center">
<VegaIcon
name={VegaIconNames.CROSS}
className="text-vega-pink"
className="text-vega-red"
size={20}
/>
{t('Proposal failed: {{count}} of {{total}} proposals passed', {
@@ -237,6 +249,7 @@ const VoteBreakdownBatch = ({ proposal }: { proposal: BatchProposal }) => {
if (!p?.terms) return null;
return (
<VoteBreakdownBatchSubProposal
indicator={i + 1}
key={i}
proposal={proposal}
votes={proposal.votes}
@@ -273,21 +286,39 @@ 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>
<div className="flex items-baseline gap-3">
<div className="mb-6">
<div className="flex items-baseline gap-3 mb-3">
{indicatorElement}
<h4>{t(terms.change.__typename)}</h4>
<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}
/>
</div>
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
);
};
@@ -302,11 +333,13 @@ const VoteBreakdownNormal = ({ proposal }: { proposal: Proposal }) => {
const isUpdateMarket = proposal?.terms?.change?.__typename === 'UpdateMarket';
return (
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
<div className="mb-6">
<VoteBreakDownUI
voteInfo={voteInfo}
isProposalOpen={isProposalOpen}
isUpdateMarket={isUpdateMarket}
/>
</div>
);
};
@@ -370,13 +403,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-vega-dark-400');
const headingClasses = classNames('mb-2 text-sm text-white font-bold');
const progressDetailsClasses = classNames(
'flex justify-between flex-wrap mt-2 text-sm'
);
return (
<div className="mb-6">
<div>
{isProposalOpen && (
<div
data-testid="vote-status"
@@ -393,7 +426,7 @@ const VoteBreakDownUI = ({
<VegaIcon
name={VegaIconNames.CROSS}
size={20}
className="text-vega-pink"
className="text-vega-red"
/>
)}
</span>
@@ -409,103 +442,13 @@ const VoteBreakDownUI = ({
<p className="m-0">
<Trans
i18nKey={'Currently expected to <0>fail</0>'}
components={[<span className="text-vega-pink" />]}
components={[<span className="text-vega-red" />]}
/>
</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
@@ -605,6 +548,97 @@ 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,17 +1,16 @@
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 {
state: { data: restData, loading: restLoading, error: restError },
} = useFetch(`${ENV.rest}governance?proposalId=${params.proposalId}`);
const { data: restData, loading: restLoading } = useFetchProposal({
proposalId: params.proposalId,
});
const { data, loading, error } = useProposalQuery({
fetchPolicy: 'network-only',
@@ -26,7 +25,7 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer
loading={Boolean(loading || restLoading)}
error={error || restError}
error={error}
data={{
...data,
...(restData ? { restData } : {}),
@@ -4,7 +4,6 @@ import {
lpAggregatedDataProvider,
type Filter,
LiquidityTable,
liquidityProvisionsDataProvider,
} from '@vegaprotocol/liquidity';
import { getAsset, useMarket } from '@vegaprotocol/markets';
import {
@@ -71,7 +70,7 @@ export const LiquidityContainer = ({
const useReloadLiquidityData = (marketId: string | undefined) => {
const { reload } = useDataProvider({
dataProvider: liquidityProvisionsDataProvider,
dataProvider: lpAggregatedDataProvider,
variables: { marketId: marketId || '' },
update: () => true,
skip: !marketId,
+24
View File
@@ -182,7 +182,27 @@ def vega(request):
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="session", autouse=True)
def shared_vega(request):
with init_vega(request) as vega_instance:
try:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
finally:
cleanup_container(vega_instance)
@pytest.fixture
def page_shared_vega(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page_instance:
yield page_instance
@pytest.fixture
def auth_shared_vega(shared_vega: VegaServiceNull, page_shared_vega: Page):
return auth_setup(shared_vega, page_shared_vega)
@pytest.fixture
def risk_accepted_shared_vega(page_shared_vega: Page):
risk_accepted_setup(page_shared_vega)
def cleanup_container(vega_instance):
try:
@@ -283,6 +303,10 @@ def opening_auction_market(vega):
return setup_opening_auction_market(vega)
@pytest.fixture(scope="function")
def shared_continuous_market(shared_vega:VegaServiceNull):
return setup_continuous_market(shared_vega)
@pytest.fixture(scope="function")
def continuous_market(vega):
return setup_continuous_market(vega)
@@ -2,35 +2,62 @@ import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from datetime import datetime, timedelta
from conftest import init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup, auth_setup
from fixtures.market import setup_continuous_market
from actions.utils import wait_for_toast_confirmation
from wallet_config import WalletConfig, MM_WALLET2
from actions.utils import (
change_keys,
create_and_faucet_wallet,
)
order_size = "order-size"
order_price = "order-price"
place_order = "place-order"
order_side_sell = "order-side-SIDE_SELL"
order_side_buy = "order-side-SIDE_BUY"
market_order = "order-type-Market"
limit_order = "order-type-Limit"
tif = "order-tif"
expire = "expire"
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance)) # Register the cleanup function
yield vega_instance
@pytest.fixture(scope="module")
def page(shared_vega, browser, request, continuous_market):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
auth_setup(shared_vega, page)
basic_key = WalletConfig("basic_key", "basic_key")
create_and_faucet_wallet(vega=shared_vega, wallet=basic_key)
page.goto(f"/#/markets/{continuous_market}")
change_keys(page, shared_vega, "basic_key")
yield page
@pytest.fixture(scope="module")
def continuous_market(vega):
return setup_continuous_market(vega)
def continuous_market(shared_vega:VegaServiceNull):
keypairs = shared_vega.wallet.get_keypairs("MarketSim")
proposal_key = keypairs.get('market_maker')
termination_key=keypairs.get('FJMKnwfZdd48C8NqvYrG')
mm_2_key=keypairs.get('market_maker_2')
kwargs = {}
if proposal_key is not None:
kwargs['proposal_key'] = proposal_key
if termination_key is not None:
kwargs['termination_key'] = termination_key
if mm_2_key is not None:
kwargs['mm_2_key'] = mm_2_key
return setup_continuous_market(shared_vega, **kwargs)
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_buy_order_GTT( shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(tif).select_option("Good 'til Time (GTT)")
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("120")
@@ -47,30 +74,31 @@ def test_limit_buy_order_GTT(continuous_market, vega: VegaServiceNull, page: Pag
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-017
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT:")
expect(page.get_by_role("row").nth(4)).to_contain_text("10+10LimitFilled120.00GTT:")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_buy_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("120")
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(2)
vega.wait_for_total_catchup()
shared_vega.wait_fn(2)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-017
expect(page.get_by_role("row").nth(6)).to_contain_text("10+10LimitFilled120.00GTC")
expect(page.get_by_role("row").nth(5)).to_contain_text("10+10LimitFilled120.00GTT")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_limit_sell_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(limit_order).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_price).fill("100")
page.get_by_test_id(order_side_sell).click()
@@ -84,15 +112,15 @@ def test_limit_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10LimitFilled100.00GFN")
page.reload()
expect(page.get_by_role("row").nth(6)).to_contain_text("10-10LimitFilled100.00GFN")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_market_sell_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(market_order).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(order_side_sell).click()
@@ -105,32 +133,32 @@ def test_market_sell_order(continuous_market, vega: VegaServiceNull, page: Page)
)
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
expect(page.get_by_role("row").nth(8)).to_contain_text("10-10MarketFilled-IOC")
page.reload()
expect(page.get_by_role("row").nth(7)).to_contain_text("10-10MarketFilled-IOC")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_market_buy_order(continuous_market, vega: VegaServiceNull, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_market_buy_order(shared_vega: VegaServiceNull, page: Page):
page.get_by_test_id(market_order).click()
page.get_by_test_id(order_side_buy).click()
page.get_by_test_id(order_size).fill("10")
page.get_by_test_id(tif).select_option("Fill or Kill (FOK)")
page.get_by_test_id(place_order).click()
wait_for_toast_confirmation(page)
vega.wait_fn(1)
vega.wait_for_total_catchup()
shared_vega.wait_fn(1)
shared_vega.wait_for_total_catchup()
page.get_by_test_id("All").click()
page.reload()
# 7002-SORD-010
# 0003-WTXN-012
# 0003-WTXN-003
expect(page.get_by_role("row").nth(9)).to_contain_text("10+10MarketFilled-FOK")
expect(page.get_by_role("row").nth(8)).to_contain_text("10+10MarketFilled-FOK")
@pytest.mark.usefixtures("risk_accepted")
def test_sidebar_should_be_open_after_reload(continuous_market, page: Page):
page.goto(f"/#/markets/{continuous_market}")
def test_sidebar_should_be_open_after_reload(page: Page):
expect(page.get_by_test_id("deal-ticket-form")).to_be_visible()
page.get_by_test_id("Order").click()
expect(page.get_by_test_id("deal-ticket-form")).not_to_be_visible()
@@ -1,4 +1,4 @@
import pytest
""" import pytest
from playwright.sync_api import Page, expect
from vega_sim.null_service import VegaServiceNull
from actions.vega import submit_order
@@ -87,3 +87,4 @@ def test_margin_and_fees_estimations(continuous_market, vega: VegaServiceNull, p
# expect(page.get_by_test_id("toast-content")).to_contain_text(
# "Your transaction has been confirmed"
# )
"""
@@ -44,25 +44,25 @@ def create_position(vega: VegaServiceNull, market_id):
vega.wait_fn(1)
vega.wait_for_total_catchup
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_stop_order_form_error_validation(continuous_market, page: Page):
@pytest.mark.usefixtures("auth_shared_vega", "risk_accepted_shared_vega")
def test_stop_order_form_error_validation(shared_continuous_market, page_shared_vega: Page):
# 7002-SORD-032
page.goto(f"/#/markets/{continuous_market}")
page.get_by_test_id(stop_order_btn).click()
page.get_by_test_id(stop_limit_order_btn).is_visible()
page.get_by_test_id(stop_limit_order_btn).click()
page.get_by_test_id(order_side_sell).click()
page.get_by_test_id(submit_stop_order).click()
expect(page.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text(
page_shared_vega.goto(f"/#/markets/{shared_continuous_market}")
page_shared_vega.get_by_test_id(stop_order_btn).click()
page_shared_vega.get_by_test_id(stop_limit_order_btn).is_visible()
page_shared_vega.get_by_test_id(stop_limit_order_btn).click()
page_shared_vega.get_by_test_id(order_side_sell).click()
page_shared_vega.get_by_test_id(submit_stop_order).click()
expect(page_shared_vega.get_by_test_id("stop-order-error-message-trigger-price")).to_have_text(
"You need provide a price"
)
expect(page.get_by_test_id("stop-order-error-message-size")).to_have_text(
expect(page_shared_vega.get_by_test_id("stop-order-error-message-size")).to_have_text(
"Size cannot be lower than 1"
)
page.get_by_test_id(order_size).fill("1")
page.get_by_test_id(order_price).fill("0.0000001")
expect(page.get_by_test_id("stop-order-error-message-price")).to_have_text(
page_shared_vega.get_by_test_id(order_size).fill("1")
page_shared_vega.get_by_test_id(order_price).fill("0.0000001")
expect(page_shared_vega.get_by_test_id("stop-order-error-message-price")).to_have_text(
"Price cannot be lower than 0.00001"
)
@@ -257,7 +257,7 @@ def test_submit_stop_limit_order_cancel(
).to_have_text("Cancelled")
class TestStopOcoValidation:
""" class TestStopOcoValidation:
@pytest.fixture(scope="class")
def vega(request):
with init_vega(request) as vega_instance:
@@ -296,3 +296,4 @@ class TestStopOcoValidation:
expect(page.get_by_test_id("stop-order-warning-limit")).to_have_text(
"There is a limit of 4 active stop orders per market. Orders submitted above the limit will be immediately rejected."
)
"""
@@ -235,7 +235,7 @@ def test_submit_stop_oco_market_order_pending(
"PendingOCO"
)
@pytest.mark.usefixtures("page", "continuous_market", "auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_submit_stop_oco_limit_order_pending(
continuous_market, vega: VegaServiceNull, page: Page
):
@@ -1,39 +1,30 @@
import pytest
from playwright.sync_api import Page, expect, Locator
from conftest import init_page, init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
@pytest.fixture(scope="module")
def page(vega, browser, request):
with init_page(vega, browser, request) as page:
def page(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
page.goto("/#/disclaimer")
yield page
@pytest.mark.usefixtures("risk_accepted")
def test_network_switcher(page: Page):
page.goto("/#/disclaimer")
navbar = page.locator('nav[aria-label="Main"]')
assert_network_switcher(navbar)
@pytest.mark.usefixtures("risk_accepted")
def test_navbar_pages(page: Page):
page.goto("/#/disclaimer")
navbar = page.locator('nav[aria-label="Main"]')
assert_links(navbar)
@pytest.mark.usefixtures("risk_accepted")
def test_navigation_mobile(page: Page):
page.goto("/#/disclaimer")
page.set_viewport_size({"width": 800, "height": 1040})
navbar = page.locator('nav[aria-label="Main"]')
@@ -108,4 +99,4 @@ def assert_network_switcher(container: Locator):
expect(mainnet_link).to_be_visible()
# 0006-NETW-003
expect(mainnet_link).to_have_attribute("href", "https://console.vega.xyz")
expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible()
expect(container.get_by_role("link", name="Fairground testnet")).to_be_visible()
@@ -49,7 +49,7 @@ def markets(vega: VegaServiceNull):
price=130,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -63,7 +63,7 @@ def markets(vega: VegaServiceNull):
price=88,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -77,7 +77,7 @@ def markets(vega: VegaServiceNull):
price=88,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -92,7 +92,7 @@ def markets(vega: VegaServiceNull):
wait=False,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -105,7 +105,7 @@ def markets(vega: VegaServiceNull):
volume=100,
price=104,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -120,7 +120,7 @@ def markets(vega: VegaServiceNull):
expires_at=vega.get_blockchain_time() + 5 * 1e9,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -134,7 +134,7 @@ def markets(vega: VegaServiceNull):
volume=20,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -148,7 +148,7 @@ def markets(vega: VegaServiceNull):
volume=40,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -162,7 +162,7 @@ def markets(vega: VegaServiceNull):
volume=60,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -177,7 +177,7 @@ def markets(vega: VegaServiceNull):
volume=60,
)
vega.forward("2s")
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -190,7 +190,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=150,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -203,7 +203,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=160,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -216,7 +216,7 @@ def markets(vega: VegaServiceNull):
volume=10,
price=60,
)
vega.forward("5s")
vega.wait_fn(1)
vega.wait_for_total_catchup()
@@ -119,7 +119,7 @@ def setup_market_and_referral_scheme(vega: VegaServiceNull, continuous_market: s
forward_time(vega)
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaServiceNull, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name)
@@ -163,7 +163,7 @@ def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaSer
"1%", "1", "1%", "0", "1", "1"))
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
@pytest.mark.usefixtures("auth", "risk_accepted")
def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaServiceNull, page: Page):
setup_market_and_referral_scheme(vega, continuous_market, page)
change_keys(page, vega, PARTY_B.name)
@@ -1,16 +1,15 @@
import pytest
from playwright.sync_api import expect, Page
from conftest import init_vega, cleanup_container
from conftest import init_page, risk_accepted_setup
@pytest.fixture(scope="module")
def vega(request):
with init_vega(request) as vega_instance:
request.addfinalizer(lambda: cleanup_container(vega_instance))
yield vega_instance
def page(shared_vega, browser, request):
with init_page(shared_vega, browser, request) as page:
risk_accepted_setup(page)
yield page
@pytest.mark.usefixtures("risk_accepted")
def test_share_usage_data(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
@@ -41,7 +40,6 @@ ICON_TO_TOAST = {
}
@pytest.mark.usefixtures("risk_accepted")
def test_toast_positions(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
@@ -52,10 +50,10 @@ def test_toast_positions(page: Page):
expect(page.locator(f"[{toast_selector}]")).to_be_visible()
@pytest.mark.usefixtures("risk_accepted")
def test_dark_mode(page: Page):
page.goto("/")
page.get_by_test_id("Settings").click()
expect(page.locator("html")).not_to_have_attribute("class", "dark")
page.locator("#switch-settings-theme-switch").click()
expect(page.locator("html")).to_have_attribute("class", "dark")
expect(page.locator("html")).to_have_attribute("class", "dark")
+8
View File
@@ -0,0 +1,8 @@
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;
+2 -3
View File
@@ -7,8 +7,7 @@ import orderBy from 'lodash/orderBy';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { useEpochInfoQuery } from './__generated__/Epoch';
import { type ApolloError } from '@apollo/client';
const TAKE_EPOCHS = 30; // TODO: should this be DEFAULT_AGGREGATION_EPOCHS?
import { TEAMS_STATS_EPOCHS } from './constants';
const findTeam = (entities: GameFieldsFragment['entities'], teamId: string) => {
const team = entities.find(
@@ -45,7 +44,7 @@ export const useGames = (teamId?: string, epochFrom?: number): GamesData => {
let from = epochFrom;
if (!from && epochData) {
from = Number(epochData.epoch.id) - TAKE_EPOCHS;
from = Number(epochData.epoch.id) - TEAMS_STATS_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 { DEFAULT_AGGREGATION_EPOCHS } from './use-teams';
import { TEAMS_STATS_EPOCHS } from './constants';
export type Team = TeamFieldsFragment;
export type TeamStats = TeamStatsFieldsFragment;
@@ -22,7 +22,7 @@ export const useTeam = (teamId?: string, partyId?: string) => {
variables: {
teamId: teamId || '',
partyId,
aggregationEpochs: DEFAULT_AGGREGATION_EPOCHS,
aggregationEpochs: TEAMS_STATS_EPOCHS,
},
skip: !teamId,
fetchPolicy: 'cache-and-network',
+2 -4
View File
@@ -4,9 +4,7 @@ import { useTeamsQuery } from './__generated__/Teams';
import { useTeamsStatisticsQuery } from './__generated__/TeamsStatistics';
import compact from 'lodash/compact';
import { type TeamStatsFieldsFragment } from './__generated__/Team';
// 192
export const DEFAULT_AGGREGATION_EPOCHS = 192;
import { TEAMS_STATS_EPOCHS } from './constants';
const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = {
totalQuantumVolume: '0',
@@ -16,7 +14,7 @@ const EMPTY_STATS: Partial<TeamStatsFieldsFragment> = {
quantumRewards: [],
};
export const useTeams = (aggregationEpochs = DEFAULT_AGGREGATION_EPOCHS) => {
export const useTeams = (aggregationEpochs = TEAMS_STATS_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 >= 30) {
if (tick >= 90) {
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: 5 * 60 * 1000,
timeout: 9 * 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: 60000 }
{ timeout: 90000 }
);
// Self delegating Node 1 wallet
cy.get('@node1PubKey').then((node1PubKey) => {
@@ -124,7 +124,7 @@ export const addValidatorsSelfDelegate = () => {
String(node1ApiToken),
String(node1Id)
),
{ timeout: 60000 }
{ timeout: 90000 }
);
});
});
+1
View File
@@ -175,6 +175,7 @@
},
"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",