2023-10-03 15:35:17 +00:00
|
|
|
import { act, render, screen, waitFor, within } from '@testing-library/react';
|
2023-12-05 10:29:01 +00:00
|
|
|
// import userEvent from '@testing-library/user-event';
|
2023-07-18 09:37:25 +00:00
|
|
|
import { MemoryRouter } from 'react-router-dom';
|
2023-05-02 17:41:21 +00:00
|
|
|
import { Closed } from './closed';
|
2023-05-19 20:29:24 +00:00
|
|
|
import { MarketStateMapping, PropertyKeyType } from '@vegaprotocol/types';
|
2023-05-02 17:41:21 +00:00
|
|
|
import { MarketState } from '@vegaprotocol/types';
|
|
|
|
import { subDays } from 'date-fns';
|
|
|
|
import type { MockedResponse } from '@apollo/client/testing';
|
|
|
|
import { MockedProvider } from '@apollo/client/testing';
|
2023-05-18 11:22:54 +00:00
|
|
|
import type {
|
|
|
|
OracleSpecDataConnectionQuery,
|
|
|
|
MarketsDataQuery,
|
|
|
|
MarketsQuery,
|
|
|
|
} from '@vegaprotocol/markets';
|
|
|
|
import {
|
|
|
|
OracleSpecDataConnectionDocument,
|
|
|
|
MarketsDataDocument,
|
|
|
|
MarketsDocument,
|
2023-09-20 09:11:03 +00:00
|
|
|
getAsset,
|
2023-05-18 11:22:54 +00:00
|
|
|
} from '@vegaprotocol/markets';
|
2023-05-02 17:41:21 +00:00
|
|
|
import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
|
|
|
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
|
|
|
import { addDecimalsFormatNumber } from '@vegaprotocol/utils';
|
|
|
|
import {
|
|
|
|
createMarketFragment,
|
|
|
|
marketsQuery,
|
|
|
|
marketsDataQuery,
|
|
|
|
createMarketsDataFragment,
|
|
|
|
} from '@vegaprotocol/mock';
|
2023-12-05 10:29:01 +00:00
|
|
|
import userEvent from '@testing-library/user-event';
|
2023-07-25 15:32:40 +00:00
|
|
|
|
2023-05-02 17:41:21 +00:00
|
|
|
describe('Closed', () => {
|
|
|
|
let originalNow: typeof Date.now;
|
|
|
|
const mockNowTimestamp = 1672531200000;
|
|
|
|
const settlementDateMetaDate = subDays(
|
|
|
|
new Date(mockNowTimestamp),
|
|
|
|
3
|
|
|
|
).toISOString();
|
|
|
|
const settlementDateTag = `settlement-expiry-date:${settlementDateMetaDate}`;
|
|
|
|
const pubKey = 'pubKey';
|
|
|
|
const marketId = 'market-0';
|
|
|
|
const settlementDataProperty = 'spec-binding';
|
|
|
|
const settlementDataId = 'settlement-data-oracle-id';
|
|
|
|
|
|
|
|
const market = createMarketFragment({
|
|
|
|
id: marketId,
|
|
|
|
state: MarketState.STATE_SETTLED,
|
|
|
|
tradableInstrument: {
|
|
|
|
instrument: {
|
|
|
|
metadata: {
|
|
|
|
tags: [settlementDateTag],
|
|
|
|
},
|
|
|
|
product: {
|
2023-09-20 09:11:03 +00:00
|
|
|
__typename: 'Future',
|
2023-05-02 17:41:21 +00:00
|
|
|
dataSourceSpecForSettlementData: {
|
2023-09-20 09:11:03 +00:00
|
|
|
__typename: 'DataSourceSpec',
|
2023-05-02 17:41:21 +00:00
|
|
|
id: settlementDataId,
|
2023-05-19 20:29:24 +00:00
|
|
|
data: {
|
|
|
|
sourceType: {
|
2023-09-20 09:11:03 +00:00
|
|
|
__typename: 'DataSourceDefinitionExternal',
|
2023-05-19 20:29:24 +00:00
|
|
|
sourceType: {
|
|
|
|
filters: [
|
|
|
|
{
|
|
|
|
__typename: 'Filter',
|
|
|
|
key: {
|
|
|
|
__typename: 'PropertyKey',
|
|
|
|
name: settlementDataProperty,
|
|
|
|
type: PropertyKeyType.TYPE_INTEGER,
|
|
|
|
numberDecimalPlaces: 5,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2023-05-02 17:41:21 +00:00
|
|
|
},
|
|
|
|
dataSourceSpecBinding: {
|
|
|
|
settlementDataProperty,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
const marketsMock: MockedResponse<MarketsQuery> = {
|
|
|
|
request: {
|
|
|
|
query: MarketsDocument,
|
|
|
|
},
|
|
|
|
result: {
|
|
|
|
data: marketsQuery({
|
|
|
|
marketsConnection: {
|
|
|
|
edges: [
|
|
|
|
{
|
|
|
|
node: market,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
const marketsData = createMarketsDataFragment({
|
|
|
|
__typename: 'MarketData',
|
|
|
|
market: {
|
|
|
|
__typename: 'Market',
|
|
|
|
id: marketId,
|
|
|
|
},
|
|
|
|
bestBidPrice: '1000',
|
|
|
|
bestOfferPrice: '2000',
|
|
|
|
markPrice: '1500',
|
|
|
|
});
|
|
|
|
const marketsDataMock: MockedResponse<MarketsDataQuery> = {
|
|
|
|
request: {
|
|
|
|
query: MarketsDataDocument,
|
|
|
|
},
|
|
|
|
result: {
|
|
|
|
data: marketsDataQuery({
|
|
|
|
marketsConnection: {
|
|
|
|
edges: [
|
|
|
|
{
|
|
|
|
node: {
|
|
|
|
data: marketsData,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
// Create mock oracle data
|
|
|
|
const property = {
|
|
|
|
__typename: 'Property' as const,
|
|
|
|
name: settlementDataProperty,
|
|
|
|
value: '12345',
|
|
|
|
};
|
|
|
|
const oracleDataMock: MockedResponse<OracleSpecDataConnectionQuery> = {
|
|
|
|
request: {
|
|
|
|
query: OracleSpecDataConnectionDocument,
|
|
|
|
variables: {
|
|
|
|
oracleSpecId: settlementDataId,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
result: {
|
|
|
|
data: {
|
|
|
|
oracleSpec: {
|
|
|
|
dataConnection: {
|
|
|
|
edges: [
|
|
|
|
{
|
|
|
|
node: {
|
|
|
|
externalData: {
|
|
|
|
data: {
|
|
|
|
data: [property],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
beforeAll(() => {
|
|
|
|
originalNow = Date.now;
|
|
|
|
Date.now = jest.fn().mockReturnValue(mockNowTimestamp);
|
|
|
|
});
|
|
|
|
|
|
|
|
afterAll(() => {
|
|
|
|
Date.now = originalNow;
|
|
|
|
});
|
|
|
|
|
2023-12-05 10:29:01 +00:00
|
|
|
const renderComponent = async (mocks: MockedResponse[]) => {
|
2023-05-02 17:41:21 +00:00
|
|
|
await act(async () => {
|
|
|
|
render(
|
2023-07-18 09:37:25 +00:00
|
|
|
<MemoryRouter>
|
2023-12-05 10:29:01 +00:00
|
|
|
<MockedProvider mocks={mocks}>
|
2023-07-18 09:37:25 +00:00
|
|
|
<VegaWalletContext.Provider
|
|
|
|
value={{ pubKey } as VegaWalletContextShape}
|
|
|
|
>
|
|
|
|
<Closed />
|
|
|
|
</VegaWalletContext.Provider>
|
|
|
|
</MockedProvider>
|
|
|
|
</MemoryRouter>
|
2023-05-02 17:41:21 +00:00
|
|
|
);
|
|
|
|
});
|
2023-12-05 10:29:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
it('renders correct headers', async () => {
|
|
|
|
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
|
2023-05-02 17:41:21 +00:00
|
|
|
|
|
|
|
const headers = screen.getAllByRole('columnheader');
|
|
|
|
const expectedHeaders = [
|
|
|
|
'Market',
|
|
|
|
'Status',
|
|
|
|
'Settlement date',
|
|
|
|
'Best bid',
|
|
|
|
'Best offer',
|
|
|
|
'Mark price',
|
|
|
|
'Settlement price',
|
|
|
|
'Settlement asset',
|
2023-05-18 04:05:53 +00:00
|
|
|
'', // actions row
|
2023-05-02 17:41:21 +00:00
|
|
|
];
|
|
|
|
expect(headers).toHaveLength(expectedHeaders.length);
|
|
|
|
expect(headers.map((h) => h.textContent?.trim())).toEqual(expectedHeaders);
|
2023-12-05 10:29:01 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders correctly formatted and filtered rows', async () => {
|
|
|
|
await renderComponent([marketsMock, marketsDataMock, oracleDataMock]);
|
2023-05-02 17:41:21 +00:00
|
|
|
|
2023-09-20 09:11:03 +00:00
|
|
|
const assetSymbol = getAsset(market).symbol;
|
|
|
|
|
2023-05-02 17:41:21 +00:00
|
|
|
const cells = screen.getAllByRole('gridcell');
|
|
|
|
const expectedValues = [
|
|
|
|
market.tradableInstrument.instrument.code,
|
|
|
|
MarketStateMapping[market.state],
|
|
|
|
'3 days ago',
|
|
|
|
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
|
|
addDecimalsFormatNumber(marketsData.bestBidPrice, market.decimalPlaces),
|
|
|
|
addDecimalsFormatNumber(
|
|
|
|
marketsData!.bestOfferPrice,
|
|
|
|
market.decimalPlaces
|
|
|
|
),
|
|
|
|
addDecimalsFormatNumber(marketsData!.markPrice, market.decimalPlaces),
|
|
|
|
/* eslint-enable @typescript-eslint/no-non-null-assertion */
|
|
|
|
addDecimalsFormatNumber(property.value, market.decimalPlaces),
|
2023-09-20 09:11:03 +00:00
|
|
|
assetSymbol,
|
2023-05-18 04:05:53 +00:00
|
|
|
'', // actions row
|
2023-05-02 17:41:21 +00:00
|
|
|
];
|
|
|
|
cells.forEach((cell, i) => {
|
|
|
|
expect(cell).toHaveTextContent(expectedValues[i]);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('only renders settled and terminated markets', async () => {
|
|
|
|
const mixedMarkets = [
|
|
|
|
{
|
2023-09-20 09:11:03 +00:00
|
|
|
// include as settled
|
2023-05-02 17:41:21 +00:00
|
|
|
__typename: 'MarketEdge' as const,
|
|
|
|
node: createMarketFragment({
|
|
|
|
id: 'include-0',
|
|
|
|
state: MarketState.STATE_SETTLED,
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// omit this market
|
|
|
|
__typename: 'MarketEdge' as const,
|
|
|
|
node: createMarketFragment({
|
|
|
|
id: 'discard-0',
|
|
|
|
state: MarketState.STATE_SUSPENDED,
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// include as terminated
|
|
|
|
__typename: 'MarketEdge' as const,
|
|
|
|
node: createMarketFragment({
|
|
|
|
id: 'include-1',
|
|
|
|
state: MarketState.STATE_TRADING_TERMINATED,
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
// omit this market
|
|
|
|
__typename: 'MarketEdge' as const,
|
|
|
|
node: createMarketFragment({
|
|
|
|
id: 'discard-1',
|
|
|
|
state: MarketState.STATE_ACTIVE,
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const mixedMarketsMock: MockedResponse<MarketsQuery> = {
|
|
|
|
request: {
|
|
|
|
query: MarketsDocument,
|
|
|
|
},
|
|
|
|
result: {
|
|
|
|
data: {
|
|
|
|
marketsConnection: {
|
|
|
|
__typename: 'MarketConnection',
|
|
|
|
edges: mixedMarkets,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
2023-12-05 10:29:01 +00:00
|
|
|
|
|
|
|
await renderComponent([mixedMarketsMock, marketsDataMock, oracleDataMock]);
|
2023-05-02 17:41:21 +00:00
|
|
|
|
|
|
|
// check that the number of rows in datagrid is 2
|
|
|
|
const container = within(
|
|
|
|
document.querySelector('.ag-center-cols-container') as HTMLElement
|
|
|
|
);
|
|
|
|
const expectedRows = mixedMarkets.filter((m) => {
|
|
|
|
return [
|
|
|
|
MarketState.STATE_SETTLED,
|
|
|
|
MarketState.STATE_TRADING_TERMINATED,
|
|
|
|
].includes(m.node.state);
|
|
|
|
});
|
|
|
|
|
2023-10-03 15:35:17 +00:00
|
|
|
await waitFor(() => {
|
|
|
|
// check rows length is correct
|
|
|
|
const rows = container.getAllByRole('row');
|
|
|
|
expect(rows).toHaveLength(expectedRows.length);
|
|
|
|
});
|
2023-05-02 17:41:21 +00:00
|
|
|
|
|
|
|
// check that only included ids are shown
|
|
|
|
const cells = screen
|
|
|
|
.getAllByRole('gridcell')
|
2023-05-18 04:05:53 +00:00
|
|
|
.filter((cell) => cell.getAttribute('col-id') === 'code')
|
|
|
|
.map((cell) => {
|
2023-09-12 18:52:03 +00:00
|
|
|
const marketCode = within(cell).getByTestId('stack-cell-primary');
|
|
|
|
return marketCode.textContent;
|
2023-05-18 04:05:53 +00:00
|
|
|
});
|
2023-09-12 18:52:03 +00:00
|
|
|
expect(cells).toEqual(
|
|
|
|
expectedRows.map((m) => m.node.tradableInstrument.instrument.code)
|
|
|
|
);
|
2023-05-02 17:41:21 +00:00
|
|
|
});
|
2023-07-18 09:37:25 +00:00
|
|
|
|
2023-12-05 10:29:01 +00:00
|
|
|
it('display market actions', async () => {
|
2024-01-16 16:30:42 +00:00
|
|
|
// Use market with a successor Id as the actions dropdown will optionally
|
2023-12-05 10:29:01 +00:00
|
|
|
// show a link to the successor market
|
|
|
|
const marketsWithSuccessorAndParent = [
|
|
|
|
{
|
|
|
|
__typename: 'MarketEdge' as const,
|
|
|
|
node: createMarketFragment({
|
|
|
|
id: 'include-0',
|
|
|
|
state: MarketState.STATE_SETTLED,
|
|
|
|
successorMarketID: 'successor',
|
|
|
|
parentMarketID: 'parent',
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
const mockWithSuccessorAndParent: MockedResponse<MarketsQuery> = {
|
|
|
|
request: {
|
|
|
|
query: MarketsDocument,
|
|
|
|
},
|
|
|
|
result: {
|
|
|
|
data: {
|
|
|
|
marketsConnection: {
|
|
|
|
__typename: 'MarketConnection',
|
|
|
|
edges: marketsWithSuccessorAndParent,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
await renderComponent([
|
|
|
|
mockWithSuccessorAndParent,
|
|
|
|
marketsDataMock,
|
|
|
|
oracleDataMock,
|
|
|
|
]);
|
|
|
|
|
|
|
|
const actionCell = screen
|
|
|
|
.getAllByRole('gridcell')
|
|
|
|
.find((el) => el.getAttribute('col-id') === 'market-actions');
|
|
|
|
|
|
|
|
await userEvent.click(
|
|
|
|
within(actionCell as HTMLElement).getByTestId('dropdown-menu')
|
|
|
|
);
|
|
|
|
|
|
|
|
expect(screen.getByRole('menu')).toBeInTheDocument();
|
|
|
|
|
|
|
|
expect(
|
|
|
|
screen.getByRole('menuitem', { name: 'Copy Market ID' })
|
|
|
|
).toBeInTheDocument();
|
|
|
|
expect(
|
|
|
|
screen.getByRole('menuitem', { name: 'View on Explorer' })
|
|
|
|
).toBeInTheDocument();
|
|
|
|
expect(
|
|
|
|
screen.getByRole('menuitem', { name: 'View settlement asset details' })
|
|
|
|
).toBeInTheDocument();
|
|
|
|
expect(
|
|
|
|
screen.getByRole('menuitem', { name: 'View parent market' })
|
|
|
|
).toBeInTheDocument();
|
|
|
|
expect(
|
|
|
|
screen.getByRole('menuitem', { name: 'View successor market' })
|
|
|
|
).toBeInTheDocument();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('successor market should be visible', async () => {
|
2023-09-12 18:52:03 +00:00
|
|
|
const marketsWithSuccessorID = [
|
2023-07-18 09:37:25 +00:00
|
|
|
{
|
|
|
|
__typename: 'MarketEdge' as const,
|
|
|
|
node: createMarketFragment({
|
|
|
|
id: 'include-0',
|
|
|
|
state: MarketState.STATE_SETTLED,
|
2023-09-12 18:52:03 +00:00
|
|
|
successorMarketID: 'successor',
|
2023-07-18 09:37:25 +00:00
|
|
|
}),
|
|
|
|
},
|
|
|
|
];
|
2023-09-12 18:52:03 +00:00
|
|
|
const mockWithSuccessors: MockedResponse<MarketsQuery> = {
|
2023-07-18 09:37:25 +00:00
|
|
|
request: {
|
|
|
|
query: MarketsDocument,
|
|
|
|
},
|
|
|
|
result: {
|
|
|
|
data: {
|
|
|
|
marketsConnection: {
|
|
|
|
__typename: 'MarketConnection',
|
2023-09-12 18:52:03 +00:00
|
|
|
edges: marketsWithSuccessorID,
|
2023-07-18 09:37:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
2023-09-12 18:52:03 +00:00
|
|
|
|
2023-12-05 10:29:01 +00:00
|
|
|
await renderComponent([
|
|
|
|
mockWithSuccessors,
|
|
|
|
marketsDataMock,
|
|
|
|
oracleDataMock,
|
|
|
|
]);
|
2023-07-18 09:37:25 +00:00
|
|
|
|
2023-09-12 18:52:03 +00:00
|
|
|
const container = within(
|
|
|
|
document.querySelector('.ag-center-cols-container') as HTMLElement
|
|
|
|
);
|
2023-07-25 15:32:40 +00:00
|
|
|
|
2023-10-16 18:18:26 +00:00
|
|
|
const cells = await container.findAllByRole('gridcell');
|
|
|
|
const cell = cells.find((el) => el.getAttribute('col-id') === 'code');
|
|
|
|
|
|
|
|
expect(
|
|
|
|
within(cell as HTMLElement).getByTestId('stack-cell-secondary')
|
|
|
|
).toHaveTextContent('PRNT');
|
2023-07-18 09:37:25 +00:00
|
|
|
});
|
2023-05-02 17:41:21 +00:00
|
|
|
});
|