diff --git a/apps/explorer-e2e/tsconfig.json b/apps/explorer-e2e/tsconfig.json index c4f818ecd..b3c03f183 100644 --- a/apps/explorer-e2e/tsconfig.json +++ b/apps/explorer-e2e/tsconfig.json @@ -3,8 +3,16 @@ "compilerOptions": { "sourceMap": false, "outDir": "../../dist/out-tsc", + "types": ["cypress", "node"], "allowJs": true, - "types": ["cypress", "node"] + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true }, "include": ["src/**/*.ts", "src/**/*.js"] } diff --git a/apps/trading-e2e/cypress.json b/apps/trading-e2e/cypress.json index 1d2c86776..3391debee 100644 --- a/apps/trading-e2e/cypress.json +++ b/apps/trading-e2e/cypress.json @@ -14,7 +14,7 @@ "chromeWebSecurity": false, "projectId": "et4snf", "env": { - "bypassPlacingOrders": true, + "vegaPublicKey": "47836c253520d2661bf5bed6339c0de08fd02cf5d4db0efee3b4373f20c7d278", "tsConfig": "tsconfig.json", "TAGS": "not @todo and not @ignore and not @manual" } diff --git a/apps/trading-e2e/src/fixtures/example.json b/apps/trading-e2e/src/fixtures/example.json deleted file mode 100644 index 294cbed6c..000000000 --- a/apps/trading-e2e/src/fixtures/example.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "name": "Using fixtures to represent data", - "email": "hello@cypress.io" -} diff --git a/apps/trading-e2e/src/integration/market-order.feature b/apps/trading-e2e/src/integration/deal-ticket.feature similarity index 68% rename from apps/trading-e2e/src/integration/market-order.feature rename to apps/trading-e2e/src/integration/deal-ticket.feature index ee0ea6753..0b99ae795 100644 --- a/apps/trading-e2e/src/integration/market-order.feature +++ b/apps/trading-e2e/src/integration/deal-ticket.feature @@ -1,11 +1,8 @@ -Feature: Market orders - +Feature: Deal ticket Scenario Outline: Successfull market buy orders - Given I am on the homepage - And I navigate to markets page - When I click on active market + Given I am on the trading page for an active market And I connect to Vega Wallet - And place a buy '' market order + When I place a buy '' market order Then order request is sent Examples: @@ -14,11 +11,9 @@ Feature: Market orders | IOC | Scenario Outline: Successfull Limit buy orders - Given I am on the homepage - And I navigate to markets page - When I click on active market + Given I am on the trading page for an active market And I connect to Vega Wallet - And place a buy '' limit order + When I place a buy '' limit order Then order request is sent Examples: @@ -30,11 +25,9 @@ Feature: Market orders | GFN | Scenario Outline: Successfull market sell order - Given I am on the homepage - And I navigate to markets page - When I click on active market + Given I am on the trading page for an active market And I connect to Vega Wallet - And place a sell '' market order + When I place a sell '' market order Then order request is sent Examples: @@ -43,11 +36,10 @@ Feature: Market orders | IOC | Scenario Outline: Successfull limit sell order - Given I am on the homepage - And I navigate to markets page - When I click on active market + Given I am on the trading page for an active market And I connect to Vega Wallet - And place a sell '' limit order + When I place a sell '' limit order + Then order request is sent Examples: | limitOrderType | @@ -66,27 +58,20 @@ Feature: Market orders And place a buy 'FOK' market order Then error message for insufficient funds is displayed - @ignore Scenario: Unable to order because market is suspended - Given I am on the homepage - And I navigate to markets page - When I click on suspended market + Given I am on the trading page for a suspended market And I connect to Vega Wallet Then place order button is disabled And "Market is currently suspended" error is shown Scenario: Unable to order because wallet is not connected - Given I am on the homepage - And I navigate to markets page - When I click on active market + Given I am on the trading page for an active market Then place order button is disabled And "No public key selected" error is shown @ignore Scenario: Unsuccessfull because quantity is 0 - Given I am on the homepage - And I navigate to markets page - When I click on active market + Given I am on the trading page for an active market And I connect to Vega Wallet And place a buy 'FOK' market order with amount of 0 Then Order rejected by wallet error shown containing text "must be positive" diff --git a/apps/trading-e2e/src/integration/home-page.feature b/apps/trading-e2e/src/integration/home-page.feature index e76dad29a..eb9c7e01c 100644 --- a/apps/trading-e2e/src/integration/home-page.feature +++ b/apps/trading-e2e/src/integration/home-page.feature @@ -7,6 +7,10 @@ Feature: Home page Given I am on the homepage And I navigate to portfolio page + Scenario: Visit Markets page + Given I am on the homepage + And I navigate to markets page + Scenario: Unable to connect Vega wallet with incorrect credentials Given I am on the homepage When I try to connect Vega wallet with incorrect details diff --git a/apps/trading-e2e/src/integration/market-page.feature b/apps/trading-e2e/src/integration/market-page.feature deleted file mode 100644 index 0bd7b203e..000000000 --- a/apps/trading-e2e/src/integration/market-page.feature +++ /dev/null @@ -1,6 +0,0 @@ -Feature: Market Page - - Scenario: Market table displayed on market page - Given I am on the homepage - And I navigate to markets page - Then the market table is displayed diff --git a/apps/trading-e2e/src/integration/markets-page.feature b/apps/trading-e2e/src/integration/markets-page.feature new file mode 100644 index 000000000..ace6099a8 --- /dev/null +++ b/apps/trading-e2e/src/integration/markets-page.feature @@ -0,0 +1,17 @@ +Feature: Markets page + + Scenario: Navigation + Given I am on the homepage + When I navigate to markets page + Then I can view markets + And the market table is displayed + + Scenario: Select active market + Given I am on the markets page + When I click on an active market + Then I am on the trading page for an active market + + Scenario: Select suspended market + Given I am on the markets page + When I click on a suspended market + Then I am on the trading page for a suspended market diff --git a/apps/trading-e2e/src/integration/portfolio-page.feature b/apps/trading-e2e/src/integration/portfolio-page.feature new file mode 100644 index 000000000..7f97d0828 --- /dev/null +++ b/apps/trading-e2e/src/integration/portfolio-page.feature @@ -0,0 +1,5 @@ + +Feature: Portfolio page + Scenario: Navigation + Given I am on the homepage + Then I navigate to portfolio page diff --git a/apps/trading-e2e/src/support/app.po.ts b/apps/trading-e2e/src/support/app.po.ts deleted file mode 100644 index 329342469..000000000 --- a/apps/trading-e2e/src/support/app.po.ts +++ /dev/null @@ -1 +0,0 @@ -export const getGreeting = () => cy.get('h1'); diff --git a/apps/trading-e2e/src/support/index.ts b/apps/trading-e2e/src/support/index.ts index 4a6c6f6c1..a00d22210 100644 --- a/apps/trading-e2e/src/support/index.ts +++ b/apps/trading-e2e/src/support/index.ts @@ -1,17 +1,11 @@ -// *********************************************************** -// This example support/index.js is processed and -// loaded automatically before your test files. -// -// This is a great place to put global configuration and -// behavior that modifies Cypress. -// -// You can change the location of this file or turn off -// automatically serving support files with the -// 'supportFile' configuration option. -// -// You can read more here: -// https://on.cypress.io/configuration -// *********************************************************** - -// Import commands.js using ES2015 syntax: import '@vegaprotocol/cypress'; +import type { CyHttpMessages } from 'cypress/types/net-stubbing'; + +// Utility to match GraphQL mutation based on the operation name +export const hasOperationName = ( + req: CyHttpMessages.IncomingHttpRequest, + operationName: string +) => { + const { body } = req; + return 'operationName' in body && body.operationName === operationName; +}; diff --git a/apps/trading-e2e/src/support/pages/base-page.js b/apps/trading-e2e/src/support/pages/base-page.js index 0fb7a8b25..3d895409b 100644 --- a/apps/trading-e2e/src/support/pages/base-page.js +++ b/apps/trading-e2e/src/support/pages/base-page.js @@ -1,56 +1,16 @@ export default class BasePage { porfolioUrl = '/portfolio'; marketsUrl = '/markets'; - connectVegaBtn = 'connect-vega-wallet'; - walletConnectors = 'connectors-list'; - walletForm = 'rest-connector-form'; - walletInputError = 'input-wallet-error'; - walletFormError = 'form-error'; - inputError = 'input-error-text'; navigateToPortfolio() { - cy.get(`a[href='${this.porfolioUrl}']`).click(); + cy.get(`a[href='${this.porfolioUrl}']`).should('be.visible').click(); + cy.url().should('include', '/portfolio'); + cy.getByTestId('portfolio'); } navigateToMarkets() { - cy.get(`a[href='${this.marketsUrl}']`) - .should('be.visible') - .click({ force: true }); + cy.get(`a[href='${this.marketsUrl}']`).should('be.visible').click(); cy.url().should('include', '/markets'); - } - - navigateToConnectVegaWallet() { - cy.getByTestId(this.connectVegaBtn).click(); - cy.contains('Connects using REST to a running Vega wallet service'); - cy.getByTestId(this.walletConnectors).find('button').click(); - } - - fillInWalletForm(walletName, walletPassphrase) { - cy.getByTestId(this.walletForm) - .find('#wallet') - .click({ force: true }) - .type(walletName); - cy.getByTestId(this.walletForm) - .find('#passphrase') - .click({ force: true }) - .type(walletPassphrase); - } - - clickConnectVegaWallet() { - cy.getByTestId(this.walletForm) - .find('button[type=submit]') - .click({ force: true }); - } - - validateWalletNotRunningError() { - cy.getByTestId(this.walletFormError).should( - 'have.text', - 'Authentication failed' - ); - } - - validateWalletErrorFieldsDisplayed() { - cy.getByTestId(this.walletInputError).should('have.text', 'Required'); - cy.getByTestId(this.inputError).should('have.text', 'Required'); + cy.getByTestId('markets'); } } diff --git a/apps/trading-e2e/src/support/pages/markets-page.js b/apps/trading-e2e/src/support/pages/markets-page.js index 4742c8b64..b2e47844f 100644 --- a/apps/trading-e2e/src/support/pages/markets-page.js +++ b/apps/trading-e2e/src/support/pages/markets-page.js @@ -7,15 +7,10 @@ export default class MarketPage extends BasePage { 'tradableInstrument.instrument.product.settlementAsset.symbol'; marketRowPrices = 'flash-cell'; marketRowDescription = 'name'; - chartTab = 'chart'; - ticketTab = 'ticket'; - orderbookTab = 'orderbook'; - ordersTab = 'orders'; - positionsTab = 'positions'; - collateralTab = 'collateral'; - tradesTab = 'trades'; - completedTrades = 'market-trades'; - orderBookTab = 'orderbook'; + + validateMarketsAreDisplayed() { + cy.get('.ag-root-wrapper').should('be.visible'); + } validateMarketTableDisplayed() { const expectedMarketHeaders = [ @@ -58,39 +53,7 @@ export default class MarketPage extends BasePage { ); } - validateCompletedTradesDisplayed() { - cy.getByTestId(this.completedTrades).should('not.be.empty'); - } - - clickOnMarket(marketText) { - cy.contains(marketText).click(); - } - - clickOnActiveMarket() { - cy.contains('Active', { timeout: 8000 }).click({ force: true }); - } - - clickOnTopMarketRow() { - cy.get('[col-id="data"]').eq(1).click(); - } - - clickOnOrdersTab() { - cy.getByTestId(this.ordersTab).click(); - } - - clickOnTicketTab() { - cy.getByTestId(this.ticketTab).click(); - } - - clickOnCollateralTab() { - cy.getByTestId(this.collateralTab).click(); - } - - clickOnTradesTab() { - cy.getByTestId(this.tradesTab).click(); - } - - clickOrderBookTab() { - cy.getByTestId(this.orderBookTab).click(); + clickOnMarket(text) { + cy.contains(text).click(); } } diff --git a/apps/trading-e2e/src/support/pages/portfolio-page.js b/apps/trading-e2e/src/support/pages/portfolio-page.js new file mode 100644 index 000000000..9ce7896dc --- /dev/null +++ b/apps/trading-e2e/src/support/pages/portfolio-page.js @@ -0,0 +1,3 @@ +import BasePage from './base-page'; + +export default class PortfolioPage extends BasePage {} diff --git a/apps/trading-e2e/src/support/pages/trading-page.js b/apps/trading-e2e/src/support/pages/trading-page.js new file mode 100644 index 000000000..c037ba91f --- /dev/null +++ b/apps/trading-e2e/src/support/pages/trading-page.js @@ -0,0 +1,33 @@ +import BasePage from './base-page'; + +export default class TradingPage extends BasePage { + chartTab = 'chart'; + ticketTab = 'ticket'; + orderbookTab = 'orderbook'; + ordersTab = 'orders'; + positionsTab = 'positions'; + collateralTab = 'collateral'; + tradesTab = 'trades'; + completedTrades = 'market-trades'; + orderBookTab = 'orderbook'; + + clickOnOrdersTab() { + cy.getByTestId(this.ordersTab).click(); + } + + clickOnTicketTab() { + cy.getByTestId(this.ticketTab).click(); + } + + clickOnCollateralTab() { + cy.getByTestId(this.collateralTab).click(); + } + + clickOnTradesTab() { + cy.getByTestId(this.tradesTab).click(); + } + + clickOrderBookTab() { + cy.getByTestId(this.orderBookTab).click(); + } +} diff --git a/apps/trading-e2e/src/support/step_definitions/common-step.js b/apps/trading-e2e/src/support/step_definitions/common-step.js deleted file mode 100644 index 816b7e783..000000000 --- a/apps/trading-e2e/src/support/step_definitions/common-step.js +++ /dev/null @@ -1,27 +0,0 @@ -import { Given, When } from 'cypress-cucumber-preprocessor/steps'; -import MarketsPage from '../pages/markets-page'; -import DealTicketPage from '../pages/deal-ticket-page'; -const marketsPage = new MarketsPage(); -const dealTicketPage = new DealTicketPage(); - -Given('I am on the homepage', () => { - cy.visit('/'); -}); - -Given('I navigate to markets page', () => { - marketsPage.navigateToMarkets(); -}); - -Given('I navigate to portfolio page', () => { - marketsPage.navigateToPortfolio(); -}); - -When('I connect to Vega Wallet', () => { - marketsPage.navigateToConnectVegaWallet(); - marketsPage.fillInWalletForm( - 'UI_Trading_Test', - Cypress.env('tradingWalletPassphrase') - ); - marketsPage.clickConnectVegaWallet(); - dealTicketPage.reloadPageIfPublicKeyErrorDisplayed(); -}); diff --git a/apps/trading-e2e/src/support/step_definitions/common.step.js b/apps/trading-e2e/src/support/step_definitions/common.step.js new file mode 100644 index 000000000..f200eecad --- /dev/null +++ b/apps/trading-e2e/src/support/step_definitions/common.step.js @@ -0,0 +1,5 @@ +import { Given } from 'cypress-cucumber-preprocessor/steps'; + +Given('I am on the homepage', () => { + cy.visit('/'); +}); diff --git a/apps/trading-e2e/src/support/step_definitions/deal-ticket.step.js b/apps/trading-e2e/src/support/step_definitions/deal-ticket.step.js new file mode 100644 index 000000000..fdcebb415 --- /dev/null +++ b/apps/trading-e2e/src/support/step_definitions/deal-ticket.step.js @@ -0,0 +1,52 @@ +import { Then, When } from 'cypress-cucumber-preprocessor/steps'; +import DealTicket from '../trading-windows/deal-ticket'; + +const dealTicket = new DealTicket(); + +When('I place a buy {string} market order', (orderType) => { + dealTicket.placeMarketOrder(true, 100, orderType); + dealTicket.clickPlaceOrder(); +}); + +When('I place a sell {string} market order', (orderType) => { + dealTicket.placeMarketOrder(false, 100, orderType); + dealTicket.clickPlaceOrder(); +}); + +When('I place a buy {string} limit order', (limitOrderType) => { + dealTicket.placeLimitOrder(true, 100, 2000, limitOrderType); + dealTicket.clickPlaceOrder(); +}); + +When('I place a sell {string} limit order', (limitOrderType) => { + dealTicket.placeLimitOrder(false, 100, 2000, limitOrderType); + dealTicket.clickPlaceOrder(); +}); + +When('I place a buy {string} market order with amount of 0', (orderType) => { + dealTicket.placeMarketOrder(true, 0, orderType); + dealTicket.clickPlaceOrder(); +}); + +Then('order request is sent', () => { + dealTicket.verifyOrderRequestSent(); +}); + +Then('error message for insufficient funds is displayed', () => { + dealTicket.verifyOrderFailedInsufficientFunds(); +}); + +Then('place order button is disabled', () => { + dealTicket.verifyPlaceOrderBtnDisabled(); +}); + +Then('{string} error is shown', (errorMsg) => { + dealTicket.verifySubmitBtnErrorText(errorMsg); +}); + +Then( + 'Order rejected by wallet error shown containing text {string}', + (expectedError) => { + dealTicket.verifyOrderRejected(expectedError); + } +); diff --git a/apps/trading-e2e/src/support/step_definitions/home-page.step.js b/apps/trading-e2e/src/support/step_definitions/home-page.step.js index 36ba3e56d..245683373 100644 --- a/apps/trading-e2e/src/support/step_definitions/home-page.step.js +++ b/apps/trading-e2e/src/support/step_definitions/home-page.step.js @@ -1,22 +1,23 @@ import { Then, When } from 'cypress-cucumber-preprocessor/steps'; -import MarketsPage from '../pages/markets-page'; -const marketsPage = new MarketsPage(); +import VegaWallet from '../vega-wallet'; + +const vegaWallet = new VegaWallet(); When('I try to connect Vega wallet with incorrect details', () => { - marketsPage.navigateToConnectVegaWallet(); - marketsPage.fillInWalletForm('name', 'wrong passphrase'); - marketsPage.clickConnectVegaWallet(); + vegaWallet.openVegaWalletConnectDialog(); + vegaWallet.fillInWalletForm('name', 'wrong passphrase'); + vegaWallet.clickConnectVegaWallet(); }); When('I try to connect Vega wallet with blank fields', () => { - marketsPage.navigateToConnectVegaWallet(); - marketsPage.clickConnectVegaWallet(); + vegaWallet.openVegaWalletConnectDialog(); + vegaWallet.clickConnectVegaWallet(); }); Then('wallet not running error message is displayed', () => { - marketsPage.validateWalletNotRunningError(); + vegaWallet.validateWalletNotRunningError(); }); Then('wallet field validation errors are shown', () => { - marketsPage.validateWalletErrorFieldsDisplayed(); + vegaWallet.validateWalletErrorFieldsDisplayed(); }); diff --git a/apps/trading-e2e/src/support/step_definitions/market-order.step.js b/apps/trading-e2e/src/support/step_definitions/market-order.step.js deleted file mode 100644 index 65eb274f3..000000000 --- a/apps/trading-e2e/src/support/step_definitions/market-order.step.js +++ /dev/null @@ -1,54 +0,0 @@ -import { Then, When } from 'cypress-cucumber-preprocessor/steps'; - -import DealTicketPage from '../pages/deal-ticket-page'; -const dealTicketPage = new DealTicketPage(); - -When('place a buy {string} market order', (orderType) => { - dealTicketPage.placeMarketOrder(true, 100, orderType); - dealTicketPage.clickPlaceOrder(); -}); - -When('place a sell {string} market order', (orderType) => { - dealTicketPage.placeMarketOrder(false, 100, orderType); - dealTicketPage.clickPlaceOrder(); -}); - -When('place a buy {string} limit order', (limitOrderType) => { - dealTicketPage.placeLimitOrder(true, 100, 2000, limitOrderType); - dealTicketPage.clickPlaceOrder(); -}); - -When('place a sell {string} limit order', (limitOrderType) => { - dealTicketPage.placeLimitOrder(false, 100, 2000, limitOrderType); - dealTicketPage.clickPlaceOrder(); -}); - -When('place a buy {string} market order with amount of 0', (orderType) => { - dealTicketPage.placeMarketOrder(true, 0, orderType); - dealTicketPage.clickPlaceOrder(); -}); - -Then('order request is sent', () => { - if (Cypress.env('bypassPlacingOrders' != true)) { - dealTicketPage.verifyOrderRequestSent(); - } -}); - -Then('error message for insufficient funds is displayed', () => { - dealTicketPage.verifyOrderFailedInsufficientFunds(); -}); - -Then('place order button is disabled', () => { - dealTicketPage.verifyPlaceOrderBtnDisabled(); -}); - -Then('{string} error is shown', (errorMsg) => { - dealTicketPage.verifySubmitBtnErrorText(errorMsg); -}); - -Then( - 'Order rejected by wallet error shown containing text {string}', - (expectedError) => { - dealTicketPage.verifyOrderRejected(expectedError); - } -); diff --git a/apps/trading-e2e/src/support/step_definitions/markets-page.step.js b/apps/trading-e2e/src/support/step_definitions/markets-page.step.js index 179e3aa75..cf8c205bf 100644 --- a/apps/trading-e2e/src/support/step_definitions/markets-page.step.js +++ b/apps/trading-e2e/src/support/step_definitions/markets-page.step.js @@ -1,23 +1,47 @@ -import { When, Then } from 'cypress-cucumber-preprocessor/steps'; +import { And, Given, Then, When } from 'cypress-cucumber-preprocessor/steps'; +import { hasOperationName } from '..'; import MarketsPage from '../pages/markets-page'; +// eslint-disable-next-line @nrwl/nx/enforce-module-boundaries +import { generateMarkets } from '../../../../../libs/market-list/src/__tests__/generate-markets'; + const marketsPage = new MarketsPage(); -When('I click on market for {string}', (marketText) => { - marketsPage.clickOnMarket(marketText); +const mockMarkets = () => { + cy.mockGQL('Markets', (req) => { + if (hasOperationName(req, 'Markets')) { + req.reply({ + body: { data: generateMarkets() }, + }); + } + }); +}; + +beforeEach(() => { + mockMarkets(); }); -When('I click on active market', () => { - if (Cypress.env('bypassPlacingOrders' != true)) { - marketsPage.clickOnMarket('Active'); - } else { - marketsPage.clickOnTopMarketRow(); - } +Then('I navigate to markets page', () => { + marketsPage.navigateToMarkets(); }); -When('I click on suspended market', () => { - marketsPage.clickOnMarket('Suspended'); +Given('I am on the markets page', () => { + cy.visit('/markets'); + cy.wait('@Markets'); }); -Then('the market table is displayed', () => { +Then('I can view markets', () => { + cy.wait('@Markets'); + marketsPage.validateMarketsAreDisplayed(); +}); + +And('the market table is displayed', () => { marketsPage.validateMarketTableDisplayed(); }); + +When('I click on an active market', () => { + marketsPage.clickOnMarket('Active'); +}); + +When('I click on a suspended market', () => { + marketsPage.clickOnMarket('Suspended'); +}); diff --git a/apps/trading-e2e/src/support/step_definitions/portfolio-page.step.js b/apps/trading-e2e/src/support/step_definitions/portfolio-page.step.js new file mode 100644 index 000000000..acfaffb49 --- /dev/null +++ b/apps/trading-e2e/src/support/step_definitions/portfolio-page.step.js @@ -0,0 +1,8 @@ +import { Then } from 'cypress-cucumber-preprocessor/steps'; +import PortfolioPage from '../pages/portfolio-page'; + +const portfolioPage = new PortfolioPage(); + +Then('I navigate to portfolio page', () => { + portfolioPage.navigateToPortfolio(); +}); diff --git a/apps/trading-e2e/src/support/step_definitions/trading-page.step.js b/apps/trading-e2e/src/support/step_definitions/trading-page.step.js new file mode 100644 index 000000000..b98addf3e --- /dev/null +++ b/apps/trading-e2e/src/support/step_definitions/trading-page.step.js @@ -0,0 +1,68 @@ +import { Given } from 'cypress-cucumber-preprocessor/steps'; +import { hasOperationName } from '..'; +import { MarketState } from '@vegaprotocol/types'; +/* eslint-disable @nrwl/nx/enforce-module-boundaries */ +import { generateTrades } from '../../../../../libs/trades/src/__tests__'; +import { + generateChart, + generateCandles, +} from '../../../../../libs/chart/src/__tests__'; +import { generateDealTicketQuery } from '../../../../../libs/deal-ticket/src/__tests__'; +import { generateMarket } from '../../../../trading/pages/markets/__tests__'; +/* eslint-enable @nrwl/nx/enforce-module-boundaries */ + +const mockMarket = (state) => { + cy.mockGQL('Market', (req) => { + if (hasOperationName(req, 'Market')) { + req.reply({ + body: { + data: generateMarket({ + market: { + name: `${state.toUpperCase()} MARKET`, + }, + }), + }, + }); + } + + if (hasOperationName(req, 'DealTicketQuery')) { + req.reply({ + body: { data: generateDealTicketQuery({ market: { state } }) }, + }); + } + + if (hasOperationName(req, 'Trades')) { + req.reply({ + body: { data: generateTrades() }, + }); + } + + if (hasOperationName(req, 'Chart')) { + req.reply({ + body: { data: generateChart() }, + }); + } + + if (hasOperationName(req, 'Candles')) { + req.reply({ + body: { data: generateCandles() }, + }); + } + }); +}; + +Given('I am on the trading page for an active market', () => { + mockMarket(MarketState.Active); + + cy.visit('/markets/market-id'); + cy.wait('@Market'); + cy.contains('Market: ACTIVE MARKET'); +}); + +Given('I am on the trading page for a suspended market', () => { + mockMarket(MarketState.Suspended); + + cy.visit('/markets/market-id'); + cy.wait('@Market'); + cy.contains('Market: SUSPENDED MARKET'); +}); diff --git a/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.js b/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.js new file mode 100644 index 000000000..917d7496d --- /dev/null +++ b/apps/trading-e2e/src/support/step_definitions/vega-wallet.step.js @@ -0,0 +1,30 @@ +import { When } from 'cypress-cucumber-preprocessor/steps'; +import VegaWallet from '../vega-wallet'; + +const vegaWallet = new VegaWallet(); + +beforeEach(() => { + // The two important values here are the signature.value and the From.Pubkey. + // From.PubKey is the first public key in the UI_Trading_Test wallet. We assert that the returned pubkey matches + // what is used to sign the transaction + // The tx.signature.value isn't currently used in any tests but the value returned is used to create IDs objects such + // as orders, if its invalid an erro will be thrown + cy.mockVegaCommandSync({ + txHash: 'test-tx-hash', + tx: { + signature: { + value: + 'd86138bba739bbc1069b3dc975d20b3a1517c2b9bdd401c70eeb1a0ecbc502ec268cf3129824841178b8b506b0b7d650c76644dbd96f524a6cb2158fb7121800', + }, + }, + }); +}); + +When('I connect to Vega Wallet', () => { + vegaWallet.openVegaWalletConnectDialog(); + vegaWallet.fillInWalletForm( + 'UI_Trading_Test', + Cypress.env('tradingWalletPassphrase') + ); + vegaWallet.clickConnectVegaWallet(); +}); diff --git a/apps/trading-e2e/src/support/pages/deal-ticket-page.js b/apps/trading-e2e/src/support/trading-windows/deal-ticket.js similarity index 90% rename from apps/trading-e2e/src/support/pages/deal-ticket-page.js rename to apps/trading-e2e/src/support/trading-windows/deal-ticket.js index ff838b914..2cbd9a793 100644 --- a/apps/trading-e2e/src/support/pages/deal-ticket-page.js +++ b/apps/trading-e2e/src/support/trading-windows/deal-ticket.js @@ -1,5 +1,4 @@ -import BasePage from './base-page'; -export default class DealTicketPage extends BasePage { +export default class DealTicket { marketOrderType = 'order-type-TYPE_MARKET'; limitOrderType = 'order-type-TYPE_LIMIT'; buyOrder = 'order-side-SIDE_BUY'; @@ -53,8 +52,7 @@ export default class DealTicketPage extends BasePage { ); cy.getByTestId(this.orderTransactionHash) .invoke('text') - .should('contain', 'Tx hash: ') - .and('have.length.above', 64); + .should('contain', 'Tx hash: test-tx-hash'); } verifyOrderFailedInsufficientFunds() { @@ -65,9 +63,8 @@ export default class DealTicketPage extends BasePage { } clickPlaceOrder() { - if (Cypress.env('bypassPlacingOrders' != true)) { - cy.getByTestId(this.placeOrderBtn).click(); - } + cy.getByTestId(this.placeOrderBtn).click(); + cy.contains('Awaiting network confirmation'); } verifyPlaceOrderBtnDisabled() { @@ -75,7 +72,10 @@ export default class DealTicketPage extends BasePage { } verifySubmitBtnErrorText(expectedText) { - cy.getByTestId(this.inputError).should('have.text', expectedText); + cy.getByTestId('dealticket-error-message').should( + 'have.text', + expectedText + ); } verifyOrderRejected(errorMsg) { diff --git a/apps/trading-e2e/src/support/vega-wallet/index.js b/apps/trading-e2e/src/support/vega-wallet/index.js new file mode 100644 index 000000000..b1867c323 --- /dev/null +++ b/apps/trading-e2e/src/support/vega-wallet/index.js @@ -0,0 +1,43 @@ +export default class VegaWallet { + connectVegaBtn = 'connect-vega-wallet'; + walletConnectors = 'connectors-list'; + walletForm = 'rest-connector-form'; + walletInputError = 'input-wallet-error'; + walletFormError = 'form-error'; + inputError = 'input-error-text'; + + openVegaWalletConnectDialog() { + cy.getByTestId(this.connectVegaBtn).click(); + cy.contains('Connects using REST to a running Vega wallet service'); + cy.getByTestId(this.walletConnectors).find('button').click(); + } + + fillInWalletForm(walletName, walletPassphrase) { + cy.getByTestId(this.walletForm) + .find('#wallet') + .click({ force: true }) + .type(walletName); + cy.getByTestId(this.walletForm) + .find('#passphrase') + .click({ force: true }) + .type(walletPassphrase); + } + + clickConnectVegaWallet() { + cy.getByTestId(this.walletForm) + .find('button[type=submit]') + .click({ force: true }); + } + + validateWalletNotRunningError() { + cy.getByTestId(this.walletFormError).should( + 'have.text', + 'Authentication failed' + ); + } + + validateWalletErrorFieldsDisplayed() { + cy.getByTestId(this.walletInputError).should('have.text', 'Required'); + cy.getByTestId(this.inputError).should('have.text', 'Required'); + } +} diff --git a/apps/trading-e2e/tsconfig.json b/apps/trading-e2e/tsconfig.json index c4f818ecd..e2bee0ecb 100644 --- a/apps/trading-e2e/tsconfig.json +++ b/apps/trading-e2e/tsconfig.json @@ -4,7 +4,15 @@ "sourceMap": false, "outDir": "../../dist/out-tsc", "allowJs": true, - "types": ["cypress", "node"] + "types": ["cypress", "node"], + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true }, "include": ["src/**/*.ts", "src/**/*.js"] } diff --git a/apps/trading/pages/_app.page.tsx b/apps/trading/pages/_app.page.tsx index bfe0eaab6..fc91815c8 100644 --- a/apps/trading/pages/_app.page.tsx +++ b/apps/trading/pages/_app.page.tsx @@ -60,7 +60,7 @@ function VegaTradingApp({ Component, pageProps }: AppProps) { -
+
({ + page: 'home', +}); + export default Index; diff --git a/apps/trading/pages/markets/[marketId].page.tsx b/apps/trading/pages/markets/[marketId].page.tsx index ade159a69..57e3459d3 100644 --- a/apps/trading/pages/markets/[marketId].page.tsx +++ b/apps/trading/pages/markets/[marketId].page.tsx @@ -60,6 +60,10 @@ const MarketPage = () => { ); }; +MarketPage.getInitialProps = () => ({ + page: 'market', +}); + export default MarketPage; const useWindowSize = () => { diff --git a/apps/trading/pages/markets/__tests__/generate-market.ts b/apps/trading/pages/markets/__tests__/generate-market.ts new file mode 100644 index 000000000..10c9b7de2 --- /dev/null +++ b/apps/trading/pages/markets/__tests__/generate-market.ts @@ -0,0 +1,15 @@ +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; +import type { Market } from '../__generated__/Market'; + +export const generateMarket = (override?: PartialDeep): Market => { + const defaultResult = { + market: { + id: 'market-id', + name: 'MARKET NAME', + __typename: 'Market', + }, + }; + + return merge(defaultResult, override); +}; diff --git a/apps/trading/pages/markets/__tests__/index.ts b/apps/trading/pages/markets/__tests__/index.ts new file mode 100644 index 000000000..33645d770 --- /dev/null +++ b/apps/trading/pages/markets/__tests__/index.ts @@ -0,0 +1 @@ +export * from './generate-market'; diff --git a/apps/trading/pages/markets/index.page.tsx b/apps/trading/pages/markets/index.page.tsx index 9f6a97c93..892cf76e0 100644 --- a/apps/trading/pages/markets/index.page.tsx +++ b/apps/trading/pages/markets/index.page.tsx @@ -4,4 +4,8 @@ const Markets = () => { return ; }; +Markets.getInitialProps = () => ({ + page: 'markets', +}); + export default Markets; diff --git a/apps/trading/pages/portfolio/deposit/index.page.tsx b/apps/trading/pages/portfolio/deposit/index.page.tsx index 04b2c79d1..2813f8dfa 100644 --- a/apps/trading/pages/portfolio/deposit/index.page.tsx +++ b/apps/trading/pages/portfolio/deposit/index.page.tsx @@ -31,4 +31,8 @@ const Deposit = () => { ); }; +Deposit.getInitialProps = () => ({ + page: 'deposit', +}); + export default Deposit; diff --git a/apps/trading/pages/portfolio/index.page.tsx b/apps/trading/pages/portfolio/index.page.tsx index e496035d0..78469463e 100644 --- a/apps/trading/pages/portfolio/index.page.tsx +++ b/apps/trading/pages/portfolio/index.page.tsx @@ -5,8 +5,8 @@ import { useVegaWallet } from '@vegaprotocol/wallet'; const Portfolio = () => { const { keypair } = useVegaWallet(); return ( -
-

{t('Portfolio')}

+
+

{t('Portfolio')}

{keypair &&

{t(`Keypair: ${keypair.name} ${keypair.pub}`)}

}
{t('Deposit')} @@ -18,4 +18,8 @@ const Portfolio = () => { ); }; +Portfolio.getInitialProps = () => ({ + page: 'portfolio', +}); + export default Portfolio; diff --git a/libs/chart/src/__tests__/generate-candles.ts b/libs/chart/src/__tests__/generate-candles.ts new file mode 100644 index 000000000..8f23f08d6 --- /dev/null +++ b/libs/chart/src/__tests__/generate-candles.ts @@ -0,0 +1,56 @@ +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; +import type { + Candles, + Candles_market_candles, +} from '../lib/__generated__/Candles'; + +export const generateCandles = (override?: PartialDeep): Candles => { + const candles: Candles_market_candles[] = [ + { + datetime: '2022-04-06T09:15:00Z', + high: '17481092', + low: '17403651', + open: '17458833', + close: '17446470', + volume: '82721', + __typename: 'Candle', + }, + { + datetime: '2022-04-06T09:30:00Z', + high: '17491202', + low: '17361138', + open: '17446470', + close: '17367174', + volume: '62637', + __typename: 'Candle', + }, + { + datetime: '2022-04-06T09:45:00Z', + high: '17424522', + low: '17337719', + open: '17367174', + close: '17376455', + volume: '60259', + __typename: 'Candle', + }, + ]; + const defaultResult = { + market: { + id: 'market-id', + decimalPlaces: 5, + tradableInstrument: { + instrument: { + id: '', + name: 'Apple Monthly (30 Jun 2022)', + code: 'AAPL.MF21', + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + candles, + __typename: 'Market', + }, + }; + return merge(defaultResult, override); +}; diff --git a/libs/chart/src/__tests__/generate-chart.ts b/libs/chart/src/__tests__/generate-chart.ts new file mode 100644 index 000000000..fb3d0b1b7 --- /dev/null +++ b/libs/chart/src/__tests__/generate-chart.ts @@ -0,0 +1,27 @@ +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; +import type { + Chart, + Chart_market_data_priceMonitoringBounds, +} from '../lib/__generated__/Chart'; + +export const generateChart = (override?: PartialDeep): Chart => { + const priceMonitoringBound: Chart_market_data_priceMonitoringBounds = { + minValidPrice: '16256291', + maxValidPrice: '18296869', + referencePrice: '17247489', + __typename: 'PriceMonitoringBounds', + }; + const defaultResult = { + market: { + decimalPlaces: 5, + data: { + priceMonitoringBounds: [priceMonitoringBound], + __typename: 'MarketData', + }, + __typename: 'Market', + }, + }; + + return merge(defaultResult, override); +}; diff --git a/libs/chart/src/__tests__/index.ts b/libs/chart/src/__tests__/index.ts new file mode 100644 index 000000000..3f9dd0686 --- /dev/null +++ b/libs/chart/src/__tests__/index.ts @@ -0,0 +1,2 @@ +export * from './generate-candles'; +export * from './generate-chart'; diff --git a/libs/cypress/src/index.ts b/libs/cypress/src/index.ts index 2dc9e67e6..08b2d5279 100644 --- a/libs/cypress/src/index.ts +++ b/libs/cypress/src/index.ts @@ -1,5 +1,9 @@ import { addGetTestIdcommand } from './lib/commands/get-by-test-id'; +import { addMockGQLCommand } from './lib/commands/mock-gql'; +import { addMockVegaWalletCommands } from './lib/commands/mock-vega-wallet'; import { addSlackCommand } from './lib/commands/slack'; addGetTestIdcommand(); addSlackCommand(); +addMockGQLCommand(); +addMockVegaWalletCommands(); diff --git a/libs/cypress/src/lib/commands/mock-gql.ts b/libs/cypress/src/lib/commands/mock-gql.ts new file mode 100644 index 000000000..0ce862547 --- /dev/null +++ b/libs/cypress/src/lib/commands/mock-gql.ts @@ -0,0 +1,21 @@ +import type { RouteHandler } from 'cypress/types/net-stubbing'; + +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + mockGQL(alias: string, handler: RouteHandler): void; + } +} + +export function addMockGQLCommand() { + Cypress.Commands.add( + // @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command + 'mockGQL', + (alias: string, handler: RouteHandler) => { + cy.intercept('POST', 'https://lb.testnet.vega.xyz/query', handler).as( + alias + ); + } + ); +} diff --git a/libs/cypress/src/lib/commands/mock-vega-wallet.ts b/libs/cypress/src/lib/commands/mock-vega-wallet.ts new file mode 100644 index 000000000..4fd655984 --- /dev/null +++ b/libs/cypress/src/lib/commands/mock-vega-wallet.ts @@ -0,0 +1,45 @@ +import merge from 'lodash/merge'; +import type { TransactionResponse } from '@vegaprotocol/vegawallet-service-api-client'; +import type { PartialDeep } from 'type-fest'; + +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + mockVegaCommandSync(txHash: string, signature: string): void; + } +} + +export function addMockVegaWalletCommands() { + Cypress.Commands.add( + // @ts-ignore - ignoring Cypress type error which gets resolved when Cypress uses the command + 'mockVegaCommandSync', + (override?: PartialDeep) => { + const defaultTransactionResponse = { + txHash: 'tx-hash', + tx: { + input_data: + 'CPe6vpiqsPqxDBDC1w7KPkoKQGE4Y2M0NjUwMjhiMGY4OTM4YTYzZTEzNDViYzM2ODc3ZWRmODg4MjNmOWU0ZmI4ZDRlN2VkMmFlMzAwNzA3ZTMYASABKAM4Ag==', + signature: { + // sig value needs to be 'real' so sigToId function doesn't error out + value: 'signature', + algo: 'vega/ed25519', + version: 1, + }, + From: { + PubKey: Cypress.env('vegaPublicKey'), + }, + version: 2, + pow: { + tid: '0CEEC2FDFDB5D68BC0C1E2640440E4AA11E49986CB2929E0F3572E16CB7DC59C', + nonce: 23525, + }, + }, + }; + + cy.intercept('POST', 'http://localhost:1789/api/v1/command/sync', { + body: merge(defaultTransactionResponse, override), + }).as('VegaCommandSync'); + } + ); +} diff --git a/libs/cypress/tsconfig.json b/libs/cypress/tsconfig.json index e258886ff..e567ed7bb 100644 --- a/libs/cypress/tsconfig.json +++ b/libs/cypress/tsconfig.json @@ -11,8 +11,13 @@ } ], "compilerOptions": { + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true } diff --git a/libs/deal-ticket/src/__tests__/generate-deal-ticket-query.ts b/libs/deal-ticket/src/__tests__/generate-deal-ticket-query.ts new file mode 100644 index 000000000..d1bf984e5 --- /dev/null +++ b/libs/deal-ticket/src/__tests__/generate-deal-ticket-query.ts @@ -0,0 +1,37 @@ +import { MarketState, MarketTradingMode } from '@vegaprotocol/types'; +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; +import type { DealTicketQuery } from '../__generated__/DealTicketQuery'; + +export const generateDealTicketQuery = ( + override?: PartialDeep +): DealTicketQuery => { + const defaultResult: DealTicketQuery = { + market: { + id: 'market-id', + decimalPlaces: 2, + state: MarketState.Active, + tradingMode: MarketTradingMode.Continuous, + tradableInstrument: { + instrument: { + product: { + quoteName: 'BTC', + __typename: 'Future', + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + depth: { + __typename: 'MarketDepth', + lastTrade: { + __typename: 'Trade', + price: '100', + }, + }, + __typename: 'Market', + }, + }; + + return merge(defaultResult, override); +}; diff --git a/libs/deal-ticket/src/__tests__/index.ts b/libs/deal-ticket/src/__tests__/index.ts new file mode 100644 index 000000000..0588dead1 --- /dev/null +++ b/libs/deal-ticket/src/__tests__/index.ts @@ -0,0 +1 @@ +export * from './generate-deal-ticket-query'; diff --git a/libs/deal-ticket/src/submit-button.tsx b/libs/deal-ticket/src/submit-button.tsx index b837ece93..bdb0aff5d 100644 --- a/libs/deal-ticket/src/submit-button.tsx +++ b/libs/deal-ticket/src/submit-button.tsx @@ -79,7 +79,11 @@ export const SubmitButton = ({ > {transactionStatus === 'pending' ? t('Pending...') : t('Place order')} - {invalidText && {invalidText}} + {invalidText && ( + + {invalidText} + + )} ); }; diff --git a/libs/market-list/src/__tests__/generate-markets.ts b/libs/market-list/src/__tests__/generate-markets.ts new file mode 100644 index 000000000..73c6f7993 --- /dev/null +++ b/libs/market-list/src/__tests__/generate-markets.ts @@ -0,0 +1,78 @@ +import merge from 'lodash/merge'; +import { MarketState, MarketTradingMode } from '@vegaprotocol/types'; +import type { PartialDeep } from 'type-fest'; +import type { Markets, Markets_markets } from '../lib/__generated__/Markets'; + +export const generateMarkets = (override?: PartialDeep): Markets => { + const markets: Markets_markets[] = [ + { + id: 'market-id', + name: 'ACTIVE MARKET', + decimalPlaces: 5, + data: { + market: { + id: '10cd0a793ad2887b340940337fa6d97a212e0e517fe8e9eab2b5ef3a38633f35', + state: MarketState.Active, + tradingMode: MarketTradingMode.Continuous, + __typename: 'Market', + }, + bestBidPrice: '0', + bestOfferPrice: '0', + markPrice: '4612690058', + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'BTCUSD.MF21', + product: { + settlementAsset: { + symbol: 'tDAI', + __typename: 'Asset', + }, + __typename: 'Future', + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + __typename: 'Market', + }, + { + id: 'test-market-suspended', + name: 'SUSPENDED MARKET', + decimalPlaces: 2, + data: { + market: { + id: '34d95e10faa00c21d19d382d6d7e6fc9722a96985369f0caec041b0f44b775ed', + state: MarketState.Suspended, + tradingMode: MarketTradingMode.Continuous, + __typename: 'Market', + }, + bestBidPrice: '0', + bestOfferPrice: '0', + markPrice: '8441', + __typename: 'MarketData', + }, + tradableInstrument: { + instrument: { + code: 'SOLUSD', + product: { + settlementAsset: { + symbol: 'XYZalpha', + __typename: 'Asset', + }, + __typename: 'Future', + }, + __typename: 'Instrument', + }, + __typename: 'TradableInstrument', + }, + __typename: 'Market', + }, + ]; + const defaultResult = { + markets, + }; + + return merge(defaultResult, override); +}; diff --git a/libs/trades/src/__tests__/generate-trades.ts b/libs/trades/src/__tests__/generate-trades.ts new file mode 100644 index 000000000..2167f3e60 --- /dev/null +++ b/libs/trades/src/__tests__/generate-trades.ts @@ -0,0 +1,53 @@ +import merge from 'lodash/merge'; +import type { PartialDeep } from 'type-fest'; +import type { Trades, Trades_market_trades } from '../lib/__generated__/Trades'; + +export const generateTrades = (override?: PartialDeep): Trades => { + const trades: Trades_market_trades[] = [ + { + id: 'FFFFBC80005C517A10ACF481F7E6893769471098E696D0CC407F18134044CB16', + price: '17116898', + size: '24', + createdAt: '2022-04-06T16:19:42.692598951Z', + market: { + id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff', + decimalPlaces: 5, + __typename: 'Market', + }, + __typename: 'Trade', + }, + { + id: 'FFFFB91453AC8F26EDAC223E2FB6C4A61461B1837946B51D943D675FB94FDF72', + price: '17209102', + size: '7', + createdAt: '2022-04-07T06:59:44.835686754Z', + market: { + id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff', + decimalPlaces: 5, + __typename: 'Market', + }, + __typename: 'Trade', + }, + { + id: 'FFFFAD1BF47AA2853E5C375B6B3A62375F62D5B10807583D32EF3119CC455CD1', + price: '17106734', + size: '18', + createdAt: '2022-04-07T17:56:47.997938583Z', + market: { + id: '0c3c1490db767f926d24fb674b4235a9aa339614915a4ab96cbfc0e1ad83c0ff', + decimalPlaces: 5, + __typename: 'Market', + }, + __typename: 'Trade', + }, + ]; + const defaultResult = { + market: { + id: 'market-id', + trades, + __typename: 'Market', + }, + }; + + return merge(defaultResult, override); +}; diff --git a/libs/trades/src/__tests__/index.ts b/libs/trades/src/__tests__/index.ts new file mode 100644 index 000000000..63e40e825 --- /dev/null +++ b/libs/trades/src/__tests__/index.ts @@ -0,0 +1 @@ +export * from './generate-trades'; diff --git a/package.json b/package.json index 31006fabd..26d5c13f5 100644 --- a/package.json +++ b/package.json @@ -124,6 +124,7 @@ "sass": "1.43.2", "storybook-addon-themes": "^6.1.0", "ts-jest": "27.0.5", + "type-fest": "^2.12.2", "typescript": "~4.5.2", "url-loader": "^3.0.0" }, diff --git a/yarn.lock b/yarn.lock index ab6f169a9..510db2bc8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20171,6 +20171,11 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== +type-fest@^2.12.2: + version "2.12.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.12.2.tgz#80a53614e6b9b475eb9077472fb7498dc7aa51d0" + integrity sha512-qt6ylCGpLjZ7AaODxbpyBZSs9fCI9SkL3Z9q2oxMBQhs/uyY+VD8jHA8ULCGmWQJlBgqvO3EJeAngOHD8zQCrQ== + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"