Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo
This commit is contained in:
@@ -106,7 +106,7 @@ jobs:
|
||||
- name: Checkout frontend mono repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
ref: master
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install root dependencies
|
||||
|
||||
@@ -64,20 +64,21 @@ context('Node switcher', function () {
|
||||
cy.getByTestId('node-url-custom').click();
|
||||
|
||||
cy.getByTestId(customNodeBtn).within(() => {
|
||||
cy.get('input').clear().type('https://n04.d.vega.xyz/query');
|
||||
cy.get('input').clear().type('https://api.token.vega.xyz/query');
|
||||
cy.getByTestId('link').click();
|
||||
});
|
||||
validateNodeError(errorTypeTxt, nodeErrorTxt);
|
||||
});
|
||||
|
||||
afterEach('Close node switcher', function () {
|
||||
cy.getByTestId(closeDialogBtn).click();
|
||||
cy.getByTestId(closeDialogBtn).should('be.visible').click();
|
||||
});
|
||||
|
||||
function validateNodeError(errortype, errorMsg) {
|
||||
cy.getByTestId(nodeErrorType).should('have.text', errortype, {
|
||||
timeout: 1000,
|
||||
});
|
||||
cy.getByTestId(nodeErrorType, { timeout: 10000 }).should(
|
||||
'have.text',
|
||||
errortype
|
||||
);
|
||||
cy.getByTestId(nodeErrorMsg).should('have.text', errorMsg);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { BrowserTracing } from '@sentry/tracing';
|
||||
import { StrictMode } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import './styles.css';
|
||||
|
||||
import App from './app/app';
|
||||
import { ENV } from './app/config/env';
|
||||
import { StrictMode } from 'react';
|
||||
|
||||
const { dsn } = ENV;
|
||||
|
||||
@@ -19,12 +19,13 @@ if (dsn) {
|
||||
environment: ENV.envName,
|
||||
});
|
||||
}
|
||||
const rootElement = document.getElementById('root');
|
||||
const root = rootElement && createRoot(rootElement);
|
||||
|
||||
ReactDOM.render(
|
||||
root?.render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
document.getElementById('root')
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
@@ -7,18 +7,7 @@ import { generateEstimateOrder } from '../support/mocks/generate-estimate-order'
|
||||
import { generatePartyBalance } from '../support/mocks/generate-party-balance';
|
||||
import { generatePartyMarketData } from '../support/mocks/generate-party-market-data';
|
||||
import { generateMarketMarkPrice } from '../support/mocks/generate-market-mark-price';
|
||||
|
||||
const connectVegaWallet = () => {
|
||||
const form = 'rest-connector-form';
|
||||
const walletName = Cypress.env('TRADING_TEST_VEGA_WALLET_NAME');
|
||||
const walletPassphrase = Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE');
|
||||
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('connectors-list').find('button').click();
|
||||
cy.getByTestId(form).find('#wallet').click().type(walletName);
|
||||
cy.getByTestId(form).find('#passphrase').click().type(walletPassphrase);
|
||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||
};
|
||||
import { connectVegaWallet } from '../support/connect-wallet';
|
||||
|
||||
describe('Market trade', () => {
|
||||
let markets;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
connectVegaWallet,
|
||||
disconnectVegaWallet,
|
||||
} from '../support/connect-wallet';
|
||||
|
||||
describe('Portfolio page', () => {
|
||||
afterEach(() => {
|
||||
disconnectVegaWallet();
|
||||
});
|
||||
|
||||
it('button for wallet connect should work', () => {
|
||||
cy.visit('/');
|
||||
cy.get('[href="/portfolio"]').eq(0).click();
|
||||
cy.getByTestId('connect-vega-wallet-text').should('be.visible');
|
||||
connectVegaWallet();
|
||||
cy.getByTestId('connect-vega-wallet-text').should('not.exist');
|
||||
});
|
||||
|
||||
it('certain tabs should exist', () => {
|
||||
cy.visit('/portfolio');
|
||||
connectVegaWallet();
|
||||
cy.getByTestId('Assets').should('exist');
|
||||
cy.getByTestId('tab-assets').should('exist');
|
||||
|
||||
cy.getByTestId('Positions').click();
|
||||
cy.getByTestId('tab-positions').should('exist');
|
||||
|
||||
cy.getByTestId('Orders').click();
|
||||
cy.getByTestId('tab-orders').should('exist');
|
||||
|
||||
cy.getByTestId('Fills').click();
|
||||
cy.getByTestId('tab-fills').should('exist');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
export const connectVegaWallet = () => {
|
||||
const form = 'rest-connector-form';
|
||||
const walletName = Cypress.env('TRADING_TEST_VEGA_WALLET_NAME');
|
||||
const walletPassphrase = Cypress.env('TRADING_TEST_VEGA_WALLET_PASSPHRASE');
|
||||
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('connectors-list').find('button').click();
|
||||
cy.getByTestId(form).find('#wallet').click().type(walletName);
|
||||
cy.getByTestId(form).find('#passphrase').click().type(walletPassphrase);
|
||||
cy.getByTestId('rest-connector-form').find('button[type=submit]').click();
|
||||
};
|
||||
|
||||
export const disconnectVegaWallet = () => {
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('disconnect').click();
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { createClient } from './lib/apollo-client';
|
||||
import { ThemeContext } from '@vegaprotocol/react-helpers';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
|
||||
import { createClient } from './lib/apollo-client';
|
||||
import {
|
||||
VegaConnectDialog,
|
||||
VegaManageDialog,
|
||||
@@ -13,14 +14,12 @@ import '../styles.scss';
|
||||
import { AppLoader } from './components/app-loader';
|
||||
import Header from './components/header';
|
||||
import { Main } from './components/main';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import LocalContext from './context/local-context';
|
||||
import useLocalValues from './hooks/use-local-values';
|
||||
|
||||
function App() {
|
||||
const localValues = useLocalValues();
|
||||
const [theme, toggleTheme] = useThemeSwitcher();
|
||||
const [vegaWallet, setVegaWallet] = useState({
|
||||
connect: false,
|
||||
manage: false,
|
||||
});
|
||||
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const onToggle = () => setMenuOpen(!menuOpen);
|
||||
@@ -36,28 +35,23 @@ function App() {
|
||||
<ThemeContext.Provider value={theme}>
|
||||
<NetworkLoader createClient={createClient}>
|
||||
<VegaWalletProvider>
|
||||
<AppLoader>
|
||||
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
|
||||
<Header
|
||||
setVegaWallet={setVegaWallet}
|
||||
toggleTheme={toggleTheme}
|
||||
/>
|
||||
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={vegaWallet.connect}
|
||||
setDialogOpen={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, connect: open }))
|
||||
}
|
||||
/>
|
||||
<VegaManageDialog
|
||||
dialogOpen={vegaWallet.manage}
|
||||
setDialogOpen={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</AppLoader>
|
||||
<LocalContext.Provider value={localValues}>
|
||||
<AppLoader>
|
||||
<div className="max-h-full min-h-full dark:bg-lite-black dark:text-white-60 bg-white text-black-60 grid grid-rows-[min-content,1fr]">
|
||||
<Header toggleTheme={toggleTheme} />
|
||||
<Main isMenuOpen={menuOpen} onToggle={onToggle} />
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
dialogOpen={localValues.vegaWalletDialog.connect}
|
||||
setDialogOpen={localValues.vegaWalletDialog.setConnect}
|
||||
/>
|
||||
<VegaManageDialog
|
||||
dialogOpen={localValues.vegaWalletDialog.manage}
|
||||
setDialogOpen={localValues.vegaWalletDialog.setManage}
|
||||
/>
|
||||
</div>
|
||||
</AppLoader>
|
||||
</LocalContext.Provider>
|
||||
</VegaWalletProvider>
|
||||
</NetworkLoader>
|
||||
</ThemeContext.Provider>
|
||||
|
||||
@@ -36,7 +36,7 @@ export const DealTicketSteps = ({
|
||||
}: DealTicketMarketProps) => {
|
||||
const navigate = useNavigate();
|
||||
const setMarket = useCallback(
|
||||
(marketId) => {
|
||||
(marketId: string) => {
|
||||
navigate(`/trading/${marketId}`);
|
||||
},
|
||||
[navigate]
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
import React from 'react';
|
||||
import React, { useContext } from 'react';
|
||||
import { ThemeSwitcher } from '@vegaprotocol/ui-toolkit';
|
||||
import Logo from './logo';
|
||||
import { VegaWalletConnectButton } from '../vega-wallet-connect-button';
|
||||
|
||||
type WalletParams = {
|
||||
connect: boolean;
|
||||
manage: boolean;
|
||||
};
|
||||
import LocalContext from '../../context/local-context';
|
||||
|
||||
interface Props {
|
||||
setVegaWallet: (func: (wParams: WalletParams) => WalletParams) => void;
|
||||
toggleTheme: () => void;
|
||||
}
|
||||
const Header = ({ setVegaWallet, toggleTheme }: Props) => {
|
||||
const Header = ({ toggleTheme }: Props) => {
|
||||
const {
|
||||
vegaWalletDialog: { setConnect, setManage },
|
||||
} = useContext(LocalContext);
|
||||
return (
|
||||
<div
|
||||
className="flex items-stretch pr-16 py-16 bg-black text-white-60"
|
||||
@@ -21,12 +19,8 @@ const Header = ({ setVegaWallet, toggleTheme }: Props) => {
|
||||
<Logo />
|
||||
<div className="flex items-center gap-4 ml-auto mr-8 relative z-10">
|
||||
<VegaWalletConnectButton
|
||||
setConnectDialog={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, connect: open }))
|
||||
}
|
||||
setManageDialog={(open) =>
|
||||
setVegaWallet((x) => ({ ...x, manage: open }))
|
||||
}
|
||||
setConnectDialog={setConnect}
|
||||
setManageDialog={setManage}
|
||||
/>
|
||||
<ThemeSwitcher
|
||||
onToggle={toggleTheme}
|
||||
|
||||
@@ -1,13 +1,49 @@
|
||||
import { useContext } from 'react';
|
||||
import { AccountsContainer } from '@vegaprotocol/accounts';
|
||||
import { t } from '@vegaprotocol/react-helpers';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { Button, Splash, Tabs, Tab } from '@vegaprotocol/ui-toolkit';
|
||||
import LocalContext from '../../context/local-context';
|
||||
import { OrderListContainer } from '@vegaprotocol/orders';
|
||||
import { PositionsContainer } from '@vegaprotocol/positions';
|
||||
import { FillsContainer } from '@vegaprotocol/fills';
|
||||
|
||||
export const Portfolio = () => {
|
||||
const { keypair } = useVegaWallet();
|
||||
const {
|
||||
vegaWalletDialog: { setConnect },
|
||||
} = useContext(LocalContext);
|
||||
if (!keypair) {
|
||||
return (
|
||||
<Splash>
|
||||
<div className="text-center">
|
||||
<p className="mb-12" data-testid="connect-vega-wallet-text">
|
||||
{t('Connect your Vega wallet')}
|
||||
</p>
|
||||
<Button
|
||||
onClick={() => setConnect(true)}
|
||||
data-testid="vega-wallet-connect"
|
||||
>
|
||||
{t('Connect')}
|
||||
</Button>
|
||||
</div>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<section>
|
||||
<h2 className="text-lg mb-16">{t('Assets')}</h2>
|
||||
<div className="h-screen">
|
||||
<Tabs>
|
||||
<Tab id="assets" name={t('Assets')}>
|
||||
<AccountsContainer />
|
||||
</div>
|
||||
</section>
|
||||
</Tab>
|
||||
<Tab id="positions" name={t('Positions')}>
|
||||
<PositionsContainer />
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<OrderListContainer />
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<FillsContainer />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
);
|
||||
};
|
||||
|
||||
+15
-3
@@ -25,6 +25,14 @@ import SimpleMarketToolbar from './simple-market-toolbar';
|
||||
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
|
||||
import type { SimpleMarketDataSub_marketData } from './__generated__/SimpleMarketDataSub';
|
||||
import { IS_MARKET_TRADABLE } from '../../constants';
|
||||
import type {
|
||||
CellKeyDownEvent,
|
||||
FullWidthCellKeyDownEvent,
|
||||
} from 'ag-grid-community/dist/lib/events';
|
||||
import type {
|
||||
GetRowIdParams,
|
||||
TabToNextCellParams,
|
||||
} from 'ag-grid-community/dist/lib/entities/iCallbackParams';
|
||||
|
||||
export type SimpleMarketsType = SimpleMarkets_markets & {
|
||||
percentChange?: number | '-';
|
||||
@@ -84,7 +92,7 @@ const SimpleMarketList = () => {
|
||||
|
||||
const { columnDefs, defaultColDef } = useColumnDefinitions({ isMobile });
|
||||
|
||||
const getRowId = useCallback(({ data }) => data.id, []);
|
||||
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
|
||||
|
||||
const handleRowClicked = useCallback(
|
||||
({ data }: { data: SimpleMarketsType }) => {
|
||||
@@ -95,7 +103,7 @@ const SimpleMarketList = () => {
|
||||
[navigate]
|
||||
);
|
||||
|
||||
const onTabToNextCell = useCallback((params) => {
|
||||
const onTabToNextCell = useCallback((params: TabToNextCellParams) => {
|
||||
const {
|
||||
api,
|
||||
previousCellPosition: { rowIndex },
|
||||
@@ -108,7 +116,11 @@ const SimpleMarketList = () => {
|
||||
}, []);
|
||||
|
||||
const onCellKeyDown = useCallback(
|
||||
(params) => {
|
||||
(
|
||||
params: (CellKeyDownEvent | FullWidthCellKeyDownEvent) & {
|
||||
event: KeyboardEvent;
|
||||
}
|
||||
) => {
|
||||
const { event: { key = '' } = {}, data } = params;
|
||||
if (key === 'Enter') {
|
||||
handleRowClicked({ data });
|
||||
|
||||
+60
-52
@@ -7,6 +7,7 @@ import type { MockedResponse } from '@apollo/client/testing';
|
||||
import type { MarketFilters } from './__generated__/MarketFilters';
|
||||
import { FILTERS_QUERY } from './data-provider';
|
||||
import filterData from './mocks/market-filters.json';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
|
||||
describe('SimpleMarketToolbar', () => {
|
||||
const filterMock: MockedResponse<MarketFilters> = {
|
||||
@@ -58,67 +59,74 @@ describe('SimpleMarketToolbar', () => {
|
||||
});
|
||||
|
||||
it('should be properly rendered', async () => {
|
||||
render(
|
||||
// @ts-ignore different versions of react types in apollo and app
|
||||
<MockedProvider mocks={[filterMock]} addTypename={false}>
|
||||
<WrappedCompForTest />
|
||||
</MockedProvider>,
|
||||
{ wrapper: BrowserRouter }
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Future')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText('Future'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('market-products-menu').children).toHaveLength(
|
||||
3
|
||||
act(async () => {
|
||||
render(
|
||||
// @ts-ignore different versions of react types in apollo and app
|
||||
<MockedProvider mocks={[filterMock]} addTypename={false}>
|
||||
<WrappedCompForTest />
|
||||
</MockedProvider>,
|
||||
{ wrapper: BrowserRouter }
|
||||
);
|
||||
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(6);
|
||||
});
|
||||
fireEvent.click(screen.getByTestId('state-trigger'));
|
||||
waitFor(() => {
|
||||
expect(screen.getByRole('menu')).toBeInTheDocument();
|
||||
expect(screen.getByRole('menu').children).toHaveLength(10);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Future')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText('Future'));
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByTestId('market-products-menu').children
|
||||
).toHaveLength(3);
|
||||
expect(screen.getByTestId('market-assets-menu').children).toHaveLength(
|
||||
6
|
||||
);
|
||||
});
|
||||
fireEvent.click(screen.getByTestId('state-trigger'));
|
||||
waitFor(() => {
|
||||
expect(screen.getByRole('menu')).toBeInTheDocument();
|
||||
expect(screen.getByRole('menu').children).toHaveLength(10);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('navigation should work well', async () => {
|
||||
render(
|
||||
// @ts-ignore different versions of react types in apollo and app
|
||||
<MockedProvider mocks={[filterMock]} addTypename={false}>
|
||||
<WrappedCompForTest />
|
||||
</MockedProvider>,
|
||||
{ wrapper: BrowserRouter }
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Future')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText('Future'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('location-display')).toHaveTextContent(
|
||||
'/markets/Active/Future'
|
||||
act(async () => {
|
||||
render(
|
||||
// @ts-ignore different versions of react types in apollo and app
|
||||
<MockedProvider mocks={[filterMock]} addTypename={false}>
|
||||
<WrappedCompForTest />
|
||||
</MockedProvider>,
|
||||
{ wrapper: BrowserRouter }
|
||||
);
|
||||
});
|
||||
|
||||
fireEvent.click(
|
||||
screen
|
||||
.getByTestId('market-assets-menu')
|
||||
.children[5].querySelector('a') as HTMLAnchorElement
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('location-display')).toHaveTextContent(
|
||||
'/markets/Active/Future/tEURO'
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Future')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText('Future'));
|
||||
|
||||
fireEvent.click(screen.getByTestId('state-trigger'));
|
||||
waitFor(() => {
|
||||
expect(screen.getByRole('menu')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByText('Pending'));
|
||||
expect(screen.getByTestId('location-display')).toHaveTextContent(
|
||||
'/markets/Pending/Future/tEURO'
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('location-display')).toHaveTextContent(
|
||||
'/markets/Active/Future'
|
||||
);
|
||||
});
|
||||
|
||||
fireEvent.click(
|
||||
screen
|
||||
.getByTestId('market-assets-menu')
|
||||
.children[5].querySelector('a') as HTMLAnchorElement
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('location-display')).toHaveTextContent(
|
||||
'/markets/Active/Future/tEURO'
|
||||
);
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('state-trigger'));
|
||||
waitFor(() => {
|
||||
expect(screen.getByRole('menu')).toBeInTheDocument();
|
||||
fireEvent.click(screen.getByText('Pending'));
|
||||
expect(screen.getByTestId('location-display')).toHaveTextContent(
|
||||
'/markets/Pending/Future/tEURO'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export interface VegaWalletDialogState {
|
||||
connect: boolean;
|
||||
manage: boolean;
|
||||
setConnect: (isOpen: boolean) => void;
|
||||
setManage: (isOpen: boolean) => void;
|
||||
}
|
||||
|
||||
export interface LocalValues {
|
||||
vegaWalletDialog: VegaWalletDialogState;
|
||||
}
|
||||
|
||||
const LocalContext = createContext<LocalValues>({} as LocalValues);
|
||||
|
||||
export default LocalContext;
|
||||
@@ -0,0 +1,17 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import useLocalValues from './use-local-values';
|
||||
|
||||
describe('local values hook', () => {
|
||||
it('state of wallet dialog should be properly handled', () => {
|
||||
const { result } = renderHook(() => useLocalValues());
|
||||
expect(result.current.vegaWalletDialog).toBeDefined();
|
||||
expect(result.current.vegaWalletDialog.manage).toBe(false);
|
||||
expect(result.current.vegaWalletDialog.connect).toBe(false);
|
||||
act(() => {
|
||||
result.current.vegaWalletDialog.setConnect(true);
|
||||
result.current.vegaWalletDialog.setManage(true);
|
||||
});
|
||||
expect(result.current.vegaWalletDialog.manage).toBe(true);
|
||||
expect(result.current.vegaWalletDialog.connect).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { LocalValues } from '../context/local-context';
|
||||
|
||||
const useLocalValues = () => {
|
||||
const [connect, setConnect] = useState<boolean>(false);
|
||||
const [manage, setManage] = useState<boolean>(false);
|
||||
return useMemo<LocalValues>(
|
||||
() => ({
|
||||
vegaWalletDialog: { connect, manage, setConnect, setManage },
|
||||
}),
|
||||
[connect, manage, setConnect, setManage]
|
||||
);
|
||||
};
|
||||
|
||||
export default useLocalValues;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import useMarketPositions from './use-market-positions';
|
||||
|
||||
let mockNotEmptyData = {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import useOrderCloseOut from './use-order-closeout';
|
||||
import type { Order } from '@vegaprotocol/orders';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { BigNumber } from 'bignumber.js';
|
||||
import type { Order } from '@vegaprotocol/orders';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useSettlementAccount } from './use-settlement-account';
|
||||
import type { PartyBalanceQuery_party_accounts } from '../components/deal-ticket/__generated__/PartyBalanceQuery';
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { StrictMode } from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import App from './app/app';
|
||||
const rootElement = document.getElementById('root');
|
||||
const root = rootElement && createRoot(rootElement);
|
||||
|
||||
ReactDOM.render(
|
||||
root?.render(
|
||||
<StrictMode>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</StrictMode>,
|
||||
document.getElementById('root')
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "129999.45",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "115112.855692879407867",
|
||||
"locked_amount": "114519.66346241700027357",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129999.45",
|
||||
@@ -521,7 +521,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "97499.58",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "56137.189810073586793362",
|
||||
"locked_amount": "55555.323490345729922172",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "97499.58",
|
||||
@@ -554,7 +554,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "135173.4239508",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "76729.84678886427237912326088",
|
||||
"locked_amount": "75934.53598482564062830135308",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "135173.4239508",
|
||||
@@ -587,7 +587,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "32499.86",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "23615.94631568356888721",
|
||||
"locked_amount": "23371.165203267934388436",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "32499.86",
|
||||
@@ -620,7 +620,7 @@
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "10833.29",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "7686.761362036356180828",
|
||||
"locked_amount": "7607.087485244758217681",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10833.29",
|
||||
@@ -728,7 +728,7 @@
|
||||
"tranche_end": "2022-11-01T00:00:00.000Z",
|
||||
"total_added": "22500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "10546.50985054347825",
|
||||
"locked_amount": "10240.73680932970875",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "15000",
|
||||
@@ -814,7 +814,7 @@
|
||||
"tranche_end": "2023-06-02T00:00:00.000Z",
|
||||
"total_added": "1939928.38",
|
||||
"total_removed": "329229.1901984227119",
|
||||
"locked_amount": "1590459.657187149873995754",
|
||||
"locked_amount": "1577169.585310124862664322",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1852091.69",
|
||||
@@ -1067,7 +1067,7 @@
|
||||
"tranche_end": "2023-02-01T00:00:00.000Z",
|
||||
"total_added": "42500",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "41171.18527324879225",
|
||||
"locked_amount": "40593.61397317833875",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "12500",
|
||||
@@ -1910,8 +1910,8 @@
|
||||
"tranche_start": "2021-09-05T00:00:00.000Z",
|
||||
"tranche_end": "2022-09-30T00:00:00.000Z",
|
||||
"total_added": "60916.66666633337",
|
||||
"total_removed": "19489.295908782179372649",
|
||||
"locked_amount": "8473.1981095215396960108024782706",
|
||||
"total_removed": "20096.13484613934556896",
|
||||
"locked_amount": "8082.621688484583321328446504115",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2833.333333",
|
||||
@@ -2010,11 +2010,21 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "279.862130034666196311",
|
||||
"user": "0x832cfD8fD4a1791b5eF5BD18a677beb7731Aa18E",
|
||||
"tx": "0xf988fe006bc73c5ffb65f185186bfe14fb0d9b16ffaf05f224dd11ccff4a79d0"
|
||||
},
|
||||
{
|
||||
"amount": "250.694756035",
|
||||
"user": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
|
||||
"tx": "0xc8ca8b7db95df047eba78e410518166d68f1332e58e3f3ab0aed8f7ce4b5edda"
|
||||
},
|
||||
{
|
||||
"amount": "326.9768073225",
|
||||
"user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2",
|
||||
"tx": "0xb5f445daaac71c28e7c5e1542bacf7396eae0d08637acb7187e4d369e5da6533"
|
||||
},
|
||||
{
|
||||
"amount": "360.65606454",
|
||||
"user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2",
|
||||
@@ -2429,6 +2439,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "279.862130034666196311",
|
||||
"user": "0x832cfD8fD4a1791b5eF5BD18a677beb7731Aa18E",
|
||||
"tranche_id": 13,
|
||||
"tx": "0xf988fe006bc73c5ffb65f185186bfe14fb0d9b16ffaf05f224dd11ccff4a79d0"
|
||||
},
|
||||
{
|
||||
"amount": "427.615401425332614652",
|
||||
"user": "0x832cfD8fD4a1791b5eF5BD18a677beb7731Aa18E",
|
||||
@@ -2467,8 +2483,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "1983.33333333333",
|
||||
"withdrawn_tokens": "1437.166618670997584593",
|
||||
"remaining_tokens": "546.166714662332415407"
|
||||
"withdrawn_tokens": "1717.028748705663780904",
|
||||
"remaining_tokens": "266.304584627666219096"
|
||||
},
|
||||
{
|
||||
"address": "0x883aD562D0a83569dA00DdF88C96C348519c0030",
|
||||
@@ -2663,6 +2679,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "326.9768073225",
|
||||
"user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2",
|
||||
"tranche_id": 13,
|
||||
"tx": "0xb5f445daaac71c28e7c5e1542bacf7396eae0d08637acb7187e4d369e5da6533"
|
||||
},
|
||||
{
|
||||
"amount": "360.65606454",
|
||||
"user": "0x1887D97F9C875108Aa6bE109B282f87A666472f2",
|
||||
@@ -2737,8 +2759,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "4250",
|
||||
"withdrawn_tokens": "3341.8215663575",
|
||||
"remaining_tokens": "908.1784336425"
|
||||
"withdrawn_tokens": "3668.79837368",
|
||||
"remaining_tokens": "581.20162632"
|
||||
},
|
||||
{
|
||||
"address": "0x9fd50776F133751E8Ae6abE1Be124638Bb917E05",
|
||||
@@ -5567,9 +5589,9 @@
|
||||
"tranche_id": 11,
|
||||
"tranche_start": "2021-09-03T00:00:00.000Z",
|
||||
"tranche_end": "2022-09-03T00:00:00.000Z",
|
||||
"total_added": "25061.000000000000000003",
|
||||
"total_added": "25091.000000000000000003",
|
||||
"total_removed": "11031.95093994249",
|
||||
"locked_amount": "1870.78743854642410777022394805936073071",
|
||||
"locked_amount": "1701.13336012810797156020339564307458148",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "2",
|
||||
@@ -5646,6 +5668,21 @@
|
||||
"user": "0x38249D7DcC4eF7dFCa6579C069ADd3a1a4E4DeA3",
|
||||
"tx": "0x1ef6629e141f778648675744582672f077039a579ec9bb0e556d287ecafe6567"
|
||||
},
|
||||
{
|
||||
"amount": "10",
|
||||
"user": "0x04E8B9AF60fb22085e68d6ec4671559c4F1c95B8",
|
||||
"tx": "0x633149680b6fcc585de237b6e36ac676c6fff63559d6bf85b6706ad74912f786"
|
||||
},
|
||||
{
|
||||
"amount": "10",
|
||||
"user": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"tx": "0x2b3d71399f5e657947392a355f9e74e9516fd13430d283e6d5baf6994cc06535"
|
||||
},
|
||||
{
|
||||
"amount": "10",
|
||||
"user": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"tx": "0xce0d84e4f939f534a8a3ddcfe77c6b01d76504a5e199e086528a0939168a999d"
|
||||
},
|
||||
{
|
||||
"amount": "25",
|
||||
"user": "0x2c51Dc5A225a9d8542FBf28BA9dC501f026CD5e8",
|
||||
@@ -9976,6 +10013,42 @@
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "135"
|
||||
},
|
||||
{
|
||||
"address": "0x04E8B9AF60fb22085e68d6ec4671559c4F1c95B8",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10",
|
||||
"user": "0x04E8B9AF60fb22085e68d6ec4671559c4F1c95B8",
|
||||
"tranche_id": 11,
|
||||
"tx": "0x633149680b6fcc585de237b6e36ac676c6fff63559d6bf85b6706ad74912f786"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "10",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "10"
|
||||
},
|
||||
{
|
||||
"address": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "10",
|
||||
"user": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"tranche_id": 11,
|
||||
"tx": "0x2b3d71399f5e657947392a355f9e74e9516fd13430d283e6d5baf6994cc06535"
|
||||
},
|
||||
{
|
||||
"amount": "10",
|
||||
"user": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"tranche_id": 11,
|
||||
"tx": "0xce0d84e4f939f534a8a3ddcfe77c6b01d76504a5e199e086528a0939168a999d"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"total_tokens": "20",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "20"
|
||||
},
|
||||
{
|
||||
"address": "0x2c51Dc5A225a9d8542FBf28BA9dC501f026CD5e8",
|
||||
"deposits": [
|
||||
@@ -16675,7 +16748,7 @@
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "3732368.4671",
|
||||
"total_removed": "148381.2401909658774",
|
||||
"locked_amount": "2468484.07858648738266865286",
|
||||
"locked_amount": "2448061.86522307740430942296",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1998.95815",
|
||||
@@ -17431,8 +17504,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-12-05T00:00:00.000Z",
|
||||
"total_added": "15788853.065470999700000001",
|
||||
"total_removed": "87554.53659285418091",
|
||||
"locked_amount": "13980828.10720845248248171565886925162206",
|
||||
"total_removed": "88375.389548878522835",
|
||||
"locked_amount": "13908782.9949687471309483310576734351595026",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "16249.93",
|
||||
@@ -17926,6 +17999,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "253.966945001875125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x78c752bb2c9a9cdf62aee63e148d31ab640a8b85fb266de27f9ade361dbc3334"
|
||||
},
|
||||
{
|
||||
"amount": "1439.64435049980255",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -17991,6 +18069,11 @@
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0xc0ad30d651b24d00e9d3569585da1c1823eac155424c3286243a3fdb22c3d0aa"
|
||||
},
|
||||
{
|
||||
"amount": "566.8860110224668",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tx": "0x8aefd70c1239dd4dbe2b48069f26c3106cd4daf9da03ef534abca5fab35370c4"
|
||||
},
|
||||
{
|
||||
"amount": "325.1056930428465",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -18252,6 +18335,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "253.966945001875125",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x78c752bb2c9a9cdf62aee63e148d31ab640a8b85fb266de27f9ade361dbc3334"
|
||||
},
|
||||
{
|
||||
"amount": "1439.64435049980255",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -18318,6 +18407,12 @@
|
||||
"tranche_id": 2,
|
||||
"tx": "0xc0ad30d651b24d00e9d3569585da1c1823eac155424c3286243a3fdb22c3d0aa"
|
||||
},
|
||||
{
|
||||
"amount": "566.8860110224668",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
"tranche_id": 2,
|
||||
"tx": "0x8aefd70c1239dd4dbe2b48069f26c3106cd4daf9da03ef534abca5fab35370c4"
|
||||
},
|
||||
{
|
||||
"amount": "325.1056930428465",
|
||||
"user": "0x20CD77B9FC2f1fEDfb6F184E25f7127BFE991C8b",
|
||||
@@ -18542,8 +18637,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "194999.1675",
|
||||
"withdrawn_tokens": "22138.18705326715755",
|
||||
"remaining_tokens": "172860.98044673284245"
|
||||
"withdrawn_tokens": "22959.040009291499475",
|
||||
"remaining_tokens": "172040.127490708500525"
|
||||
},
|
||||
{
|
||||
"address": "0x89051CAb67Bc7F8CC44F7e270c6EDaf1EC57676c",
|
||||
@@ -19996,8 +20091,8 @@
|
||||
"tranche_start": "2021-11-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-05-05T00:00:00.000Z",
|
||||
"total_added": "14597706.0446472999",
|
||||
"total_removed": "2555774.676426156395044652",
|
||||
"locked_amount": "7251985.667103230085781404877384725",
|
||||
"total_removed": "2557310.374144334864250402",
|
||||
"locked_amount": "7185131.80997249945246236165554315",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "129284.449",
|
||||
@@ -20206,6 +20301,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "469.2149598263806575",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x729f50d007fcd49ca9be63265e100b8dc47462214c8d6d590cfce9aadae3a41b"
|
||||
},
|
||||
{
|
||||
"amount": "676.87356319623428375",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -20276,6 +20376,11 @@
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0xe89e90cf12e064fcd35f07de7532d8a49c57c98fe5450786822a9ecab2283af7"
|
||||
},
|
||||
{
|
||||
"amount": "1066.48275835208854825",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tx": "0x1c9bc32e3b7a6f2df77bb8400c004b6f8cb4f607b884aca08132642f11589036"
|
||||
},
|
||||
{
|
||||
"amount": "600.79246609513746875",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -22102,6 +22207,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "469.2149598263806575",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x729f50d007fcd49ca9be63265e100b8dc47462214c8d6d590cfce9aadae3a41b"
|
||||
},
|
||||
{
|
||||
"amount": "676.87356319623428375",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -22180,6 +22291,12 @@
|
||||
"tranche_id": 3,
|
||||
"tx": "0xe89e90cf12e064fcd35f07de7532d8a49c57c98fe5450786822a9ecab2283af7"
|
||||
},
|
||||
{
|
||||
"amount": "1066.48275835208854825",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
"tranche_id": 3,
|
||||
"tx": "0x1c9bc32e3b7a6f2df77bb8400c004b6f8cb4f607b884aca08132642f11589036"
|
||||
},
|
||||
{
|
||||
"amount": "600.79246609513746875",
|
||||
"user": "0x4Aa3c35F6CC2d507E5C18205ee57099A4C80B19b",
|
||||
@@ -23646,8 +23763,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "359123.469575",
|
||||
"withdrawn_tokens": "180340.793310407992522",
|
||||
"remaining_tokens": "178782.676264592007478"
|
||||
"withdrawn_tokens": "181876.49102858646172775",
|
||||
"remaining_tokens": "177246.97854641353827225"
|
||||
},
|
||||
{
|
||||
"address": "0xBdd412797c1B78535Afc5F71503b91fAbD0160fB",
|
||||
@@ -24716,8 +24833,8 @@
|
||||
"tranche_start": "2021-10-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-04-05T00:00:00.000Z",
|
||||
"total_added": "5778205.3912159303",
|
||||
"total_removed": "1689572.490316409277194627",
|
||||
"locked_amount": "2548399.993176457542673417492313425",
|
||||
"total_removed": "1691301.542530270364867627",
|
||||
"locked_amount": "2521985.629895344185593035568823738",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "552496.6455",
|
||||
@@ -24861,6 +24978,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "1729.052213861087673",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tx": "0x7819558c787850ba53da454062b2d6f3420b3130efca67ffb6c2ad7734b061b0"
|
||||
},
|
||||
{
|
||||
"amount": "4262.756978634768333",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@@ -25844,6 +25966,12 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "1729.052213861087673",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
"tranche_id": 4,
|
||||
"tx": "0x7819558c787850ba53da454062b2d6f3420b3130efca67ffb6c2ad7734b061b0"
|
||||
},
|
||||
{
|
||||
"amount": "4262.756978634768333",
|
||||
"user": "0xafa64cCa337eFEE0AD827F6C2684e69275226e90",
|
||||
@@ -25954,8 +26082,8 @@
|
||||
}
|
||||
],
|
||||
"total_tokens": "331498.5873",
|
||||
"withdrawn_tokens": "184626.184307596609197",
|
||||
"remaining_tokens": "146872.402992403390803"
|
||||
"withdrawn_tokens": "186355.23652145769687",
|
||||
"remaining_tokens": "145143.35077854230313"
|
||||
},
|
||||
{
|
||||
"address": "0x16da609341ed67750A8BCC5AAa2005471006Cd77",
|
||||
@@ -26037,8 +26165,8 @@
|
||||
"tranche_start": "2022-06-05T00:00:00.000Z",
|
||||
"tranche_end": "2023-06-05T00:00:00.000Z",
|
||||
"total_added": "472355.6199999996",
|
||||
"total_removed": "990.63679188326",
|
||||
"locked_amount": "391145.41270858799297531973820392",
|
||||
"total_removed": "1095.86336468526",
|
||||
"locked_amount": "387909.39626280724755391190969052",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "3000",
|
||||
@@ -32657,6 +32785,16 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "35.252308472",
|
||||
"user": "0xFBD4E1d0D89632c279F06a1d9bb9438647D99a29",
|
||||
"tx": "0xa301694ad07494740352d692930e6a1c27db9f5405216e80117dc96363ac1d35"
|
||||
},
|
||||
{
|
||||
"amount": "35.302765092",
|
||||
"user": "0x65eEbD9137d633b910f842259E5699b731b514a8",
|
||||
"tx": "0xbb3e038a9281419c23a5aac6754ca30cfa09df5958f41f75ee9c2a091746c817"
|
||||
},
|
||||
{
|
||||
"amount": "27.606855656",
|
||||
"user": "0x4d4AA3f302cbA998AAFC03F9d73484434ed9Fd53",
|
||||
@@ -32712,6 +32850,11 @@
|
||||
"user": "0xe52a291B177de546cd146Fcb25F489378cAEA21f",
|
||||
"tx": "0xa356e2e5e5685fb4d0140aa4b9e47d85a61bcc56e9b5f97a0dcc583cd41bd3a8"
|
||||
},
|
||||
{
|
||||
"amount": "34.671499238",
|
||||
"user": "0x942e32E60f465f5DCDc071840E9D9D6a3D619dE7",
|
||||
"tx": "0x6a7e20b99460e48f31e7acfe26aa73a7b62574f12de2e27814d81c0bb4ced98c"
|
||||
},
|
||||
{
|
||||
"amount": "18.521226534",
|
||||
"user": "0xfeF4b56A370135A17Eef5ba8f5c308451a64Cf86",
|
||||
@@ -34465,10 +34608,17 @@
|
||||
"tx": "0x23a584d4881de8a7fdac91afaf0fd834e84ddcad95a0bc420020032bbeae8f50"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "35.302765092",
|
||||
"user": "0x65eEbD9137d633b910f842259E5699b731b514a8",
|
||||
"tranche_id": 5,
|
||||
"tx": "0xbb3e038a9281419c23a5aac6754ca30cfa09df5958f41f75ee9c2a091746c817"
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "200"
|
||||
"withdrawn_tokens": "35.302765092",
|
||||
"remaining_tokens": "164.697234908"
|
||||
},
|
||||
{
|
||||
"address": "0x4F169FA469548050D73092f092E8Cc97d1fd19C3",
|
||||
@@ -35275,10 +35425,17 @@
|
||||
"tx": "0x23a584d4881de8a7fdac91afaf0fd834e84ddcad95a0bc420020032bbeae8f50"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "35.252308472",
|
||||
"user": "0xFBD4E1d0D89632c279F06a1d9bb9438647D99a29",
|
||||
"tranche_id": 5,
|
||||
"tx": "0xa301694ad07494740352d692930e6a1c27db9f5405216e80117dc96363ac1d35"
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "200"
|
||||
"withdrawn_tokens": "35.252308472",
|
||||
"remaining_tokens": "164.747691528"
|
||||
},
|
||||
{
|
||||
"address": "0x56fb022401dbb8AfBE085439de0a96e996AF4071",
|
||||
@@ -49697,10 +49854,17 @@
|
||||
"tx": "0xc8541da6a57f410b6faba47a5e5184bae700193b7bd042914fffc562114d92f5"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "34.671499238",
|
||||
"user": "0x942e32E60f465f5DCDc071840E9D9D6a3D619dE7",
|
||||
"tranche_id": 5,
|
||||
"tx": "0x6a7e20b99460e48f31e7acfe26aa73a7b62574f12de2e27814d81c0bb4ced98c"
|
||||
}
|
||||
],
|
||||
"total_tokens": "200",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "200"
|
||||
"withdrawn_tokens": "34.671499238",
|
||||
"remaining_tokens": "165.328500762"
|
||||
},
|
||||
{
|
||||
"address": "0x88c3adb4ac567810b9618B1eA43b2061E6290861",
|
||||
@@ -52025,7 +52189,7 @@
|
||||
"tranche_start": "2021-12-05T00:00:00.000Z",
|
||||
"tranche_end": "2022-06-05T00:00:00.000Z",
|
||||
"total_added": "171288.42",
|
||||
"total_removed": "43907.1232333307989",
|
||||
"total_removed": "44407.1232333307989",
|
||||
"locked_amount": "0",
|
||||
"deposits": [
|
||||
{
|
||||
@@ -56250,6 +56414,11 @@
|
||||
}
|
||||
],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x9A453696D38e0e42b11AC30Be8C3A100eDDA5f53",
|
||||
"tx": "0x88b1536663a138314319d8d5d7e576c959b895d5135250153815136a6f99ab66"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x4aD00C7AF1dC96c935d832fd39bF53d0DAB66B55",
|
||||
@@ -56360,6 +56529,11 @@
|
||||
"user": "0x5003b210D7F862DB4DeaFe465356DCfbC1fd0bdE",
|
||||
"tx": "0x43759ab0b41ea38276793b34f095acde8b351c00d9a627bac5d497a772a27c7a"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"tx": "0x67b8333e3b22b4a66cb201dc7ce434ce544f20752f591f3889a5a6b1d80bb47d"
|
||||
},
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x1f7C20Bd98B2f346A58d2c5faaFc940E0C36450E",
|
||||
@@ -63059,10 +63233,17 @@
|
||||
"tx": "0xc7dd4c2b995cc486fcd8b7892cd79f8fb393ada004dc68cf66ec82d99b35763c"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x6C4730dCD66D130Da02256aF547B36edc9a15F63",
|
||||
"tranche_id": 6,
|
||||
"tx": "0x67b8333e3b22b4a66cb201dc7ce434ce544f20752f591f3889a5a6b1d80bb47d"
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "250"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x3EcF29F7ddf73A4D347f72Cd86668f1898675Ef8",
|
||||
@@ -65454,10 +65635,17 @@
|
||||
"tx": "0xadc25e73736cd85e84fee16f2d7ee41f16f7deb83a52ceb82b327974063964f7"
|
||||
}
|
||||
],
|
||||
"withdrawals": [],
|
||||
"withdrawals": [
|
||||
{
|
||||
"amount": "250",
|
||||
"user": "0x9A453696D38e0e42b11AC30Be8C3A100eDDA5f53",
|
||||
"tranche_id": 6,
|
||||
"tx": "0x88b1536663a138314319d8d5d7e576c959b895d5135250153815136a6f99ab66"
|
||||
}
|
||||
],
|
||||
"total_tokens": "250",
|
||||
"withdrawn_tokens": "0",
|
||||
"remaining_tokens": "250"
|
||||
"withdrawn_tokens": "250",
|
||||
"remaining_tokens": "0"
|
||||
},
|
||||
{
|
||||
"address": "0x92Faf283Ff5Bca05673411091D922a054A959900",
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"tranche_end": "2022-11-26T13:48:10.000Z",
|
||||
"total_added": "100",
|
||||
"total_removed": "0",
|
||||
"locked_amount": "30.63620624048706",
|
||||
"locked_amount": "29.951135210553015",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "100",
|
||||
@@ -242,7 +242,7 @@
|
||||
"tranche_end": "2022-10-12T00:53:20.000Z",
|
||||
"total_added": "1100",
|
||||
"total_removed": "673.04388635",
|
||||
"locked_amount": "199.760223236935515",
|
||||
"locked_amount": "192.224441907661119",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1000",
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"tranche_end": "2022-10-12T00:53:20.000Z",
|
||||
"total_added": "1010.000000000000000001",
|
||||
"total_removed": "668.4622323651",
|
||||
"locked_amount": "183.41620497209533650018160020294266865",
|
||||
"locked_amount": "176.49698756976157290017474949264332829",
|
||||
"deposits": [
|
||||
{
|
||||
"amount": "1000",
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import React, { StrictMode } from 'react';
|
||||
import './styles/styles.css';
|
||||
import App from './app';
|
||||
import reportWebVitals from './report-web-vitals';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
const rootElement = document.getElementById('root');
|
||||
const root = rootElement && createRoot(rootElement);
|
||||
|
||||
root?.render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
@@ -86,7 +86,7 @@ context('Staking Page - verify elements on page', function () {
|
||||
|
||||
// 1002-STKE-021
|
||||
it('Should be able to see validator ranking score', function () {
|
||||
//
|
||||
cy.get('.ag-body-horizontal-scroll-viewport').scrollTo('right');
|
||||
cy.get('[col-id="rankingScore"]')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($rankingScore) => {
|
||||
@@ -114,7 +114,6 @@ context('Staking Page - verify elements on page', function () {
|
||||
|
||||
// 1002-STKE-024
|
||||
it('Should be able to see validator voting power score', function () {
|
||||
cy.get('.ag-body-horizontal-scroll-viewport').scrollTo('right');
|
||||
cy.get('[col-id="votingPower"]')
|
||||
.should('have.length.at.least', 1)
|
||||
.each(($votingPower) => {
|
||||
|
||||
@@ -6,3 +6,4 @@ NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET":"https://dev.
|
||||
NX_ETHEREUM_PROVIDER_URL=https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
|
||||
NX_ETHERSCAN_URL=https://ropsten.etherscan.io
|
||||
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
|
||||
NX_IS_NEW_BRIDGE_CONTRACT=false
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
export interface HeadingProps {
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface HeadingProps {
|
||||
title?: string;
|
||||
centerContent?: boolean;
|
||||
}
|
||||
|
||||
export const Heading = ({ title }: HeadingProps) => {
|
||||
export const Heading = ({ title, centerContent = true }: HeadingProps) => {
|
||||
if (!title) return null;
|
||||
|
||||
return (
|
||||
<header className="my-0 mx-auto">
|
||||
<header
|
||||
className={classNames('my-0', {
|
||||
'mx-auto': centerContent,
|
||||
})}
|
||||
>
|
||||
<h1 className="font-alpha calt">{title}</h1>
|
||||
</header>
|
||||
);
|
||||
|
||||
@@ -62,6 +62,9 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
|
||||
if (connector?.connectEagerly) {
|
||||
connector.connectEagerly();
|
||||
}
|
||||
// wallet connect doesnt handle connectEagerly being called when connector is also in the
|
||||
// deps array.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [connector]);
|
||||
|
||||
if (error) {
|
||||
|
||||
@@ -30,6 +30,7 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
// contracts so that we can sign transactions, otherwise use the provider for just
|
||||
// reading data
|
||||
React.useEffect(() => {
|
||||
let cancelled = false;
|
||||
const run = async () => {
|
||||
let signer = null;
|
||||
|
||||
@@ -38,7 +39,6 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
ETHEREUM_PROVIDER_URL,
|
||||
Number(config.chain_id)
|
||||
);
|
||||
|
||||
const provider = activeProvider ? activeProvider : defaultProvider;
|
||||
|
||||
if (
|
||||
@@ -55,23 +55,28 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
signer || provider
|
||||
);
|
||||
const vegaAddress = await staking.staking_token();
|
||||
|
||||
setContracts({
|
||||
token: new Token(vegaAddress, signer || provider),
|
||||
staking: new StakingBridge(
|
||||
config.staking_bridge_contract.address,
|
||||
signer || provider
|
||||
),
|
||||
vesting: new TokenVesting(
|
||||
config.token_vesting_contract.address,
|
||||
signer || provider
|
||||
),
|
||||
claim: new Claim(ENV.addresses.claimAddress, signer || provider),
|
||||
});
|
||||
if (!cancelled) {
|
||||
setContracts({
|
||||
token: new Token(vegaAddress, signer || provider),
|
||||
staking: new StakingBridge(
|
||||
config.staking_bridge_contract.address,
|
||||
signer || provider
|
||||
),
|
||||
vesting: new TokenVesting(
|
||||
config.token_vesting_contract.address,
|
||||
signer || provider
|
||||
),
|
||||
claim: new Claim(ENV.addresses.claimAddress, signer || provider),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
run();
|
||||
return () => {
|
||||
// TODO: hacky quick fix for release to prevent race condition, find a better fix for this.
|
||||
cancelled = true;
|
||||
};
|
||||
}, [activeProvider, account, config, VEGA_ENV, ETHEREUM_PROVIDER_URL]);
|
||||
|
||||
if (!contracts) {
|
||||
|
||||
@@ -2,12 +2,12 @@ import './styles.css';
|
||||
|
||||
import * as Sentry from '@sentry/react';
|
||||
import { Integrations } from '@sentry/tracing';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import App from './app';
|
||||
import reportWebVitals from './report-web-vitals';
|
||||
import { ENV } from './config/env';
|
||||
import { StrictMode } from 'react';
|
||||
|
||||
const dsn = ENV.dsn || false;
|
||||
const environment = ENV.envName || 'local';
|
||||
@@ -38,11 +38,13 @@ if (dsn) {
|
||||
Sentry.setTag('commit', commit);
|
||||
}
|
||||
|
||||
ReactDOM.render(
|
||||
<React.StrictMode>
|
||||
const rootElement = document.getElementById('root');
|
||||
const root = rootElement && createRoot(rootElement);
|
||||
|
||||
root?.render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>,
|
||||
document.getElementById('root')
|
||||
</StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
|
||||
@@ -29,7 +29,7 @@ const ClaimIndex = ({ name }: RouteChildProps) => {
|
||||
if (error) {
|
||||
return (
|
||||
<Callout intent={Intent.Danger} title={t('errorLoadingTranches')}>
|
||||
{error}
|
||||
{error.message}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -84,6 +84,12 @@ afterAll(() => {
|
||||
});
|
||||
|
||||
describe('Proposals list', () => {
|
||||
it('Render a page title and link to the make proposal form', () => {
|
||||
render(renderComponent([]));
|
||||
expect(screen.getByText('Governance')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('new-proposal-link')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Culls failed proposals', () => {
|
||||
render(renderComponent([failedProposal]));
|
||||
expect(screen.queryByTestId('open-proposals')).not.toBeInTheDocument();
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { isFuture } from 'date-fns';
|
||||
import { useState } from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import { Routes } from '../../../router-config';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposals_proposals[];
|
||||
@@ -44,7 +47,17 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Heading title={t('pageTitleGovernance')} />
|
||||
<div className="grid xs:grid-cols-2 items-center">
|
||||
<Heading centerContent={false} title={t('pageTitleGovernance')} />
|
||||
<Link
|
||||
className="mb-16 xs:justify-self-end"
|
||||
data-testid="new-proposal-link"
|
||||
to={`${Routes.GOVERNANCE}/propose`}
|
||||
>
|
||||
<Button variant={'primary'}>{t('NewProposal')}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{failedProposalsCulled.length > 0 && (
|
||||
<ProposalsListFilter setFilterString={setFilterString} />
|
||||
)}
|
||||
|
||||
@@ -28,7 +28,7 @@ export const TokenDetails = ({
|
||||
if (error) {
|
||||
return (
|
||||
<Callout intent={Intent.Danger} title={t('errorLoadingTranches')}>
|
||||
{error}
|
||||
{error.message}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ const RedemptionRouter = () => {
|
||||
if (error) {
|
||||
return (
|
||||
<Callout intent={Intent.Danger} title={t('errorLoadingTranches')}>
|
||||
{error}
|
||||
{error.message}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
// @generated
|
||||
// This file was automatically generated and should not be edited.
|
||||
|
||||
import { AccountType } from "@vegaprotocol/types";
|
||||
|
||||
// ====================================================
|
||||
// GraphQL query operation: Rewards
|
||||
// ====================================================
|
||||
@@ -47,10 +45,6 @@ export interface Rewards_party_rewardDetails_rewards_epoch {
|
||||
|
||||
export interface Rewards_party_rewardDetails_rewards {
|
||||
__typename: "Reward";
|
||||
/**
|
||||
* The type of reward
|
||||
*/
|
||||
rewardType: AccountType;
|
||||
/**
|
||||
* The asset this reward is paid in
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,6 @@ export const REWARDS_QUERY = gql`
|
||||
symbol
|
||||
}
|
||||
rewards {
|
||||
rewardType
|
||||
asset {
|
||||
id
|
||||
}
|
||||
|
||||
@@ -19,6 +19,10 @@ interface RewardInfoProps {
|
||||
rewardAssetId: string;
|
||||
}
|
||||
|
||||
// Note: For now the only reward type is Staking. We'll need this from the API
|
||||
// at a later date
|
||||
const DEFAULT_REWARD_TYPE = 'Staking';
|
||||
|
||||
export const RewardInfo = ({
|
||||
data,
|
||||
currVegaKey,
|
||||
@@ -115,7 +119,7 @@ export const RewardTable = ({ reward, delegations }: RewardTableProps) => {
|
||||
<KeyValueTable>
|
||||
<KeyValueTableRow>
|
||||
{t('rewardType')}
|
||||
<span>{reward.rewardType}</span>
|
||||
<span>{DEFAULT_REWARD_TYPE}</span>
|
||||
</KeyValueTableRow>
|
||||
<KeyValueTableRow>
|
||||
{t('yourStake')}
|
||||
|
||||
@@ -52,6 +52,7 @@ export interface Nodes_nodes {
|
||||
* Amount of stake on the next epoch
|
||||
*/
|
||||
pendingStake: string;
|
||||
pendingStakeFormatted: string;
|
||||
/**
|
||||
* Ranking scores and status for the validator for the current epoch
|
||||
*/
|
||||
|
||||
@@ -34,6 +34,7 @@ export const NODES_QUERY = gql`
|
||||
stakedTotal
|
||||
stakedTotalFormatted @client
|
||||
pendingStake
|
||||
pendingStakeFormatted @client
|
||||
rankingScore {
|
||||
rankingScore
|
||||
stakeScore
|
||||
@@ -122,7 +123,7 @@ export const NodeList = ({ epoch }: NodeListProps) => {
|
||||
performanceScore,
|
||||
votingPower,
|
||||
},
|
||||
pendingStake,
|
||||
pendingStakeFormatted,
|
||||
}) => {
|
||||
const stakedTotal = new BigNumber(
|
||||
data?.nodeData?.stakedTotalFormatted || 0
|
||||
@@ -142,10 +143,10 @@ export const NodeList = ({ epoch }: NodeListProps) => {
|
||||
name,
|
||||
},
|
||||
[STATUS]: statusTranslated,
|
||||
[TOTAL_STAKE_THIS_EPOCH]: formatNumber(stakedTotal, 2),
|
||||
[TOTAL_STAKE_THIS_EPOCH]: formatNumber(stakedTotalFormatted, 2),
|
||||
[SHARE]: stakedTotalPercentage,
|
||||
[VALIDATOR_STAKE]: formatNumber(stakedOnNode, 2),
|
||||
[PENDING_STAKE]: formatNumber(pendingStake, 2),
|
||||
[PENDING_STAKE]: formatNumber(pendingStakeFormatted, 2),
|
||||
[RANKING_SCORE]: formatNumber(new BigNumber(rankingScore), 5),
|
||||
[STAKE_SCORE]: formatNumber(new BigNumber(stakeScore), 5),
|
||||
[PERFORMANCE_SCORE]: formatNumber(new BigNumber(performanceScore), 5),
|
||||
|
||||
@@ -24,7 +24,7 @@ const TrancheRouter = ({ name }: RouteChildProps) => {
|
||||
if (error) {
|
||||
return (
|
||||
<Callout intent={Intent.Danger} title={t('errorLoadingTranches')}>
|
||||
{error}
|
||||
{error.message}
|
||||
</Callout>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export * from './trading-mode-tooltip';
|
||||
@@ -0,0 +1,201 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import {
|
||||
t,
|
||||
getDateTimeFormat,
|
||||
addDecimalsFormatNumber,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { Link } from '@vegaprotocol/ui-toolkit';
|
||||
import { MarketTradingMode, AuctionTrigger } from '@vegaprotocol/types';
|
||||
import type { Market_market } from '../../pages/markets/__generated__/Market';
|
||||
|
||||
type MarketDataGridProps = {
|
||||
grid: {
|
||||
label: string;
|
||||
value?: ReactNode;
|
||||
isEstimate?: boolean;
|
||||
}[];
|
||||
};
|
||||
|
||||
const MarketDataGrid = ({ grid }: MarketDataGridProps) => {
|
||||
return (
|
||||
<>
|
||||
{grid.map(
|
||||
({ label, value, isEstimate }, index) =>
|
||||
value && (
|
||||
<div key={index} className="grid grid-cols-2">
|
||||
<span>{label}</span>
|
||||
<span>
|
||||
{isEstimate && <span className="ml-[-0.625em]">{'~'}</span>}
|
||||
{value}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const formatStake = (value: string, market: Market_market) => {
|
||||
const formattedValue = addDecimalsFormatNumber(
|
||||
value,
|
||||
market.positionDecimalPlaces
|
||||
);
|
||||
const asset =
|
||||
market.tradableInstrument.instrument.product.settlementAsset.symbol;
|
||||
return `${formattedValue} ${asset}`;
|
||||
};
|
||||
|
||||
const compileGridData = (market: Market_market) => {
|
||||
const grid: MarketDataGridProps['grid'] = [];
|
||||
const isLiquidityMonitoringAuction =
|
||||
market.tradingMode === MarketTradingMode.MonitoringAuction &&
|
||||
market.data?.trigger === AuctionTrigger.Liquidity;
|
||||
|
||||
if (!market.data) return grid;
|
||||
|
||||
if (market.data?.auctionStart) {
|
||||
grid.push({
|
||||
label: t('Auction start'),
|
||||
value: getDateTimeFormat().format(new Date(market.data.auctionStart)),
|
||||
});
|
||||
}
|
||||
|
||||
if (market.data?.auctionEnd) {
|
||||
grid.push({
|
||||
label: isLiquidityMonitoringAuction
|
||||
? t('Est auction end')
|
||||
: t('Auction end'),
|
||||
value: getDateTimeFormat().format(new Date(market.data.auctionEnd)),
|
||||
isEstimate: isLiquidityMonitoringAuction ? true : false,
|
||||
});
|
||||
}
|
||||
|
||||
if (isLiquidityMonitoringAuction && market.data?.targetStake) {
|
||||
grid.push({
|
||||
label: t('Target liquidity'),
|
||||
value: formatStake(market.data.targetStake, market),
|
||||
});
|
||||
}
|
||||
|
||||
if (isLiquidityMonitoringAuction && market.data?.suppliedStake) {
|
||||
grid.push({
|
||||
label: t('Current liquidity'),
|
||||
// @TODO: link this to liquidity view when https://github.com/vegaprotocol/frontend-monorepo/issues/491 is done
|
||||
value: formatStake(market.data.suppliedStake, market),
|
||||
});
|
||||
}
|
||||
|
||||
if (market.data?.indicativePrice) {
|
||||
grid.push({
|
||||
label: t('Est uncrossing price'),
|
||||
value: addDecimalsFormatNumber(
|
||||
market.data.indicativePrice,
|
||||
market.positionDecimalPlaces
|
||||
),
|
||||
isEstimate: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (market.data?.indicativeVolume) {
|
||||
grid.push({
|
||||
label: t('Est uncrossing vol'),
|
||||
value: addDecimalsFormatNumber(
|
||||
market.data.indicativeVolume,
|
||||
market.positionDecimalPlaces
|
||||
),
|
||||
isEstimate: true,
|
||||
});
|
||||
}
|
||||
|
||||
return grid;
|
||||
};
|
||||
|
||||
type TradingModeTooltipProps = {
|
||||
market: Market_market;
|
||||
};
|
||||
|
||||
export const TradingModeTooltip = ({ market }: TradingModeTooltipProps) => {
|
||||
switch (market.tradingMode) {
|
||||
case MarketTradingMode.Continuous: {
|
||||
return (
|
||||
<>
|
||||
{t(
|
||||
'This is the standard trading mode where trades are executed whenever orders are received.'
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case MarketTradingMode.OpeningAuction: {
|
||||
return (
|
||||
<>
|
||||
<p className="mb-16">
|
||||
<span>
|
||||
{t(
|
||||
'This new market is in an opening auction to determine a fair mid-price before starting continuous trading.'
|
||||
)}
|
||||
</span>{' '}
|
||||
<Link
|
||||
href="https://docs.fairground.vega.xyz/docs/trading-questions/#auctions-what-happens-in-an-opening-auction"
|
||||
target="_blank"
|
||||
>
|
||||
{t('Find out more')}
|
||||
</Link>
|
||||
</p>
|
||||
<MarketDataGrid grid={compileGridData(market)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
case MarketTradingMode.MonitoringAuction: {
|
||||
switch (market.data?.trigger) {
|
||||
case AuctionTrigger.Liquidity: {
|
||||
return (
|
||||
<>
|
||||
<p className="mb-16">
|
||||
<span>
|
||||
{t(
|
||||
'This market is in auction until it reaches sufficient liquidity.'
|
||||
)}
|
||||
</span>{' '}
|
||||
<Link
|
||||
href="https://docs.fairground.vega.xyz/docs/trading-questions/#auctions-what-is-a-liquidity-monitoring-auction"
|
||||
target="_blank"
|
||||
>
|
||||
{t('Find out more')}
|
||||
</Link>
|
||||
</p>
|
||||
<MarketDataGrid grid={compileGridData(market)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
case AuctionTrigger.Price: {
|
||||
return (
|
||||
<>
|
||||
<p className="mb-16">
|
||||
<span>
|
||||
{t('This market is in auction due to high price volatility.')}
|
||||
</span>{' '}
|
||||
<Link
|
||||
href="https://docs.fairground.vega.xyz/docs/trading-questions/#auctions-what-is-a-price-monitoring-auction"
|
||||
target="_blank"
|
||||
>
|
||||
{t('Find out more')}
|
||||
</Link>
|
||||
</p>
|
||||
<MarketDataGrid grid={compileGridData(market)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
case MarketTradingMode.NoTrading: {
|
||||
return <>{t('No trading enabled for this market.')}</>;
|
||||
}
|
||||
case MarketTradingMode.BatchAuction:
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -25,13 +25,17 @@ const MARKET_QUERY = gql`
|
||||
market {
|
||||
id
|
||||
}
|
||||
auctionStart
|
||||
auctionEnd
|
||||
markPrice
|
||||
indicativeVolume
|
||||
indicativePrice
|
||||
suppliedStake
|
||||
targetStake
|
||||
bestBidVolume
|
||||
bestOfferVolume
|
||||
bestStaticBidVolume
|
||||
bestStaticOfferVolume
|
||||
indicativeVolume
|
||||
trigger
|
||||
}
|
||||
tradableInstrument {
|
||||
|
||||
+20
@@ -23,6 +23,14 @@ export interface Market_market_data {
|
||||
* market id of the associated mark price
|
||||
*/
|
||||
market: Market_market_data_market;
|
||||
/**
|
||||
* RFC3339Nano time at which the next auction will start (null if none is scheduled)
|
||||
*/
|
||||
auctionStart: string | null;
|
||||
/**
|
||||
* RFC3339Nano time at which the auction will stop (null if not in auction mode)
|
||||
*/
|
||||
auctionEnd: string | null;
|
||||
/**
|
||||
* the mark price (actually an unsigned int)
|
||||
*/
|
||||
@@ -31,6 +39,18 @@ export interface Market_market_data {
|
||||
* indicative volume if the auction ended now, 0 if not in auction mode
|
||||
*/
|
||||
indicativeVolume: string;
|
||||
/**
|
||||
* indicative price if the auction ended now, 0 if not in auction mode
|
||||
*/
|
||||
indicativePrice: string;
|
||||
/**
|
||||
* the supplied stake for the market
|
||||
*/
|
||||
suppliedStake: string | null;
|
||||
/**
|
||||
* the amount of stake targeted for this market
|
||||
*/
|
||||
targetStake: string | null;
|
||||
/**
|
||||
* the aggregated volume being bid at the best bid price.
|
||||
*/
|
||||
|
||||
@@ -25,12 +25,14 @@ import {
|
||||
Tab,
|
||||
Tabs,
|
||||
PriceCellChange,
|
||||
Tooltip,
|
||||
ResizablePanel,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import type { CandleClose } from '@vegaprotocol/types';
|
||||
import { AuctionTrigger } from '@vegaprotocol/types';
|
||||
import { MarketTradingMode } from '@vegaprotocol/types';
|
||||
import { Allotment, LayoutPriority } from 'allotment';
|
||||
import { TradingModeTooltip } from '../../components/trading-mode-tooltip';
|
||||
|
||||
const TradingViews = {
|
||||
Candles: CandlesChartContainer,
|
||||
@@ -102,18 +104,23 @@ export const TradeMarketHeader = ({
|
||||
: '-'}
|
||||
</span>
|
||||
</div>
|
||||
<div className={headerItemClassName}>
|
||||
<span className={itemClassName}>{t('Trading mode')}</span>
|
||||
<span data-testid="trading-mode" className={itemValueClassName}>
|
||||
{market.tradingMode === MarketTradingMode.MonitoringAuction &&
|
||||
market.data?.trigger &&
|
||||
market.data.trigger !== AuctionTrigger.Unspecified
|
||||
? `${formatLabel(
|
||||
market.tradingMode
|
||||
)} - ${market.data?.trigger.toLowerCase()}`
|
||||
: formatLabel(market.tradingMode)}
|
||||
</span>
|
||||
</div>
|
||||
<Tooltip
|
||||
align="start"
|
||||
description={<TradingModeTooltip market={market} />}
|
||||
>
|
||||
<div className={headerItemClassName}>
|
||||
<span className={itemClassName}>{t('Trading mode')}</span>
|
||||
<span data-testid="trading-mode" className={itemValueClassName}>
|
||||
{market.tradingMode === MarketTradingMode.MonitoringAuction &&
|
||||
market.data?.trigger &&
|
||||
market.data.trigger !== AuctionTrigger.Unspecified
|
||||
? `${formatLabel(
|
||||
market.tradingMode
|
||||
)} - ${market.data?.trigger.toLowerCase()}`
|
||||
: formatLabel(market.tradingMode)}
|
||||
</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className={headerItemClassName}>
|
||||
<span className={itemClassName}>{t('Price')}</span>
|
||||
<span data-testid="mark-price" className={itemValueClassName}>
|
||||
|
||||
@@ -21,41 +21,43 @@ const singleRow: Accounts_party_accounts = {
|
||||
};
|
||||
const singleRowData = [singleRow];
|
||||
|
||||
it('should render successfully', async () => {
|
||||
await act(async () => {
|
||||
const { baseElement } = render(<AccountsTable data={[]} />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('Render correct columns', async () => {
|
||||
await act(async () => {
|
||||
render(<AccountsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const headers = await screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(4);
|
||||
expect(
|
||||
headers.map((h) =>
|
||||
h.querySelector('[ref="eText"]')?.textContent?.trim()
|
||||
)
|
||||
).toEqual(['Asset', 'Type', 'Market', 'Balance']);
|
||||
describe('AccountsTable', () => {
|
||||
it('should render successfully', async () => {
|
||||
await act(async () => {
|
||||
const { baseElement } = render(<AccountsTable data={[]} />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Correct formatting applied', async () => {
|
||||
await act(async () => {
|
||||
render(<AccountsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const cells = await screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
'tBTC',
|
||||
singleRow.type,
|
||||
'BTCUSD Monthly (30 Jun 2022)',
|
||||
'1,256.00000',
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
it('should render correct columns', async () => {
|
||||
act(async () => {
|
||||
render(<AccountsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const headers = await screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(4);
|
||||
expect(
|
||||
headers.map((h) =>
|
||||
h.querySelector('[ref="eText"]')?.textContent?.trim()
|
||||
)
|
||||
).toEqual(['Asset', 'Type', 'Market', 'Balance']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply correct formatting', async () => {
|
||||
act(async () => {
|
||||
render(<AccountsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const cells = await screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
'tBTC',
|
||||
singleRow.type,
|
||||
'BTCUSD Monthly (30 Jun 2022)',
|
||||
'1,256.00000',
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => {
|
||||
);
|
||||
|
||||
const handleMarketSelect = useCallback(
|
||||
({ id, name }) => {
|
||||
({ id, name }: { id: string; name: string }) => {
|
||||
setLookup(name);
|
||||
setShowPane(false);
|
||||
setMarket(id);
|
||||
@@ -163,7 +163,7 @@ export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => {
|
||||
}, [showPane, setShowPane, setSkip, inputRef]);
|
||||
|
||||
const handleDialogOnchange = useCallback(
|
||||
(isOpen) => {
|
||||
(isOpen: boolean) => {
|
||||
setShowPane(isOpen);
|
||||
if (!isOpen) {
|
||||
setLookup(lookup || market.name);
|
||||
|
||||
@@ -31,7 +31,7 @@ export const DepositManager = ({
|
||||
const faucet = useSubmitFaucet();
|
||||
|
||||
const handleSelectAsset = useCallback(
|
||||
(id) => {
|
||||
(id: string) => {
|
||||
const asset = assets.find((a) => a.id === id);
|
||||
if (!asset) return;
|
||||
update({ asset });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import type { EnvironmentWithOptionalUrl } from './use-config';
|
||||
import { useConfig } from './use-config';
|
||||
import { Networks, ErrorType } from '../types';
|
||||
@@ -75,22 +75,23 @@ describe('useConfig hook', () => {
|
||||
});
|
||||
|
||||
it('fetches configuration from the provided url', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useConfig(mockEnvironment, onError)
|
||||
);
|
||||
const { result } = renderHook(() => useConfig(mockEnvironment, onError));
|
||||
|
||||
await waitForNextUpdate();
|
||||
expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
|
||||
expect(result.current.config).toEqual(mockConfig);
|
||||
await waitFor(() => {
|
||||
expect(fetch).toHaveBeenCalledWith(mockEnvironment.VEGA_CONFIG_URL);
|
||||
expect(result.current.config).toEqual(mockConfig);
|
||||
});
|
||||
});
|
||||
|
||||
it('caches the configuration', async () => {
|
||||
const { result: firstResult, waitForNextUpdate: waitForFirstUpdate } =
|
||||
renderHook(() => useConfig(mockEnvironment, onError));
|
||||
const { result: firstResult } = renderHook(() =>
|
||||
useConfig(mockEnvironment, onError)
|
||||
);
|
||||
|
||||
await waitForFirstUpdate();
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
expect(firstResult.current.config).toEqual(mockConfig);
|
||||
await waitFor(() => {
|
||||
expect(fetch).toHaveBeenCalledTimes(1);
|
||||
expect(firstResult.current.config).toEqual(mockConfig);
|
||||
});
|
||||
|
||||
const { result: secondResult } = renderHook(() =>
|
||||
useConfig(mockEnvironment, onError)
|
||||
@@ -104,13 +105,12 @@ describe('useConfig hook', () => {
|
||||
// @ts-ignore typescript doesn't recognise the mocked instance
|
||||
global.fetch.mockImplementation(() => Promise.reject());
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useConfig(mockEnvironment, onError)
|
||||
);
|
||||
const { result } = renderHook(() => useConfig(mockEnvironment, onError));
|
||||
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.config).toEqual({ hosts: [] });
|
||||
expect(onError).toHaveBeenCalledWith(ErrorType.CONFIG_LOAD_ERROR);
|
||||
await waitFor(() => {
|
||||
expect(result.current.config).toEqual({ hosts: [] });
|
||||
expect(onError).toHaveBeenCalledWith(ErrorType.CONFIG_LOAD_ERROR);
|
||||
});
|
||||
});
|
||||
|
||||
it('executes the error callback when the config validation fails', async () => {
|
||||
@@ -122,12 +122,11 @@ describe('useConfig hook', () => {
|
||||
})
|
||||
);
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useConfig(mockEnvironment, onError)
|
||||
);
|
||||
const { result } = renderHook(() => useConfig(mockEnvironment, onError));
|
||||
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.config).toBe(undefined);
|
||||
expect(onError).toHaveBeenCalledWith(ErrorType.CONFIG_VALIDATION_ERROR);
|
||||
await waitFor(() => {
|
||||
expect(result.current.config).toBe(undefined);
|
||||
expect(onError).toHaveBeenCalledWith(ErrorType.CONFIG_VALIDATION_ERROR);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,199 @@
|
||||
// having the node switcher dialog in the environment provider breaks the test renderer
|
||||
// workaround based on: https://github.com/facebook/react/issues/11565
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import createClient from '../utils/apollo-client';
|
||||
import { useEnvironment, EnvironmentProvider } from './use-environment';
|
||||
import { Networks } from '../types';
|
||||
import createMockClient from './mocks/apollo-client';
|
||||
jest.mock('../utils/apollo-client');
|
||||
|
||||
jest.mock('react-dom', () => ({
|
||||
...jest.requireActual('react-dom'),
|
||||
createPortal: (node: ReactNode) => node,
|
||||
}));
|
||||
|
||||
global.fetch = jest.fn();
|
||||
|
||||
const MockWrapper = (props: ComponentProps<typeof EnvironmentProvider>) => {
|
||||
return <EnvironmentProvider {...props} />;
|
||||
};
|
||||
|
||||
const MOCK_HOST = 'https://vega.host/query';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const noop = () => {};
|
||||
|
||||
const mockEnvironmentState = {
|
||||
VEGA_URL: 'https://vega.xyz',
|
||||
VEGA_ENV: Networks.TESTNET,
|
||||
VEGA_CONFIG_URL: 'https://vega.xyz/testnet-config.json',
|
||||
VEGA_NETWORKS: {
|
||||
TESTNET: 'https://testnet.url',
|
||||
STAGNET: 'https://stagnet.url',
|
||||
MAINNET: 'https://mainnet.url',
|
||||
},
|
||||
ETHEREUM_PROVIDER_URL: 'https://ether.provider',
|
||||
ETHERSCAN_URL: 'https://etherscan.url',
|
||||
GIT_BRANCH: 'test',
|
||||
GIT_ORIGIN_URL: 'https://github.com/test/repo',
|
||||
GIT_COMMIT_HASH: 'abcde01234',
|
||||
GITHUB_FEEDBACK_URL: 'https://github.com/test/feedback',
|
||||
setNodeSwitcherOpen: noop,
|
||||
networkError: undefined,
|
||||
};
|
||||
|
||||
const MOCK_DURATION = 76;
|
||||
|
||||
window.performance.getEntriesByName = jest
|
||||
.fn()
|
||||
.mockImplementation((url: string) => [
|
||||
{
|
||||
entryType: 'resource',
|
||||
name: url,
|
||||
startTime: 0,
|
||||
toJSON: () => ({}),
|
||||
duration: MOCK_DURATION,
|
||||
},
|
||||
]);
|
||||
|
||||
function setupFetch(
|
||||
configUrl: string = mockEnvironmentState.VEGA_CONFIG_URL,
|
||||
hosts?: string[]
|
||||
) {
|
||||
return (url: RequestInfo) => {
|
||||
if (url === configUrl) {
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({ hosts: hosts || [MOCK_HOST] }),
|
||||
} as Response);
|
||||
}
|
||||
|
||||
return Promise.resolve({
|
||||
ok: true,
|
||||
} as Response);
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(setupFetch());
|
||||
|
||||
window.localStorage.clear();
|
||||
|
||||
// @ts-ignore allow adding a mock return value to mocked module
|
||||
createClient.mockImplementation(() => createMockClient());
|
||||
|
||||
process.env['NX_VEGA_URL'] = mockEnvironmentState.VEGA_URL;
|
||||
process.env['NX_VEGA_ENV'] = mockEnvironmentState.VEGA_ENV;
|
||||
process.env['NX_VEGA_CONFIG_URL'] = mockEnvironmentState.VEGA_CONFIG_URL;
|
||||
process.env['NX_VEGA_NETWORKS'] = JSON.stringify(
|
||||
mockEnvironmentState.VEGA_NETWORKS
|
||||
);
|
||||
process.env['NX_ETHEREUM_PROVIDER_URL'] =
|
||||
mockEnvironmentState.ETHEREUM_PROVIDER_URL;
|
||||
process.env['NX_ETHERSCAN_URL'] = mockEnvironmentState.ETHERSCAN_URL;
|
||||
process.env['NX_GIT_BRANCH'] = mockEnvironmentState.GIT_BRANCH;
|
||||
process.env['NX_GIT_ORIGIN_URL'] = mockEnvironmentState.GIT_ORIGIN_URL;
|
||||
process.env['NX_GIT_COMMIT_HASH'] = mockEnvironmentState.GIT_COMMIT_HASH;
|
||||
process.env['NX_GITHUB_FEEDBACK_URL'] =
|
||||
mockEnvironmentState.GITHUB_FEEDBACK_URL;
|
||||
});
|
||||
|
||||
describe('throws error', () => {
|
||||
beforeEach(() => {
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(setupFetch());
|
||||
|
||||
window.localStorage.clear();
|
||||
|
||||
// @ts-ignore allow adding a mock return value to mocked module
|
||||
createClient.mockImplementation(() => createMockClient());
|
||||
|
||||
process.env['NX_VEGA_URL'] = mockEnvironmentState.VEGA_URL;
|
||||
process.env['NX_VEGA_ENV'] = mockEnvironmentState.VEGA_ENV;
|
||||
process.env['NX_VEGA_CONFIG_URL'] = mockEnvironmentState.VEGA_CONFIG_URL;
|
||||
process.env['NX_VEGA_NETWORKS'] = JSON.stringify(
|
||||
mockEnvironmentState.VEGA_NETWORKS
|
||||
);
|
||||
process.env['NX_ETHEREUM_PROVIDER_URL'] =
|
||||
mockEnvironmentState.ETHEREUM_PROVIDER_URL;
|
||||
process.env['NX_ETHERSCAN_URL'] = mockEnvironmentState.ETHERSCAN_URL;
|
||||
process.env['NX_GIT_BRANCH'] = mockEnvironmentState.GIT_BRANCH;
|
||||
process.env['NX_GIT_ORIGIN_URL'] = mockEnvironmentState.GIT_ORIGIN_URL;
|
||||
process.env['NX_GIT_COMMIT_HASH'] = mockEnvironmentState.GIT_COMMIT_HASH;
|
||||
process.env['NX_GITHUB_FEEDBACK_URL'] =
|
||||
mockEnvironmentState.GITHUB_FEEDBACK_URL;
|
||||
});
|
||||
|
||||
beforeEach(() => jest.resetModules()); // clears the cache of the modules
|
||||
it('throws a validation error when NX_ETHERSCAN_URL is not a valid url', () => {
|
||||
process.env['NX_ETHERSCAN_URL'] = 'invalid-url';
|
||||
const result = () =>
|
||||
renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result).toThrow(
|
||||
`The NX_ETHERSCAN_URL environment variable must be a valid url`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when NX_ETHEREUM_PROVIDER_URL is not a valid url', () => {
|
||||
process.env['NX_ETHEREUM_PROVIDER_URL'] = 'invalid-url';
|
||||
const result = () =>
|
||||
renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result).toThrow(
|
||||
`The NX_ETHEREUM_PROVIDER_URL environment variable must be a valid url`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when VEGA_NETWORKS is has an invalid network as a key', () => {
|
||||
process.env['NX_VEGA_NETWORKS'] = JSON.stringify({
|
||||
NOT_A_NETWORK: 'https://somewhere.url',
|
||||
});
|
||||
const result = () =>
|
||||
renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result).toThrow(
|
||||
`All keys in NX_VEGA_NETWORKS must represent a valid environment: CUSTOM | TESTNET | STAGNET | STAGNET2 | DEVNET | MAINNET`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when both VEGA_URL and VEGA_CONFIG_URL are missing in the environment', () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
delete process.env['NX_VEGA_CONFIG_URL'];
|
||||
const result = () =>
|
||||
renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result).toThrow(
|
||||
`Must provide either NX_VEGA_CONFIG_URL or NX_VEGA_URL in the environment.`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when NX_VEGA_ENV is not found in the environment', () => {
|
||||
delete process.env['NX_VEGA_ENV'];
|
||||
const result = () =>
|
||||
renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result).toThrow(
|
||||
`NX_VEGA_ENV is invalid, received "undefined" instead of: 'CUSTOM' | 'TESTNET' | 'STAGNET' | 'STAGNET2' | 'DEVNET' | 'MAINNET'`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when VEGA_ENV is not a valid network', () => {
|
||||
process.env['NX_VEGA_ENV'] = 'SOMETHING';
|
||||
const result = () =>
|
||||
renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result).not.toThrow(
|
||||
`Error processing the vega app environment:
|
||||
- NX_VEGA_ENV is invalid, received "SOMETHING" instead of: CUSTOM | TESTNET | STAGNET | STAGNET2 | DEVNET | MAINNET`
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
// having the node switcher dialog in the environment provider breaks the test renderer
|
||||
// workaround based on: https://github.com/facebook/react/issues/11565
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook, waitFor, act } from '@testing-library/react';
|
||||
import createClient from '../utils/apollo-client';
|
||||
import { useEnvironment, EnvironmentProvider } from './use-environment';
|
||||
import { Networks, ErrorType } from '../types';
|
||||
@@ -96,7 +96,7 @@ const getQuickestNode = (mockNodes: Record<string, MockRequestConfig>) => {
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(setupFetch());
|
||||
|
||||
window.localStorage.clear();
|
||||
@@ -126,165 +126,109 @@ afterAll(() => {
|
||||
|
||||
describe('useEnvironment hook', () => {
|
||||
it('transforms and exposes values from the environment', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
|
||||
it('allows for the VEGA_CONFIG_URL to be missing when there is a VEGA_URL present', async () => {
|
||||
delete process.env['NX_VEGA_CONFIG_URL'];
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_CONFIG_URL: undefined,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
await waitFor(() => {
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('allows for the VEGA_NETWORKS to be missing from the environment', async () => {
|
||||
it('allows for the VEGA_CONFIG_URL to be missing when there is a VEGA_URL present', async () => {
|
||||
delete process.env['NX_VEGA_CONFIG_URL'];
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_CONFIG_URL: undefined,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
|
||||
it('allows for the VEGA_NETWORKS to be missing from the environment', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_NETWORKS'];
|
||||
const { result, waitForNextUpdate } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_NETWORKS: {},
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
|
||||
it('throws a validation error when NX_VEGA_ENV is not found in the environment', async () => {
|
||||
delete process.env['NX_VEGA_ENV'];
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result.error?.message).toContain(
|
||||
`NX_VEGA_ENV is invalid, received "undefined" instead of: 'CUSTOM' | 'TESTNET' | 'STAGNET' | 'STAGNET2' | 'DEVNET' | 'MAINNET'`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when VEGA_ENV is not a valid network', async () => {
|
||||
process.env['NX_VEGA_ENV'] = 'SOMETHING';
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
await waitFor(() => {
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_NETWORKS: {},
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
expect(result.error).not.toContain(
|
||||
`NX_VEGA_ENV is invalid, received "SOMETHING" instead of: CUSTOM | TESTNET | STAGNET | STAGNET2 | DEVNET | MAINNET`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('when VEGA_NETWORKS is not a valid json, prints a warning and continues without using the value from it', async () => {
|
||||
it('when VEGA_NETWORKS is not a valid json, prints a warning and continues without using the value from it', async () => {
|
||||
act(async () => {
|
||||
const consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(noop);
|
||||
process.env['NX_VEGA_NETWORKS'] = '{not:{valid:json';
|
||||
const { result, waitForNextUpdate } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_NETWORKS: {},
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
await waitFor(() => {
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_NETWORKS: {},
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
|
||||
expect(consoleWarnSpy).toHaveBeenCalled();
|
||||
consoleWarnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
it('throws a validation error when VEGA_NETWORKS is has an invalid network as a key', async () => {
|
||||
process.env['NX_VEGA_NETWORKS'] = JSON.stringify({
|
||||
NOT_A_NETWORK: 'https://somewhere.url',
|
||||
});
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result.error?.message).toContain(
|
||||
`All keys in NX_VEGA_NETWORKS must represent a valid environment: CUSTOM | TESTNET | STAGNET | STAGNET2 | DEVNET | MAINNET`
|
||||
);
|
||||
});
|
||||
|
||||
it('throws a validation error when both VEGA_URL and VEGA_CONFIG_URL are missing in the environment', async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
delete process.env['NX_VEGA_CONFIG_URL'];
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result.error?.message).toContain(
|
||||
`Must provide either NX_VEGA_CONFIG_URL or NX_VEGA_URL in the environment.`
|
||||
);
|
||||
});
|
||||
|
||||
it.each`
|
||||
env | etherscanUrl | providerUrl
|
||||
${Networks.DEVNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.TESTNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.STAGNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.STAGNET2} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.MAINNET} | ${'https://etherscan.io'} | ${'https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
`(
|
||||
'uses correct default ethereum connection variables in $env',
|
||||
async ({ env, etherscanUrl, providerUrl }) => {
|
||||
it.each`
|
||||
env | etherscanUrl | providerUrl
|
||||
${Networks.DEVNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.TESTNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.STAGNET} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.STAGNET2} | ${'https://ropsten.etherscan.io'} | ${'https://ropsten.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
${Networks.MAINNET} | ${'https://etherscan.io'} | ${'https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'}
|
||||
`(
|
||||
'uses correct default ethereum connection variables in $env',
|
||||
async ({ env, etherscanUrl, providerUrl }) => {
|
||||
act(async () => {
|
||||
// @ts-ignore allow adding a mock return value to mocked module
|
||||
createClient.mockImplementation(() => createMockClient({ network: env }));
|
||||
|
||||
process.env['NX_VEGA_ENV'] = env;
|
||||
delete process.env['NX_ETHEREUM_PROVIDER_URL'];
|
||||
delete process.env['NX_ETHERSCAN_URL'];
|
||||
const { result, waitForNextUpdate } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_ENV: env,
|
||||
ETHEREUM_PROVIDER_URL: providerUrl,
|
||||
ETHERSCAN_URL: etherscanUrl,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
await waitFor(() => {
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_ENV: env,
|
||||
ETHEREUM_PROVIDER_URL: providerUrl,
|
||||
ETHERSCAN_URL: etherscanUrl,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('throws a validation error when NX_ETHERSCAN_URL is not a valid url', async () => {
|
||||
process.env['NX_ETHERSCAN_URL'] = 'invalid-url';
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result.error?.message).toContain(
|
||||
`The NX_ETHERSCAN_URL environment variable must be a valid url`
|
||||
);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('throws a validation error when NX_ETHEREUM_PROVIDER_URL is not a valid url', async () => {
|
||||
process.env['NX_ETHEREUM_PROVIDER_URL'] = 'invalid-url';
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
expect(result.error?.message).toContain(
|
||||
`The NX_ETHEREUM_PROVIDER_URL environment variable must be a valid url`
|
||||
);
|
||||
});
|
||||
|
||||
describe('node selection', () => {
|
||||
it('updates the VEGA_URL from the config when it is missing from the environment', async () => {
|
||||
describe('node selection', () => {
|
||||
it('updates the VEGA_URL from the config when it is missing from the environment', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_URL: MOCK_HOST,
|
||||
@@ -292,8 +236,10 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('updates the VEGA_URL with the quickest node to respond from the config urls', async () => {
|
||||
it('updates the VEGA_URL with the quickest node to respond from the config urls', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
|
||||
const mockNodes: Record<string, MockRequestConfig> = {
|
||||
@@ -303,7 +249,7 @@ describe('useEnvironment hook', () => {
|
||||
'https://mock-node-4.com': { hasError: false, delay: 0 },
|
||||
};
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(
|
||||
setupFetch(mockEnvironmentState.VEGA_CONFIG_URL, Object.keys(mockNodes))
|
||||
);
|
||||
@@ -314,12 +260,11 @@ describe('useEnvironment hook', () => {
|
||||
|
||||
const nodeUrl = getQuickestNode(mockNodes);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_URL: nodeUrl,
|
||||
@@ -327,8 +272,10 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores failing nodes and selects the first successful one to use', async () => {
|
||||
it('ignores failing nodes and selects the first successful one to use', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
|
||||
const mockNodes: Record<string, MockRequestConfig> = {
|
||||
@@ -338,7 +285,7 @@ describe('useEnvironment hook', () => {
|
||||
'https://mock-node-4.com': { hasError: true, delay: 0 },
|
||||
};
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(
|
||||
setupFetch(mockEnvironmentState.VEGA_CONFIG_URL, Object.keys(mockNodes))
|
||||
);
|
||||
@@ -349,12 +296,11 @@ describe('useEnvironment hook', () => {
|
||||
|
||||
const nodeUrl = getQuickestNode(mockNodes);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_URL: nodeUrl,
|
||||
@@ -362,8 +308,10 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has a network error when cannot connect to any nodes', async () => {
|
||||
it('has a network error when cannot connect to any nodes', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
|
||||
const mockNodes: Record<string, MockRequestConfig> = {
|
||||
@@ -373,7 +321,7 @@ describe('useEnvironment hook', () => {
|
||||
'https://mock-node-4.com': { hasError: true, delay: 0 },
|
||||
};
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(
|
||||
setupFetch(mockEnvironmentState.VEGA_CONFIG_URL, Object.keys(mockNodes))
|
||||
);
|
||||
@@ -382,12 +330,11 @@ describe('useEnvironment hook', () => {
|
||||
return createMockClient({ statistics: mockNodes[url] });
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_URL: undefined,
|
||||
@@ -396,21 +343,22 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has a network error when it cannot fetch the network config and there is no VEGA_URL in the environment', async () => {
|
||||
it('has a network error when it cannot fetch the network config and there is no VEGA_URL in the environment', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(() => {
|
||||
throw new Error('Cannot fetch');
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_URL: undefined,
|
||||
@@ -419,23 +367,24 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('logs an error when it cannot fetch the network config and there is a VEGA_URL in the environment', async () => {
|
||||
it('logs an error when it cannot fetch the network config and there is a VEGA_URL in the environment', async () => {
|
||||
act(async () => {
|
||||
const consoleWarnSpy = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(noop);
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(() => {
|
||||
throw new Error('Cannot fetch');
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
@@ -448,13 +397,15 @@ describe('useEnvironment hook', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// SKIP due to https://github.com/facebook/jest/issues/12670
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has a network error when the config is invalid and there is no VEGA_URL in the environment', async () => {
|
||||
// SKIP due to https://github.com/facebook/jest/issues/12670
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has a network error when the config is invalid and there is no VEGA_URL in the environment', async () => {
|
||||
act(async () => {
|
||||
delete process.env['NX_VEGA_URL'];
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
@@ -462,12 +413,11 @@ describe('useEnvironment hook', () => {
|
||||
})
|
||||
);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
VEGA_URL: undefined,
|
||||
@@ -476,15 +426,17 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// SKIP due to https://github.com/facebook/jest/issues/12670
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('logs an error when the network config in invalid and there is a VEGA_URL in the environment', async () => {
|
||||
// SKIP due to https://github.com/facebook/jest/issues/12670
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('logs an error when the network config in invalid and there is a VEGA_URL in the environment', async () => {
|
||||
act(async () => {
|
||||
const consoleWarnSpy = jest
|
||||
.spyOn(console, 'warn')
|
||||
.mockImplementation(noop);
|
||||
|
||||
// @ts-ignore: typscript doesn't recognise the mock implementation
|
||||
// @ts-ignore: typescript doesn't recognize the mock implementation
|
||||
global.fetch.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
@@ -492,12 +444,11 @@ describe('useEnvironment hook', () => {
|
||||
})
|
||||
);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
setNodeSwitcherOpen: result.current.setNodeSwitcherOpen,
|
||||
@@ -510,18 +461,19 @@ describe('useEnvironment hook', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// SKIP due to https://github.com/facebook/jest/issues/12670
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has a network error when the selected node is not a valid url', async () => {
|
||||
// SKIP due to https://github.com/facebook/jest/issues/12670
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('has a network error when the selected node is not a valid url', async () => {
|
||||
act(async () => {
|
||||
process.env['NX_VEGA_URL'] = 'not-url';
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
networkError: ErrorType.INVALID_URL,
|
||||
@@ -529,19 +481,20 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has a network error when cannot connect to the selected node', async () => {
|
||||
it('has a network error when cannot connect to the selected node', async () => {
|
||||
act(async () => {
|
||||
// @ts-ignore allow adding a mock return value to mocked module
|
||||
createClient.mockImplementation(() => {
|
||||
return createMockClient({ statistics: { hasError: true } });
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
networkError: ErrorType.CONNECTION_ERROR,
|
||||
@@ -549,19 +502,20 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has a network error when the selected node is not on the correct network', async () => {
|
||||
it('has a network error when the selected node is not on the correct network', async () => {
|
||||
act(async () => {
|
||||
// @ts-ignore allow adding a mock return value to mocked module
|
||||
createClient.mockImplementation(() => {
|
||||
return createMockClient({ network: Networks.MAINNET });
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
networkError: ErrorType.INVALID_NETWORK,
|
||||
@@ -569,19 +523,20 @@ describe('useEnvironment hook', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('has a network error when the selected node has not ssl available', async () => {
|
||||
it('has a network error when the selected node has not ssl available', async () => {
|
||||
act(async () => {
|
||||
// @ts-ignore allow adding a mock return value to mocked module
|
||||
createClient.mockImplementation(() => {
|
||||
return createMockClient({ busEvents: { hasError: true } });
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEnvironment(), {
|
||||
const { result } = renderHook(() => useEnvironment(), {
|
||||
wrapper: MockWrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.error).toBe(undefined);
|
||||
expect(result.current).toEqual({
|
||||
...mockEnvironmentState,
|
||||
networkError: ErrorType.SSL_ERROR,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { ApolloClient } from '@apollo/client';
|
||||
import createClient from '../utils/apollo-client';
|
||||
import { useNodes } from './use-nodes';
|
||||
import createMockClient, {
|
||||
getMockStatisticsResult,
|
||||
} from './mocks/apollo-client';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
jest.mock('../utils/apollo-client');
|
||||
|
||||
@@ -65,9 +66,7 @@ describe('useNodes hook', () => {
|
||||
|
||||
it('sets loading state while waiting for the results', async () => {
|
||||
const node = 'https://some.url';
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useNodes({ hosts: [node] })
|
||||
);
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
expect(result.current.state[node]).toEqual({
|
||||
...initialState,
|
||||
@@ -90,14 +89,12 @@ describe('useNodes hook', () => {
|
||||
isLoading: true,
|
||||
},
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
});
|
||||
|
||||
it('sets statistics results', async () => {
|
||||
const mockResult = getMockStatisticsResult();
|
||||
const node = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].block).toEqual({
|
||||
@@ -122,7 +119,7 @@ describe('useNodes hook', () => {
|
||||
|
||||
it('sets subscription result', async () => {
|
||||
const node = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].ssl).toEqual({
|
||||
@@ -135,7 +132,7 @@ describe('useNodes hook', () => {
|
||||
|
||||
it('sets error when host in not a valid url', async () => {
|
||||
const node = 'not-url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].block.hasError).toBe(true);
|
||||
@@ -152,7 +149,7 @@ describe('useNodes hook', () => {
|
||||
);
|
||||
|
||||
const node = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].block).toEqual({
|
||||
@@ -182,7 +179,7 @@ describe('useNodes hook', () => {
|
||||
);
|
||||
|
||||
const node = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].ssl).toEqual({
|
||||
@@ -196,7 +193,7 @@ describe('useNodes hook', () => {
|
||||
it('allows updating block values', async () => {
|
||||
const mockResult = getMockStatisticsResult();
|
||||
const node = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].block.value).toEqual(
|
||||
@@ -216,7 +213,7 @@ describe('useNodes hook', () => {
|
||||
it('does nothing when calling the block update on a non-existing node', async () => {
|
||||
const mockResult = getMockStatisticsResult();
|
||||
const node = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.state[node].block.value).toEqual(
|
||||
@@ -233,7 +230,7 @@ describe('useNodes hook', () => {
|
||||
|
||||
it('adds new node', async () => {
|
||||
const node = 'custom-node-key';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [] }));
|
||||
|
||||
expect(result.current.state[node]).toEqual(undefined);
|
||||
|
||||
@@ -249,7 +246,7 @@ describe('useNodes hook', () => {
|
||||
it('sets new url for node', async () => {
|
||||
const node = 'https://some.url';
|
||||
const newUrl = 'https://some-other.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [node] }));
|
||||
|
||||
act(() => {
|
||||
result.current.updateNodeUrl(node, newUrl);
|
||||
@@ -263,7 +260,7 @@ describe('useNodes hook', () => {
|
||||
it('sets error when custom node has an invalid url', async () => {
|
||||
const node = 'node-key';
|
||||
const url = 'not-url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [] }));
|
||||
|
||||
expect(result.current.state[node]).toBe(undefined);
|
||||
|
||||
@@ -288,7 +285,7 @@ describe('useNodes hook', () => {
|
||||
|
||||
const node = 'node-key';
|
||||
const url = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [] }));
|
||||
|
||||
expect(result.current.state[node]).toBe(undefined);
|
||||
|
||||
@@ -325,7 +322,7 @@ describe('useNodes hook', () => {
|
||||
|
||||
const node = 'node-key';
|
||||
const url = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [] }));
|
||||
|
||||
expect(result.current.state[node]).toBe(undefined);
|
||||
|
||||
@@ -345,9 +342,7 @@ describe('useNodes hook', () => {
|
||||
it('exposes a collection of clients', async () => {
|
||||
const url1 = 'https://some.url';
|
||||
const url2 = 'https://some-other.url';
|
||||
const { result, waitFor } = renderHook(() =>
|
||||
useNodes({ hosts: [url1, url2] })
|
||||
);
|
||||
const { result } = renderHook(() => useNodes({ hosts: [url1, url2] }));
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.clients[url1]).toBeInstanceOf(ApolloClient);
|
||||
@@ -358,7 +353,7 @@ describe('useNodes hook', () => {
|
||||
it('exposes a client for the custom node', async () => {
|
||||
const node = 'node-key';
|
||||
const url = 'https://some.url';
|
||||
const { result, waitFor } = renderHook(() => useNodes({ hosts: [] }));
|
||||
const { result } = renderHook(() => useNodes({ hosts: [] }));
|
||||
|
||||
act(() => {
|
||||
result.current.updateNodeUrl(node, url);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { render, screen, waitFor, act } from '@testing-library/react';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
|
||||
import { Side } from '@vegaprotocol/types';
|
||||
import type { PartialDeep } from 'type-fest';
|
||||
@@ -46,134 +46,142 @@ describe('FillsTable', () => {
|
||||
};
|
||||
});
|
||||
|
||||
it('correct columns are rendered', async () => {
|
||||
render(<FillsTable partyId="party-id" rowData={[generateFill()]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
it('should render correct columns', async () => {
|
||||
act(async () => {
|
||||
render(<FillsTable partyId="party-id" rowData={[generateFill()]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(7);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual([
|
||||
'Market',
|
||||
'Amount',
|
||||
'Value',
|
||||
'Filled value',
|
||||
'Role',
|
||||
'Fee',
|
||||
'Date',
|
||||
]);
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(7);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual([
|
||||
'Market',
|
||||
'Amount',
|
||||
'Value',
|
||||
'Filled value',
|
||||
'Role',
|
||||
'Fee',
|
||||
'Date',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats cells correctly for buyer fill', async () => {
|
||||
const partyId = 'party-id';
|
||||
const buyerFill = generateFill({
|
||||
...defaultFill,
|
||||
buyer: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Sell,
|
||||
buyerFee: {
|
||||
makerFee: '2',
|
||||
infrastructureFee: '2',
|
||||
liquidityFee: '2',
|
||||
},
|
||||
it('should format cells correctly for buyer fill', async () => {
|
||||
act(async () => {
|
||||
const partyId = 'party-id';
|
||||
const buyerFill = generateFill({
|
||||
...defaultFill,
|
||||
buyer: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Sell,
|
||||
buyerFee: {
|
||||
makerFee: '2',
|
||||
infrastructureFee: '2',
|
||||
liquidityFee: '2',
|
||||
},
|
||||
});
|
||||
|
||||
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market.name,
|
||||
'+3.00000',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
'Maker',
|
||||
'0.06 BTC',
|
||||
getDateTimeFormat().format(new Date(buyerFill.createdAt)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
|
||||
const amountCell = cells.find((c) => c.getAttribute('col-id') === 'size');
|
||||
expect(amountCell).toHaveClass('text-vega-green');
|
||||
});
|
||||
|
||||
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market.name,
|
||||
'+3.00000',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
'Maker',
|
||||
'0.06 BTC',
|
||||
getDateTimeFormat().format(new Date(buyerFill.createdAt)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
|
||||
const amountCell = cells.find((c) => c.getAttribute('col-id') === 'size');
|
||||
expect(amountCell).toHaveClass('text-vega-green');
|
||||
});
|
||||
|
||||
it('formats cells correctly for seller fill', async () => {
|
||||
const partyId = 'party-id';
|
||||
const buyerFill = generateFill({
|
||||
...defaultFill,
|
||||
seller: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Sell,
|
||||
sellerFee: {
|
||||
makerFee: '1',
|
||||
infrastructureFee: '1',
|
||||
liquidityFee: '1',
|
||||
},
|
||||
it('should format cells correctly for seller fill', async () => {
|
||||
act(async () => {
|
||||
const partyId = 'party-id';
|
||||
const buyerFill = generateFill({
|
||||
...defaultFill,
|
||||
seller: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Sell,
|
||||
sellerFee: {
|
||||
makerFee: '1',
|
||||
infrastructureFee: '1',
|
||||
liquidityFee: '1',
|
||||
},
|
||||
});
|
||||
|
||||
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market.name,
|
||||
'-3.00000',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
'Taker',
|
||||
'0.03 BTC',
|
||||
getDateTimeFormat().format(new Date(buyerFill.createdAt)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
|
||||
const amountCell = cells.find((c) => c.getAttribute('col-id') === 'size');
|
||||
expect(amountCell).toHaveClass('text-vega-red');
|
||||
});
|
||||
|
||||
render(<FillsTable partyId={partyId} rowData={[buyerFill]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
buyerFill.market.name,
|
||||
'-3.00000',
|
||||
'1.00 BTC',
|
||||
'3.00 BTC',
|
||||
'Taker',
|
||||
'0.03 BTC',
|
||||
getDateTimeFormat().format(new Date(buyerFill.createdAt)),
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
|
||||
const amountCell = cells.find((c) => c.getAttribute('col-id') === 'size');
|
||||
expect(amountCell).toHaveClass('text-vega-red');
|
||||
});
|
||||
|
||||
it('renders correct maker or taker role', async () => {
|
||||
const partyId = 'party-id';
|
||||
const takerFill = generateFill({
|
||||
seller: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Sell,
|
||||
it('should render correct maker or taker role', async () => {
|
||||
act(async () => {
|
||||
const partyId = 'party-id';
|
||||
const takerFill = generateFill({
|
||||
seller: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Sell,
|
||||
});
|
||||
|
||||
const { rerender } = render(
|
||||
<FillsTable partyId={partyId} rowData={[takerFill]} />
|
||||
);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
expect(
|
||||
screen
|
||||
.getAllByRole('gridcell')
|
||||
.find((c) => c.getAttribute('col-id') === 'aggressor')
|
||||
).toHaveTextContent('Taker');
|
||||
|
||||
const makerFill = generateFill({
|
||||
seller: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Buy,
|
||||
});
|
||||
|
||||
rerender(<FillsTable partyId={partyId} rowData={[makerFill]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
expect(
|
||||
screen
|
||||
.getAllByRole('gridcell')
|
||||
.find((c) => c.getAttribute('col-id') === 'aggressor')
|
||||
).toHaveTextContent('Maker');
|
||||
});
|
||||
|
||||
const { rerender } = render(
|
||||
<FillsTable partyId={partyId} rowData={[takerFill]} />
|
||||
);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
expect(
|
||||
screen
|
||||
.getAllByRole('gridcell')
|
||||
.find((c) => c.getAttribute('col-id') === 'aggressor')
|
||||
).toHaveTextContent('Taker');
|
||||
|
||||
const makerFill = generateFill({
|
||||
seller: {
|
||||
id: partyId,
|
||||
},
|
||||
aggressor: Side.Buy,
|
||||
});
|
||||
|
||||
rerender(<FillsTable partyId={partyId} rowData={[makerFill]} />);
|
||||
await waitForGridToBeInTheDOM();
|
||||
await waitForDataToHaveLoaded();
|
||||
|
||||
expect(
|
||||
screen
|
||||
.getAllByRole('gridcell')
|
||||
.find((c) => c.getAttribute('col-id') === 'aggressor')
|
||||
).toHaveTextContent('Maker');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -69,19 +69,22 @@ describe('ProposalForm', () => {
|
||||
it('handles validation', async () => {
|
||||
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve());
|
||||
setup(mockSendTx);
|
||||
|
||||
fireEvent.click(screen.getByTestId('proposal-submit'));
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('proposal-submit'));
|
||||
});
|
||||
expect(mockSendTx).not.toHaveBeenCalled();
|
||||
|
||||
expect(await screen.findByTestId('input-error-text')).toHaveTextContent(
|
||||
'Required'
|
||||
);
|
||||
|
||||
fireEvent.change(screen.getByTestId('proposal-data'), {
|
||||
target: { value: 'invalid' },
|
||||
await act(async () => {
|
||||
fireEvent.change(screen.getByTestId('proposal-data'), {
|
||||
target: { value: 'invalid' },
|
||||
});
|
||||
});
|
||||
await act(async () => {
|
||||
fireEvent.click(screen.getByTestId('proposal-submit'));
|
||||
});
|
||||
|
||||
fireEvent.click(screen.getByTestId('proposal-submit'));
|
||||
expect(mockSendTx).not.toHaveBeenCalled();
|
||||
|
||||
expect(await screen.findByTestId('input-error-text')).toHaveTextContent(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import type {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import type {
|
||||
VegaKeyExtended,
|
||||
VegaWalletContextShape,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import type { Order } from '../utils';
|
||||
import type {
|
||||
VegaKeyExtended,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import {
|
||||
VegaWalletOrderTimeInForce,
|
||||
VegaWalletOrderType,
|
||||
|
||||
@@ -56,46 +56,48 @@ const singleRow: Positions_party_positions = {
|
||||
};
|
||||
const singleRowData = [singleRow];
|
||||
|
||||
it('should render successfully', async () => {
|
||||
await act(async () => {
|
||||
const { baseElement } = render(<PositionsTable data={[]} />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('Render correct columns', async () => {
|
||||
await act(async () => {
|
||||
render(<PositionsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(5);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual([
|
||||
'Market',
|
||||
'Amount',
|
||||
'Average Entry Price',
|
||||
'Mark Price',
|
||||
'Realised PNL',
|
||||
]);
|
||||
describe('PositionsTable', () => {
|
||||
it('should render successfully', async () => {
|
||||
act(async () => {
|
||||
const { baseElement } = render(<PositionsTable data={[]} />);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Correct formatting applied', async () => {
|
||||
await act(async () => {
|
||||
render(<PositionsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
singleRow.market.tradableInstrument.instrument.code,
|
||||
'+100.00',
|
||||
'11.29935',
|
||||
'11.38885',
|
||||
'+5,200.000',
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
it('should render correct columns', async () => {
|
||||
act(async () => {
|
||||
render(<PositionsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const headers = screen.getAllByRole('columnheader');
|
||||
expect(headers).toHaveLength(5);
|
||||
expect(headers.map((h) => h.textContent?.trim())).toEqual([
|
||||
'Market',
|
||||
'Amount',
|
||||
'Average Entry Price',
|
||||
'Mark Price',
|
||||
'Realised PNL',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply correct formatting', () => {
|
||||
act(async () => {
|
||||
render(<PositionsTable data={singleRowData} />);
|
||||
await waitFor(async () => {
|
||||
const cells = screen.getAllByRole('gridcell');
|
||||
const expectedValues = [
|
||||
singleRow.market.tradableInstrument.instrument.code,
|
||||
'+100.00',
|
||||
'11.29935',
|
||||
'11.38885',
|
||||
'+5,200.000',
|
||||
];
|
||||
cells.forEach((cell, i) => {
|
||||
expect(cell).toHaveTextContent(expectedValues[i]);
|
||||
});
|
||||
expect(cells[cells.length - 1]).toHaveClass('color-vega-green');
|
||||
});
|
||||
expect(cells[cells.length - 1]).toHaveClass('color-vega-green');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useApplyGridTransaction } from './use-apply-grid-transaction';
|
||||
import { renderHook } from '@testing-library/react-hooks';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
|
||||
type Items = Array<{ id: string; value: number }>;
|
||||
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import type { OperationVariables } from '@apollo/client';
|
||||
import type { Subscribe, Load } from '../lib/generic-data-provider';
|
||||
import type {
|
||||
Subscribe,
|
||||
Load,
|
||||
UpdateCallback,
|
||||
} from '../lib/generic-data-provider';
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -54,14 +58,14 @@ export function useDataProvider<Data, Delta>({
|
||||
}
|
||||
return Promise.reject();
|
||||
}, []);
|
||||
const callback = useCallback(
|
||||
const callback = useCallback<UpdateCallback<Data, Delta>>(
|
||||
({ data, error, loading, delta, insertionData, totalCount }) => {
|
||||
setError(error);
|
||||
setLoading(loading);
|
||||
if (!error && !loading) {
|
||||
// if update or insert function returns true it means that component handles updates
|
||||
// component can use flush() which will call callback without delta and cause data state update
|
||||
if (initialized.current) {
|
||||
if (initialized.current && data) {
|
||||
if (delta && update && update({ delta, data })) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@ export const PriceCell = React.memo(
|
||||
return <span data-testid="price">-</span>;
|
||||
}
|
||||
const decimalSeparator = getDecimalSeparator();
|
||||
const valueSplit = decimalSeparator
|
||||
? valueFormatted.split(decimalSeparator)
|
||||
: [value];
|
||||
const valueSplit: string[] = decimalSeparator
|
||||
? valueFormatted.split(decimalSeparator).map((v) => `${v}`)
|
||||
: [`${value}`];
|
||||
return (
|
||||
<span
|
||||
className="font-mono relative text-ui-small text-black dark:text-white"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import ChevronDownIcon from './chevron-down';
|
||||
import classNames from 'classnames';
|
||||
import { ChevronDownIcon } from '@radix-ui/react-icons';
|
||||
|
||||
export interface AccordionItemProps {
|
||||
title: React.ReactNode;
|
||||
@@ -13,21 +13,20 @@ export interface AccordionProps {
|
||||
}
|
||||
|
||||
export const Accordion = ({ panels }: AccordionProps) => {
|
||||
const [value, setValue] = useState<string>('');
|
||||
const [values, setValues] = useState<string[]>([]);
|
||||
const triggerClassNames = classNames(
|
||||
'w-full py-2 box-border',
|
||||
'appearance-none cursor-pointer focus:outline-none',
|
||||
'flex items-center justify-between border-b border-muted',
|
||||
'text-left'
|
||||
'text-left text-black dark:text-white'
|
||||
);
|
||||
|
||||
return (
|
||||
<AccordionPrimitive.Root
|
||||
type="single"
|
||||
type="multiple"
|
||||
className="flex flex-col"
|
||||
value={value}
|
||||
onValueChange={setValue}
|
||||
collapsible
|
||||
value={values}
|
||||
onValueChange={setValues}
|
||||
>
|
||||
{panels.map(({ title, content }, i) => (
|
||||
<AccordionPrimitive.Item value={`item-${i + 1}`} key={`item-${i + 1}`}>
|
||||
@@ -37,12 +36,15 @@ export const Accordion = ({ panels }: AccordionProps) => {
|
||||
className={triggerClassNames}
|
||||
>
|
||||
<p
|
||||
className="inline-block text-footnote font-medium text-h6 text-black dark:text-white pt-5"
|
||||
className="inline-block text-footnote font-medium text-h6 pt-5"
|
||||
data-testid="accordion-title"
|
||||
>
|
||||
{title}
|
||||
</p>
|
||||
<ChevronDownIcon active={value === `item-${i + 1}`} aria-hidden />
|
||||
<AccordionChevron
|
||||
active={values.includes(`item-${i + 1}`)}
|
||||
aria-hidden
|
||||
/>
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
<AccordionPrimitive.Content
|
||||
@@ -58,3 +60,14 @@ export const Accordion = ({ panels }: AccordionProps) => {
|
||||
</AccordionPrimitive.Root>
|
||||
);
|
||||
};
|
||||
|
||||
export const AccordionChevron = ({ active }: { active: boolean }) => {
|
||||
return (
|
||||
<ChevronDownIcon
|
||||
className={classNames('w-20 h-20 transition ease-in-out duration-300', {
|
||||
'transform rotate-180': active,
|
||||
})}
|
||||
aria-hidden
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
export const ChevronDownIcon = ({ active }: ChevronDownIconProps) => {
|
||||
const rotate = active
|
||||
? 'transform duration-300 ease rotate-180'
|
||||
: 'transform duration-300 ease';
|
||||
return (
|
||||
<svg
|
||||
width="14"
|
||||
height="8"
|
||||
aria-label="chevron icon"
|
||||
data-testid="accordion-chevron-icon"
|
||||
viewBox="0 0 14 8"
|
||||
fill="fillCurrent"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className={`${rotate} inline-block fill-black dark:fill-white mx-4`}
|
||||
>
|
||||
<rect x="12" y="0" width="2" height="2" />
|
||||
<rect x="10" y="2" width="2" height="2" />
|
||||
<rect x="8" y="4" width="2" height="2" />
|
||||
<rect x="6" y="6" width="2" height="2" />
|
||||
<rect x="4" y="4" width="2" height="2" />
|
||||
<rect x="2" y="2" width="2" height="2" />
|
||||
<rect x="0" y="0" width="2" height="2" />
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export interface ChevronDownIconProps {
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export default ChevronDownIcon;
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
Provider,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
|
||||
export interface TooltipProps {
|
||||
children: React.ReactElement;
|
||||
description?: string;
|
||||
description?: string | ReactNode;
|
||||
open?: boolean;
|
||||
align?: 'start' | 'center' | 'end';
|
||||
}
|
||||
@@ -20,19 +21,19 @@ export const Tooltip = ({ children, description, open, align }: TooltipProps) =>
|
||||
<Provider delayDuration={200} skipDelayDuration={100}>
|
||||
<Root open={open}>
|
||||
<Trigger asChild>{children}</Trigger>
|
||||
<Content align={align} alignOffset={5}>
|
||||
<Content align={align} alignOffset={8}>
|
||||
<div className="relative z-0 p-8 bg-black-50 border border-black-60 text-white rounded-sm max-w-sm">
|
||||
{description}
|
||||
</div>
|
||||
<Arrow
|
||||
width={10}
|
||||
height={5}
|
||||
className="z-[1] fill-black-60 dark:fill-white-60 z-0 translate-x-[1px] translate-y-[-1px]"
|
||||
className="z-[1] mx-8 fill-black-60 dark:fill-white-60 z-0 translate-x-[1px] translate-y-[-1px]"
|
||||
/>
|
||||
<Arrow
|
||||
width={8}
|
||||
height={4}
|
||||
className="z-[1] translate-y-[-1px] fill-black-50"
|
||||
className="z-[1] mx-8 translate-y-[-1px] fill-black-50"
|
||||
/>
|
||||
</Content>
|
||||
</Root>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import type { VegaWalletContextShape } from './context';
|
||||
import { VegaWalletContext } from './context';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { renderHook, act } from '@testing-library/react-hooks/dom';
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { EthTxStatus } from './use-ethereum-transaction';
|
||||
import { useEthereumTransaction } from './use-ethereum-transaction';
|
||||
import type { ethers } from 'ethers';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { act, renderHook } from '@testing-library/react-hooks';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
+16
-14
@@ -14,6 +14,9 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.5.8",
|
||||
"@babel/plugin-proposal-export-default-from": "^7.18.10",
|
||||
"@babel/preset-env": "^7.18.10",
|
||||
"@babel/runtime": "^7.18.9",
|
||||
"@blueprintjs/icons": "^3.32.0",
|
||||
"@emotion/react": "^11.9.0",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
@@ -29,7 +32,7 @@
|
||||
"@sentry/nextjs": "^6.19.3",
|
||||
"@sentry/react": "^6.19.2",
|
||||
"@sentry/tracing": "^6.19.2",
|
||||
"@testing-library/user-event": "^14.2.1",
|
||||
"@testing-library/user-event": "^14.4.1",
|
||||
"@walletconnect/ethereum-provider": "^1.7.5",
|
||||
"@web3-react/core": "8.0.20-beta.0",
|
||||
"@web3-react/metamask": "8.0.16-beta.0",
|
||||
@@ -56,16 +59,16 @@
|
||||
"immer": "^9.0.12",
|
||||
"js-sha3": "^0.8.0",
|
||||
"lodash": "^4.17.21",
|
||||
"next": "^12.0.7",
|
||||
"next": "12.0.9",
|
||||
"pennant": "^0.4.12",
|
||||
"postcss": "^8.4.6",
|
||||
"react": "17.0.2",
|
||||
"react": "18.2.0",
|
||||
"react-copy-to-clipboard": "^5.0.4",
|
||||
"react-dom": "17.0.2",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.27.0",
|
||||
"react-i18next": "^11.11.4",
|
||||
"react-intersection-observer": "^9.2.2",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"react-router-dom": "6.3.0",
|
||||
"react-syntax-highlighter": "^15.4.5",
|
||||
"react-use-websocket": "^3.0.0",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
@@ -107,25 +110,24 @@
|
||||
"@storybook/react": "~6.4.12",
|
||||
"@svgr/webpack": "^5.4.0",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"@testing-library/react": "12.1.2",
|
||||
"@testing-library/react-hooks": "7.0.2",
|
||||
"@testing-library/react": "13.3.0",
|
||||
"@types/classnames": "^2.3.1",
|
||||
"@types/faker": "^5.5.8",
|
||||
"@types/jest": "27.0.2",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/lodash": "^4.14.171",
|
||||
"@types/node": "16.11.7",
|
||||
"@types/node": "18.0.4",
|
||||
"@types/prismjs": "^1.26.0",
|
||||
"@types/react": "17.0.39",
|
||||
"@types/react": "18.0.1",
|
||||
"@types/react-copy-to-clipboard": "^5.0.2",
|
||||
"@types/react-dom": "17.0.9",
|
||||
"@types/react-dom": "18.0.6",
|
||||
"@types/react-router-dom": "5.3.1",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@types/react-window-infinite-loader": "^1.0.6",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.18.0",
|
||||
"@typescript-eslint/parser": "5.18.0",
|
||||
"babel-jest": "27.2.3",
|
||||
"@typescript-eslint/eslint-plugin": "5.32.0",
|
||||
"@typescript-eslint/parser": "5.32.0",
|
||||
"babel-jest": "27.5.1",
|
||||
"babel-loader": "8.1.0",
|
||||
"cypress": "^10.2.0",
|
||||
"cypress-cucumber-preprocessor": "^4.3.1",
|
||||
|
||||
Reference in New Issue
Block a user