Test/deal ticket tests (#161)
* scaffold dealticket package, remove trading views from react-helpers * add deal ticket component, add intent utils, expand dialog and form group styles * add splash component, show market not found message if market doesnt exist * tidy up error handling * add handleError method for vega tx hook * add better testname for provider test, flesh out tests a bit more for deal ticket * Add unit tests for useVegaTransaction and useOrderSubmit hooks * add wrapper component for order dialog styles * add vega styled loader to ui toolkit and use in order dialog * add title prop to order dialog * split limit and market tickets into own files * add button radio component * revert dialog styles * move splash component to ui-toolkit, add story * convert intent to enum * Make button always type=button unless type prop is passed * inline filter logic for tif selector * add date-fns, add datetime to helpers * add order types to wallet package, make price undefined if order type is market * use enums in deal ticket logic * tidy up order state by moving submit and transaction hooks out of deal ticket * add comment for dialog styles * remove decimal from price input * add types package, delete old generated types from trading project * rename types package to graphql * update generate command to point to correct locations * fix use order submit test * BDD and navigation tests passing * Remove commented steps * Steps up to placing order * Date picker and date-fns update * Vega connector wallet tests * Passing up to request sent, updated date picker * Tests for sell orders and errors * Update market feature * Fix failing tests * Update wallet login * Readded tx hash assertion and remaining tests * Add CI wallet import * Update .github/workflows/cypress.yml Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com> * Resolved PR comments * Fix yaml error * Attempt to fix failing tests in CI * Run Cypress in Chrome * Add reload if public key error displayed * Fix wallet name * Add force click and waits * Increase timeout for deal ticket page * Removed network list from yaml and using input error id * Increase timeout to 8 seconds * Re add deleted test id Co-authored-by: Matthew Russell <mattrussell36@gmail.com> Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com>
This commit is contained in:
parent
8fd1d00474
commit
205f4124f1
51
.github/workflows/cypress.yml
vendored
51
.github/workflows/cypress.yml
vendored
@ -11,7 +11,13 @@ jobs:
|
|||||||
master:
|
master:
|
||||||
name: Run end-to-end tests - main
|
name: Run end-to-end tests - main
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
vegawallet-version:
|
||||||
|
- '0.13.2'
|
||||||
|
|
||||||
if: ${{ github.event_name != 'pull_request' }}
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
@ -28,11 +34,34 @@ jobs:
|
|||||||
node-version: 16.14.0
|
node-version: 16.14.0
|
||||||
- name: Install root dependencies
|
- name: Install root dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
|
||||||
|
- name: Download and unzip wallet
|
||||||
|
run: curl -L https://github.com/vegaprotocol/vegawallet/releases/download/v${{ matrix.vegawallet-version }}/vegawallet-linux-amd64.zip -O
|
||||||
|
- name: Unzip wallet
|
||||||
|
run: unzip ./vegawallet-linux-amd64.zip
|
||||||
|
- name: Create passphrase
|
||||||
|
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
|
||||||
|
- name: Create recovery
|
||||||
|
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
|
||||||
|
|
||||||
|
- name: Initialize wallet
|
||||||
|
run: ./vegawallet init -f
|
||||||
|
- name: Import wallet
|
||||||
|
run: ./vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase
|
||||||
|
- name: Import config
|
||||||
|
run: ./vegawallet network import --from-url https://raw.githubusercontent.com/vegaprotocol/networks/master/fairground/fairground.toml
|
||||||
|
- name: Start service
|
||||||
|
run: ./vegawallet service run --network fairground &
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.tradingWalletPassphrase=${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }} --browser chrome
|
||||||
pr:
|
pr:
|
||||||
name: Run end-to-end tests - PR
|
name: Run end-to-end tests - PR
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
vegawallet-version:
|
||||||
|
- '0.13.2'
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
if: ${{ github.event_name == 'pull_request' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
@ -51,5 +80,23 @@ jobs:
|
|||||||
node-version: 16.14.0
|
node-version: 16.14.0
|
||||||
- name: Install root dependencies
|
- name: Install root dependencies
|
||||||
run: yarn install
|
run: yarn install
|
||||||
|
- name: Download and unzip wallet
|
||||||
|
run: curl -L https://github.com/vegaprotocol/vegawallet/releases/download/v${{ matrix.vegawallet-version }}/vegawallet-linux-amd64.zip -O
|
||||||
|
- name: Unzip wallet
|
||||||
|
run: unzip ./vegawallet-linux-amd64.zip
|
||||||
|
- name: Create passphrase
|
||||||
|
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
|
||||||
|
- name: Create recovery
|
||||||
|
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
|
||||||
|
|
||||||
|
- name: Initialize wallet
|
||||||
|
run: ./vegawallet init -f
|
||||||
|
- name: Import wallet
|
||||||
|
run: ./vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase
|
||||||
|
- name: Import config
|
||||||
|
run: ./vegawallet network import --from-url https://raw.githubusercontent.com/vegaprotocol/networks/master/fairground/fairground.toml
|
||||||
|
- name: Start service
|
||||||
|
run: ./vegawallet service run --network fairground &
|
||||||
|
|
||||||
- name: Run Cypress tests
|
- name: Run Cypress tests
|
||||||
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
|
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.tradingWalletPassphrase=${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }} --browser chrome
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"screenshotsFolder": "../../dist/cypress/apps/explorer-e2e/screenshots",
|
"screenshotsFolder": "../../dist/cypress/apps/explorer-e2e/screenshots",
|
||||||
"chromeWebSecurity": false,
|
"chromeWebSecurity": false,
|
||||||
"env": {
|
"env": {
|
||||||
"tsConfig": "tsconfig.json"
|
"tsConfig": "tsconfig.json",
|
||||||
|
"TAGS": "not @todo and not @ignore and not @manual"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
apps/trading-e2e/.cypress-cucumber-preprocessorrc
Normal file
3
apps/trading-e2e/.cypress-cucumber-preprocessorrc
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"stepDefinitions": "src/support/step_definitions"
|
||||||
|
}
|
@ -1,13 +1,20 @@
|
|||||||
{
|
{
|
||||||
|
"baseUrl": "http://localhost:4200",
|
||||||
"fileServerFolder": ".",
|
"fileServerFolder": ".",
|
||||||
"fixturesFolder": "./src/fixtures",
|
"fixturesFolder": false,
|
||||||
|
"pluginsFile": "./src/plugins/index.js",
|
||||||
|
"testFiles": "*.{ts,feature,features}",
|
||||||
|
"ignoreTestFiles": "**/*.js",
|
||||||
"integrationFolder": "./src/integration",
|
"integrationFolder": "./src/integration",
|
||||||
"modifyObstructiveCode": false,
|
"modifyObstructiveCode": false,
|
||||||
"supportFile": "./src/support/index.ts",
|
"supportFile": "./src/support/index.ts",
|
||||||
"pluginsFile": false,
|
|
||||||
"video": true,
|
"video": true,
|
||||||
"videosFolder": "../../dist/cypress/apps/trading-e2e/videos",
|
"videosFolder": "../../dist/cypress/apps/trading-e2e/videos",
|
||||||
"screenshotsFolder": "../../dist/cypress/apps/trading-e2e/screenshots",
|
"screenshotsFolder": "../../dist/cypress/apps/trading-e2e/screenshots",
|
||||||
"chromeWebSecurity": false,
|
"chromeWebSecurity": false,
|
||||||
"projectId": "et4snf"
|
"projectId": "et4snf",
|
||||||
|
"env": {
|
||||||
|
"tsConfig": "tsconfig.json",
|
||||||
|
"TAGS": "not @todo and not @ignore and not @manual"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import { getGreeting } from '../support/app.po';
|
|
||||||
|
|
||||||
describe('trading', () => {
|
|
||||||
beforeEach(() => cy.visit('/'));
|
|
||||||
|
|
||||||
it('should display welcome message', () => {
|
|
||||||
// Custom command example, see `../support/commands.ts` file
|
|
||||||
cy.login('my-email@something.com', 'myPassword');
|
|
||||||
|
|
||||||
// Function helper example, see `../support/app.po.ts` file
|
|
||||||
getGreeting().contains('Welcome to Vega Trading App');
|
|
||||||
});
|
|
||||||
});
|
|
18
apps/trading-e2e/src/integration/home-page.feature
Normal file
18
apps/trading-e2e/src/integration/home-page.feature
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Feature: Home page
|
||||||
|
|
||||||
|
Scenario: Visit Home page
|
||||||
|
Given I am on the homepage
|
||||||
|
|
||||||
|
Scenario: Visit Portfolio page
|
||||||
|
Given I am on the homepage
|
||||||
|
And I navigate to portfolio 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
|
||||||
|
Then wallet not running error message is displayed
|
||||||
|
|
||||||
|
Scenario: Unable to connect Vega wallet with blank fields
|
||||||
|
Given I am on the homepage
|
||||||
|
When I try to connect Vega wallet with blank fields
|
||||||
|
Then wallet field validation errors are shown
|
98
apps/trading-e2e/src/integration/market-order.feature
Normal file
98
apps/trading-e2e/src/integration/market-order.feature
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
Feature: Market orders
|
||||||
|
|
||||||
|
Scenario Outline: Successfull market buy orders
|
||||||
|
Given I am on the homepage
|
||||||
|
And I navigate to markets page
|
||||||
|
When I click on active market
|
||||||
|
And I connect to Vega Wallet
|
||||||
|
And place a buy '<marketOrderType>' market order
|
||||||
|
Then order request is sent
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| marketOrderType |
|
||||||
|
| FOK |
|
||||||
|
| 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
|
||||||
|
And I connect to Vega Wallet
|
||||||
|
And place a buy '<limitOrderType>' limit order
|
||||||
|
Then order request is sent
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| limitOrderType |
|
||||||
|
| IOC |
|
||||||
|
| FOK |
|
||||||
|
| GTT |
|
||||||
|
# | GFA | Requires market to be in auction
|
||||||
|
| 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
|
||||||
|
And I connect to Vega Wallet
|
||||||
|
And place a sell '<marketOrderType>' market order
|
||||||
|
Then order request is sent
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| marketOrderType |
|
||||||
|
| FOK |
|
||||||
|
| 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
|
||||||
|
And I connect to Vega Wallet
|
||||||
|
And place a sell '<limitOrderType>' limit order
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
| limitOrderType |
|
||||||
|
| IOC |
|
||||||
|
| FOK |
|
||||||
|
| GTT |
|
||||||
|
# | GFA | Requires market to be in auction
|
||||||
|
| GFN |
|
||||||
|
|
||||||
|
Scenario: Unsuccessfull order because lack of funds
|
||||||
|
Given I am on the homepage
|
||||||
|
And I navigate to markets page
|
||||||
|
When I click on active market
|
||||||
|
And I connect to Vega Wallet
|
||||||
|
And place a buy 'FOK' market order
|
||||||
|
Then error message for insufficient funds is displayed
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
Then place order button is disabled
|
||||||
|
And "No public key selected" error is shown
|
||||||
|
|
||||||
|
Scenario: Unsuccessfull because quantity is 0
|
||||||
|
Given I am on the homepage
|
||||||
|
And I navigate to markets page
|
||||||
|
When I click on 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"
|
||||||
|
|
||||||
|
@manual
|
||||||
|
Scenario: GTT order failed because invalid date
|
||||||
|
|
||||||
|
@manual
|
||||||
|
Scenario: GTT order failed because date in the past
|
||||||
|
|
||||||
|
@manual
|
||||||
|
Scenario: GTT order failed because date over allowed period
|
75
apps/trading-e2e/src/plugins/index.js
Normal file
75
apps/trading-e2e/src/plugins/index.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
const webpackPreprocessor = require('@cypress/webpack-preprocessor');
|
||||||
|
const webpack = require('webpack');
|
||||||
|
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
|
||||||
|
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||||
|
const nodeExternals = require('webpack-node-externals');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Cypress.PluginConfig}
|
||||||
|
*/
|
||||||
|
module.exports = (on, config) => {
|
||||||
|
on(
|
||||||
|
'file:preprocessor',
|
||||||
|
webpackPreprocessor({
|
||||||
|
webpackOptions: {
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
|
||||||
|
plugins: [
|
||||||
|
new TsconfigPathsPlugin({
|
||||||
|
configFile: config.env.tsConfig,
|
||||||
|
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
fallback: {
|
||||||
|
path: require.resolve('path-browserify'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.([jt])sx?$/,
|
||||||
|
loader: 'ts-loader',
|
||||||
|
exclude: [/node_modules/],
|
||||||
|
options: {
|
||||||
|
configFile: config.env.tsConfig,
|
||||||
|
// https://github.com/TypeStrong/ts-loader/pull/685
|
||||||
|
experimentalWatchApi: true,
|
||||||
|
transpileOnly: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.feature$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'cypress-cucumber-preprocessor/loader',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.features$/,
|
||||||
|
use: [
|
||||||
|
{
|
||||||
|
loader: 'cypress-cucumber-preprocessor/lib/featuresLoader',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new ForkTsCheckerWebpackPlugin({
|
||||||
|
typescript: {
|
||||||
|
enabled: true,
|
||||||
|
configFile: config.env.tsConfig,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
process: 'process/browser',
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
externals: [nodeExternals()],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
@ -13,6 +13,7 @@ declare namespace Cypress {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
interface Chainable<Subject> {
|
interface Chainable<Subject> {
|
||||||
login(email: string, password: string): void;
|
login(email: string, password: string): void;
|
||||||
|
getByTestId(selector: string): Chainable<JQuery<HTMLElement>>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
@ -31,3 +32,6 @@ Cypress.Commands.add('login', (email, password) => {
|
|||||||
//
|
//
|
||||||
// -- This will overwrite an existing command --
|
// -- This will overwrite an existing command --
|
||||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||||
|
Cypress.Commands.add('getByTestId', (selector, ...args) => {
|
||||||
|
return cy.get(`[data-testid=${selector}]`, ...args);
|
||||||
|
});
|
||||||
|
56
apps/trading-e2e/src/support/pages/base-page.js
Normal file
56
apps/trading-e2e/src/support/pages/base-page.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
navigateToMarkets() {
|
||||||
|
cy.get(`a[href='${this.marketsUrl}']`)
|
||||||
|
.should('be.visible')
|
||||||
|
.click({ force: true });
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
114
apps/trading-e2e/src/support/pages/deal-ticket-page.js
Normal file
114
apps/trading-e2e/src/support/pages/deal-ticket-page.js
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
export default class DealTicketPage extends BasePage {
|
||||||
|
marketOrderType = 'order-type-TYPE_MARKET';
|
||||||
|
limitOrderType = 'order-type-TYPE_LIMIT';
|
||||||
|
buyOrder = 'order-side-SIDE_BUY';
|
||||||
|
sellOrder = 'order-side-SIDE_SELL';
|
||||||
|
orderSizeField = 'order-size';
|
||||||
|
orderPriceField = 'order-price';
|
||||||
|
orderTypeDropDown = 'order-tif';
|
||||||
|
datePickerField = 'date-picker-field';
|
||||||
|
placeOrderBtn = 'place-order';
|
||||||
|
orderDialog = 'order-wrapper';
|
||||||
|
orderStatusHeader = 'order-status-header';
|
||||||
|
orderTransactionHash = 'tx-hash';
|
||||||
|
orderErrorTxt = 'error-reason';
|
||||||
|
|
||||||
|
placeMarketOrder(isBuy, orderSize, orderType) {
|
||||||
|
cy.get(`[data-testid=${this.placeOrderBtn}]`, { timeout: 8000 }).should(
|
||||||
|
'be.visible'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isBuy == false) {
|
||||||
|
cy.getByTestId(this.sellOrder).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.getByTestId(this.orderSizeField).clear().type(orderSize);
|
||||||
|
cy.getByTestId(this.orderTypeDropDown).select(orderType);
|
||||||
|
}
|
||||||
|
|
||||||
|
placeLimitOrder(isBuy, orderSize, orderPrice, orderType) {
|
||||||
|
cy.getByTestId(this.limitOrderType).click();
|
||||||
|
|
||||||
|
if (isBuy == false) {
|
||||||
|
cy.getByTestId(this.sellOrder).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
cy.getByTestId(this.orderSizeField).clear().type(orderSize);
|
||||||
|
cy.getByTestId(this.orderPriceField).clear().type(orderPrice);
|
||||||
|
cy.getByTestId(this.orderTypeDropDown).select(orderType);
|
||||||
|
|
||||||
|
if (orderType == 'GTT') {
|
||||||
|
const today = new Date(new Date().setSeconds(0));
|
||||||
|
const futureDate = new Date(today.setMonth(today.getMonth() + 1)); // set expiry to one month from now
|
||||||
|
const formattedDate = this.formatDate(futureDate);
|
||||||
|
cy.getByTestId(this.datePickerField).click().type(formattedDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyOrderRequestSent() {
|
||||||
|
cy.getByTestId(this.orderStatusHeader).should(
|
||||||
|
'have.text',
|
||||||
|
'Awaiting network confirmation'
|
||||||
|
);
|
||||||
|
cy.getByTestId(this.orderTransactionHash)
|
||||||
|
.invoke('text')
|
||||||
|
.should('contain', 'Tx hash: ')
|
||||||
|
.and('have.length.above', 64);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyOrderFailedInsufficientFunds() {
|
||||||
|
cy.get(`[data-testid=${this.orderErrorTxt}]`, { timeout: 8000 }).should(
|
||||||
|
'have.text',
|
||||||
|
'Reason: InsufficientAssetBalance'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
clickPlaceOrder() {
|
||||||
|
cy.getByTestId(this.placeOrderBtn).click();
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyPlaceOrderBtnDisabled() {
|
||||||
|
cy.getByTestId(this.placeOrderBtn).should('be.disabled');
|
||||||
|
}
|
||||||
|
|
||||||
|
verifySubmitBtnErrorText(expectedText) {
|
||||||
|
cy.getByTestId(this.inputError).should('have.text', expectedText);
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyOrderRejected(errorMsg) {
|
||||||
|
cy.getByTestId(this.orderStatusHeader).should(
|
||||||
|
'have.text',
|
||||||
|
'Order rejected by wallet'
|
||||||
|
);
|
||||||
|
cy.getByTestId(this.orderDialog)
|
||||||
|
.find('pre')
|
||||||
|
.should('contain.text', errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPageIfPublicKeyErrorDisplayed() {
|
||||||
|
cy.get('body').then(($body) => {
|
||||||
|
if ($body.find(`[data-testid=${this.inputError}]`).length) {
|
||||||
|
cy.getByTestId(this.inputError)
|
||||||
|
.invoke('text')
|
||||||
|
.then(($errorText) => {
|
||||||
|
if ($errorText == 'No public key selected') {
|
||||||
|
cy.reload;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
formatDate(date) {
|
||||||
|
const padZero = (num) => num.toString().padStart(2, '0');
|
||||||
|
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = padZero(date.getMonth() + 1);
|
||||||
|
const day = padZero(date.getDate());
|
||||||
|
const hours = padZero(date.getHours());
|
||||||
|
const minutes = padZero(date.getMinutes());
|
||||||
|
|
||||||
|
return `${year}-${month}-${day}T${hours}:${minutes}`;
|
||||||
|
}
|
||||||
|
}
|
54
apps/trading-e2e/src/support/pages/markets-page.js
Normal file
54
apps/trading-e2e/src/support/pages/markets-page.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import BasePage from './base-page';
|
||||||
|
|
||||||
|
export default class MarketPage extends BasePage {
|
||||||
|
marketRow = 'market-row';
|
||||||
|
chartTab = 'chart';
|
||||||
|
ticketTab = 'ticket';
|
||||||
|
orderbookTab = 'orderbook';
|
||||||
|
ordersTab = 'orders';
|
||||||
|
positionsTab = 'positions';
|
||||||
|
collateralTab = 'collateral';
|
||||||
|
tradesTab = 'trades';
|
||||||
|
completedTrades = 'market-trades';
|
||||||
|
orderBookTab = 'orderbook';
|
||||||
|
|
||||||
|
validateMarketsAreDisplayed() {
|
||||||
|
cy.getByTestId(this.marketRow).should('have.length.above', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.getByTestId(this.marketRow).first().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();
|
||||||
|
}
|
||||||
|
}
|
27
apps/trading-e2e/src/support/step_definitions/common-step.js
Normal file
27
apps/trading-e2e/src/support/step_definitions/common-step.js
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Given, Then, 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();
|
||||||
|
});
|
@ -0,0 +1,22 @@
|
|||||||
|
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
|
||||||
|
import MarketsPage from '../pages/markets-page';
|
||||||
|
const marketsPage = new MarketsPage();
|
||||||
|
|
||||||
|
When('I try to connect Vega wallet with incorrect details', () => {
|
||||||
|
marketsPage.navigateToConnectVegaWallet();
|
||||||
|
marketsPage.fillInWalletForm('name', 'wrong passphrase');
|
||||||
|
marketsPage.clickConnectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
When('I try to connect Vega wallet with blank fields', () => {
|
||||||
|
marketsPage.navigateToConnectVegaWallet();
|
||||||
|
marketsPage.clickConnectVegaWallet();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('wallet not running error message is displayed', () => {
|
||||||
|
marketsPage.validateWalletNotRunningError();
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('wallet field validation errors are shown', () => {
|
||||||
|
marketsPage.validateWalletErrorFieldsDisplayed();
|
||||||
|
});
|
@ -0,0 +1,65 @@
|
|||||||
|
import { Given, Then, 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();
|
||||||
|
|
||||||
|
When('I click on market for {string}', (marketText) => {
|
||||||
|
marketsPage.clickOnMarket(marketText);
|
||||||
|
});
|
||||||
|
|
||||||
|
When('I click on active market', () => {
|
||||||
|
marketsPage.clickOnMarket('Active');
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
When('I click on suspended market', () => {
|
||||||
|
marketsPage.clickOnMarket('Suspended');
|
||||||
|
});
|
||||||
|
|
||||||
|
Then('order request is sent', () => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
);
|
@ -27,6 +27,7 @@ export const VegaWalletConnectButton = ({
|
|||||||
<span className="text-ui-small font-mono mr-2">Vega key:</span>
|
<span className="text-ui-small font-mono mr-2">Vega key:</span>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
|
data-testid="connect-vega-wallet"
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
className="ml-auto inline-block text-ui-small font-mono hover:underline"
|
className="ml-auto inline-block text-ui-small font-mono hover:underline"
|
||||||
>
|
>
|
||||||
|
@ -53,7 +53,11 @@ export const GridTabs = ({ children, group }: GridTabsProps) => {
|
|||||||
'bg-black-10 dark:bg-white-10': !isActive,
|
'bg-black-10 dark:bg-white-10': !isActive,
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<Tabs.Trigger value={child.props.id} className={triggerClass}>
|
<Tabs.Trigger
|
||||||
|
data-testid={child.props.name}
|
||||||
|
value={child.props.id}
|
||||||
|
className={triggerClass}
|
||||||
|
>
|
||||||
{child.props.name}
|
{child.props.name}
|
||||||
</Tabs.Trigger>
|
</Tabs.Trigger>
|
||||||
);
|
);
|
||||||
|
@ -156,6 +156,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
|
|||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
data-testid={key}
|
||||||
onClick={() => setView(key as TradingView)}
|
onClick={() => setView(key as TradingView)}
|
||||||
className={className}
|
className={className}
|
||||||
key={key}
|
key={key}
|
||||||
|
@ -14,6 +14,7 @@ export const ExpirySelector = ({ order, onSelect }: ExpirySelectorProps) => {
|
|||||||
return (
|
return (
|
||||||
<FormGroup label="Expiry time/date" labelFor="expiration">
|
<FormGroup label="Expiry time/date" labelFor="expiration">
|
||||||
<Input
|
<Input
|
||||||
|
data-testid="date-picker-field"
|
||||||
id="expiration"
|
id="expiration"
|
||||||
name="expiration"
|
name="expiration"
|
||||||
type="datetime-local"
|
type="datetime-local"
|
||||||
|
@ -40,7 +40,9 @@ export const OrderDialog = ({
|
|||||||
icon={<Loader />}
|
icon={<Loader />}
|
||||||
>
|
>
|
||||||
{transaction.hash && (
|
{transaction.hash && (
|
||||||
<p className="break-all">{t(`Tx hash: ${transaction.hash}`)}</p>
|
<p data-testid="tx-hash" className="break-all">
|
||||||
|
{t(`Tx hash: ${transaction.hash}`)}
|
||||||
|
</p>
|
||||||
)}
|
)}
|
||||||
</OrderDialogWrapper>
|
</OrderDialogWrapper>
|
||||||
);
|
);
|
||||||
@ -53,7 +55,9 @@ export const OrderDialog = ({
|
|||||||
title="Order failed"
|
title="Order failed"
|
||||||
icon={<Icon name="warning-sign" size={20} />}
|
icon={<Icon name="warning-sign" size={20} />}
|
||||||
>
|
>
|
||||||
<p>{t(`Reason: ${finalizedOrder.rejectionReason}`)}</p>
|
<p data-testid="error-reason">
|
||||||
|
{t(`Reason: ${finalizedOrder.rejectionReason}`)}
|
||||||
|
</p>
|
||||||
</OrderDialogWrapper>
|
</OrderDialogWrapper>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -97,8 +101,10 @@ const OrderDialogWrapper = ({
|
|||||||
return (
|
return (
|
||||||
<div className="flex gap-12 max-w-full">
|
<div className="flex gap-12 max-w-full">
|
||||||
<div className="pt-8 fill-current">{icon}</div>
|
<div className="pt-8 fill-current">{icon}</div>
|
||||||
<div className="flex-1">
|
<div data-testid="order-wrapper" className="flex-1">
|
||||||
<h1 className="text-h4">{title}</h1>
|
<h1 data-testid="order-status-header" className="text-h4">
|
||||||
|
{title}
|
||||||
|
</h1>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -75,6 +75,7 @@ export const SubmitButton = ({
|
|||||||
variant="primary"
|
variant="primary"
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
|
data-testid="place-order"
|
||||||
>
|
>
|
||||||
{transactionStatus === 'pending' ? t('Pending...') : t('Place order')}
|
{transactionStatus === 'pending' ? t('Pending...') : t('Place order')}
|
||||||
</Button>
|
</Button>
|
||||||
|
@ -35,7 +35,11 @@ export const InputError = ({
|
|||||||
'fill-intent-warning': intent === 'warning',
|
'fill-intent-warning': intent === 'warning',
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<div className={effectiveClassName} {...props}>
|
<div
|
||||||
|
data-testid="input-error-text"
|
||||||
|
className={effectiveClassName}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
<Icon name="warning-sign" className={iconClassName} />
|
<Icon name="warning-sign" className={iconClassName} />
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,7 +60,11 @@ export function RestConnectorForm({
|
|||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
/>
|
/>
|
||||||
{errors.wallet?.message && (
|
{errors.wallet?.message && (
|
||||||
<InputError intent="danger" className="mt-4">
|
<InputError
|
||||||
|
data-testid="input-wallet-error"
|
||||||
|
intent="danger"
|
||||||
|
className="mt-4"
|
||||||
|
>
|
||||||
{errors.wallet.message}
|
{errors.wallet.message}
|
||||||
</InputError>
|
</InputError>
|
||||||
)}
|
)}
|
||||||
|
Loading…
Reference in New Issue
Block a user