Merge branch 'master' of github.com:vegaprotocol/frontend-monorepo

This commit is contained in:
madalinaraicu 2022-07-14 12:19:13 +03:00
commit e5efa417a0
215 changed files with 5445 additions and 2947 deletions

View File

@ -1,16 +1,13 @@
const { defineConfig } = require('cypress');
const setupNodeEvents = require('./src/plugins/index.js');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
setupNodeEvents,
baseUrl: 'http://localhost:3000',
fileServerFolder: '.',
fixturesFolder: false,
specPattern: '**/*.feature',
excludeSpecPattern: '**/*.js',
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
modifyObstructiveCode: false,
supportFile: './src/support/index.ts',
video: true,

View File

@ -1,11 +0,0 @@
Feature: Asset Page
Scenario: Navigate to Asset Page
Given I am on the homepage
When I navigate to the asset page
Then asset page is correctly displayed
Scenario: Navigate to Asset Page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the asset page
Then asset page is correctly displayed

View File

@ -0,0 +1,20 @@
import '../support/common.functions';
context('Asset page', function () {
describe('Verify elements on page', function () {
const assetsNavigation = 'a[href="/assets"]';
const assetHeader = '[data-testid="asset-header"]';
it('Assets page is displayed', function () {
cy.visit('/');
cy.get(assetsNavigation).click();
cy.common_validate_blocks_data_displayed(assetHeader);
});
it('Assets page displayed in mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(assetsNavigation).click();
cy.common_validate_blocks_data_displayed(assetHeader);
});
});
});

View File

@ -1,38 +0,0 @@
Feature: Blocks Page
Scenario: Navigate to blocks page
Given I am on the homepage
When I navigate to the blocks page
Then blocks page is correctly displayed
Scenario: Navigate to blocks page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the blocks page
Then blocks page is correctly displayed
Scenario: Navigate to block validator page
Given I am on the homepage
When I navigate to the blocks page
And I click on top block
Then validator block page is correctly displayed
Scenario: Navigate to previous block
Given I am on the homepage
When I navigate to the blocks page
And I click on top block
Then I am on the previous block when I click previous
Scenario: Previous button disabled on first block
Given I am on the homepage
When I navigate to the blocks page
And jump to first block
Then previous button is disabled
And I am on the second block when I click next
# Skipping these tests for time being - since blockchain in capsule
# is now too small to show historical data - re-enable once addressed
# Scenario: Infinite scroll shows at least 300 new blocks
# Given I am on the homepage
# When I navigate to the blocks page
# And I scroll down to the last block on the page
# Then I can expect to see at least 100 blocks if i scroll 7 times

View File

@ -0,0 +1,132 @@
import '../support/common.functions';
context('Blocks page', function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('Verify elements on page', function () {
const blockNavigation = 'a[href="/blocks"]';
const blockHeight = '[data-testid="block-height"]';
const blockTime = '[data-testid="block-time"]';
const blockHeader = '[data-testid="block-header"]';
const previousBlockBtn = '[data-testid="previous-block"]';
const infiniteScrollWrapper = '[data-testid="infinite-scroll-wrapper"]';
beforeEach('navigate to blocks page', function () {
cy.get(blockNavigation).click();
});
it('Blocks page is displayed', function () {
validateBlocksDisplayed();
});
it('Blocks page is displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(blockNavigation).click();
validateBlocksDisplayed();
});
it('Block validator page is displayed', function () {
waitForBlocksResponse();
cy.get(blockHeight).eq(0).click();
cy.get('[data-testid="block-validator"]').should('not.be.empty');
cy.get(blockTime).should('not.be.empty');
//TODO: Add assertion for transactions when txs are added
});
it('Navigate to previous block', function () {
waitForBlocksResponse();
cy.get(blockHeight).eq(0).click();
cy.get(blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
cy.get(previousBlockBtn).click();
cy.get(blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.lessThan(blockHeight);
});
});
});
it('Previous button disabled on first block', function () {
cy.get('[data-testid="block-input"]').type('1');
cy.get('[data-testid="go-submit"]').click();
cy.get(previousBlockBtn).find('button').should('be.disabled');
cy.get(blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
cy.get('[data-testid="next-block"]').click();
cy.get(blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.greaterThan(blockHeight);
});
});
});
// Skipping these tests for time being - since blockchain in capsule is now too small to show historical data - re-enable once addressed
it.skip('Infinite scroll shows at least 100 new blocks', function () {
const expectedBlocks = 100;
const scrollAttempts = 7;
waitForBlocksResponse();
cy.get(infiniteScrollWrapper).children().scrollTo('bottom');
cy.intercept('*blockchain?maxHeight*').as('blockchain_load');
cy.get(blockHeight)
.last()
.invoke('text')
.then(($initialLastBlockHeight) => {
for (let index = 0; index < scrollAttempts; index++) {
cy.get(infiniteScrollWrapper)
.children()
.children()
.invoke('css', 'height')
.then((scrollTarget) => {
cy.get(infiniteScrollWrapper)
.children()
.scrollTo(0, scrollTarget.toString(), { easing: 'linear' })
.wait('@blockchain_load');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(50); // Need this as although network response has arrived it takes a few millisecs for the css height to expand
});
}
cy.get(blockHeight)
.last()
.invoke('text')
.then(($lastBlockHeight) => {
const totalBlocksLoadedSinceScrollBegan =
parseInt($initialLastBlockHeight) - parseInt($lastBlockHeight);
expect(totalBlocksLoadedSinceScrollBegan).to.be.at.least(
expectedBlocks
);
});
});
});
function waitForBlocksResponse() {
cy.contains('Loading...').should('not.exist', { timeout: 18000 });
}
function validateBlocksDisplayed() {
waitForBlocksResponse();
cy.get('[data-testid="block-row"]').should('have.length.above', 1);
cy.get(blockHeight).first().should('not.be.empty');
cy.get('[data-testid="num-txs"]').first().should('not.be.empty');
cy.get('[data-testid="validator-link"]').first().should('not.be.empty');
cy.get(blockTime).first().should('not.be.empty');
}
});
});

View File

@ -1,27 +0,0 @@
Feature: Home page
Scenario: Stats page displayed correctly
Given I am on the homepage
Then the stats for deployed environment are correctly displayed
Scenario Outline: Succesfful search for specific id by <IdType>
Given I am on the homepage
When I search for '<Id>'
Then I am redirected to page containing id '<Id>'
Examples:
| IdType | Id |
| Block Id | 973624 |
| Tx Hash | 9ED3718AA8308E7E08EC588EE7AADAF49711D2138860D8914B4D81A2054D9FB8 |
| Tx Id | 0x61DCCEBB955087F50D0B85382DAE138EDA9631BF1A4F92E563D528904AA38898 |
Scenario Outline: Error message displayed when invalid search by <invalidType>
Given I am on the homepage
When I search for '<Id>'
Then search error message "<errorMessage>" is displayed
Examples:
| invalidType | Id | errorMessage |
| wrong string length | 9ED3718AA8308E7E08EC588EE7AADAF497D2138860D8914B4D81A2054D9FB8 | Something doesn't look right |
| invalid hash | 9ED3718AA8308E7E08ECht8EE753DAF49711D2138860D8914B4D81A2054D9FB8 | Transaction is not hexadecimal |
| empty search | | Search required |

View File

@ -0,0 +1,141 @@
context('Home Page', function () {
before('visit home page', function () {
cy.visit('/');
});
describe('Stats page', function () {
const statsValue = '[data-testid="stats-value"]';
it('Should show connected environment', function () {
const deployedEnv = Cypress.env('environment').toUpperCase();
cy.get('[data-testid="stats-environment"]').should(
'have.text',
`/ ${deployedEnv}`
);
});
it('should show connected environment stats', function () {
const statTitles = {
0: 'Status',
1: 'Height',
2: 'Validating nodes',
3: 'Uptime',
4: 'Total nodes',
5: 'Inactive nodes',
6: 'Total staked',
7: 'Backlog',
8: 'Trades / second',
9: 'Orders / block',
10: 'Orders / second',
11: 'Transactions / block',
12: 'Block time',
13: 'Time',
14: 'App',
15: 'Tendermint',
16: 'Up since',
17: 'Chain ID',
};
cy.get('[data-testid="stats-title"]')
.each(($list, index) => {
cy.wrap($list).should('have.text', statTitles[index]);
})
.then(($list) => {
cy.wrap($list).should('have.length', 18);
});
cy.get(statsValue).eq(0).should('have.text', 'CONNECTED');
cy.get(statsValue).eq(1).should('not.be.empty');
cy.get(statsValue).eq(2).should('have.text', '2');
cy.get(statsValue)
.eq(3)
.invoke('text')
.should('match', /\d+d \d+h \d+m \d+s/i);
cy.get(statsValue).eq(4).should('have.text', '2');
cy.get(statsValue).eq(5).should('have.text', '0');
cy.get(statsValue).eq(6).should('have.text', '0.00');
cy.get(statsValue).eq(7).should('have.text', '0');
cy.get(statsValue).eq(8).should('have.text', '0');
cy.get(statsValue).eq(9).should('have.text', '0');
cy.get(statsValue).eq(10).should('have.text', '0');
cy.get(statsValue).eq(11).should('not.be.empty');
cy.get(statsValue).eq(12).should('not.be.empty');
cy.get(statsValue).eq(13).should('not.be.empty');
if (Cypress.env('NIGHTLY_RUN') != true) {
cy.get(statsValue)
.eq(14)
.invoke('text')
.should('match', /v\d+\.\d+\.\d+/i);
}
cy.get(statsValue)
.eq(15)
.invoke('text')
.should('match', /\d+\.\d+\.\d+/i);
cy.get(statsValue).eq(16).should('not.be.empty');
cy.get(statsValue).eq(17).should('not.be.empty');
});
it('Block height should be updating', function () {
cy.get(statsValue)
.eq(1)
.invoke('text')
.then((blockHeightTxt) => {
cy.get(statsValue)
.eq(1)
.invoke('text')
.should((newBlockHeightTxt) => {
expect(blockHeightTxt).not.to.eq(newBlockHeightTxt);
});
});
});
});
describe('Search bar', function () {
it('Successful search for specific id by block id', function () {
const blockId = '973624';
search(blockId);
cy.url().should('include', blockId);
});
it('Successful search for specific id by tx hash', function () {
const txHash =
'9ED3718AA8308E7E08EC588EE7AADAF49711D2138860D8914B4D81A2054D9FB8';
search(txHash);
cy.url().should('include', txHash);
});
it('Successful search for specific id by tx id', function () {
const txId =
'0x61DCCEBB955087F50D0B85382DAE138EDA9631BF1A4F92E563D528904AA38898';
search(txId);
cy.url().should('include', txId);
});
it('Error message displayed when invalid search by wrong string length', function () {
search('9ED3718AA8308E7E08EC588EE7AADAF497D2138860D8914B4D81A2054D9FB8');
validateSearchError("Something doesn't look right");
});
it('Error message displayed when invalid search by invalid hash', function () {
search(
'9ED3718AA8308E7E08ECht8EE753DAF49711D2138860D8914B4D81A2054D9FB8'
);
validateSearchError('Transaction is not hexadecimal');
});
it('Error message displayed when searching empty field', function () {
cy.get('[data-testid="search"]').clear();
cy.get('[data-testid="search-button"]').click();
validateSearchError('Search required');
});
function search(searchTxt) {
cy.get('[data-testid="search"]').clear().type(searchTxt);
cy.get('[data-testid="search-button"]').click();
}
function validateSearchError(expectedError) {
cy.get('[data-testid="search-error"]').should('have.text', expectedError);
}
});
});

View File

@ -0,0 +1,21 @@
import '../support/common.functions';
//Tests set to skip until market bug for capsule checkpoint is fixed
context.skip('Market page', function () {
describe('Verify elements on page', function () {
const marketHeaders = '[data-testid="markets-header"]';
const marketNavigation = 'a[href="/markets"]';
it('Markets page is displayed', function () {
cy.visit('/');
cy.get(marketNavigation).click();
cy.common_validate_blocks_data_displayed(marketHeaders);
});
it('Markets page displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(marketNavigation).click();
cy.common_validate_blocks_data_displayed(marketHeaders);
});
});
});

View File

@ -1,14 +0,0 @@
@todo
Feature: Markets Page
@todo - Needs markets in capsule state
Scenario: Navigate to markets page
Given I am on the homepage
When I navigate to the markets page
Then markets page is correctly displayed
@todo
Scenario: Navigate to markets page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the markets page
Then markets page is correctly displayed

View File

@ -1,11 +0,0 @@
Feature: Network parameters Page
Scenario: Navigate to network parameters page
Given I am on the homepage
When I navigate to network parameters page
Then network parameters page is correctly displayed
Scenario: Navigate to network parameters page using mobile
Given I am on mobile and open the toggle menu
When I navigate to network parameters page
Then network parameters page is correctly displayed

View File

@ -0,0 +1,35 @@
import '../support/common.functions';
context('Network parameters page', function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('Verify elements on page', function () {
const networkParametersNavigation = 'a[href="/network-parameters"]';
beforeEach('Navigate to network parameter page', function () {
cy.get(networkParametersNavigation).click();
});
it('Network paremeter page is displayed', function () {
verifyNetworkParametersPageDisplayed();
});
it('Network parameter page displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(networkParametersNavigation).click();
verifyNetworkParametersPageDisplayed();
});
});
function verifyNetworkParametersPageDisplayed() {
cy.get('[data-testid="network-param-header"]').should(
'have.text',
'Network Parameters'
);
cy.common_verify_json_parameters(18);
cy.common_verify_json_string_values(6);
cy.common_verify_json_int_values(7);
}
});

View File

@ -1,21 +0,0 @@
@ignore
# tendermint times out getting txs on testnet atm
Feature: Transactions Page
Scenario: Navigate to transactions page
Given I am on the homepage
When I navigate to the transactions page
Then transactions page is correctly displayed
Scenario: Navigate to transaction details page
Given I am on the homepage
When I navigate to the transactions page
And I click on the top transaction
Then transaction details are displayed
Scenario: Navigate to transactions page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the transactions page
Then transactions page is correctly displayed
When I click on the top transaction
Then transaction details are displayed

View File

@ -0,0 +1,87 @@
//Tests set to skip until transactions are generated after capsule start up
context.skip('Transactions page', function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('Verify elements on page', function () {
const transactionNavigation = 'a[href="/txs"]';
const transactionRow = 'transaction-row';
const txHash = 'hash';
beforeEach('Navigate to transactions page', function () {
cy.get(transactionNavigation).click();
});
it('transactions are displayed', function () {
cy.get(transactionRow).should('have.length.above', 1);
});
it('transactions details page is displayed', function () {
clickTopTransaction();
validateTxDetailsAreDisplayed();
});
it('transactions details page is displayed in mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(transactionNavigation).click();
clickTopTransaction();
validateTxDetailsAreDisplayed();
});
function clickTopTransaction() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(transactionRow).length) {
cy.get(transactionRow)
.first()
.find('a')
.first()
.click({ force: true });
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
function validateTxDetailsAreDisplayed() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(txHash).length) {
cy.get(txHash).invoke('text').should('have.length', 64);
cy.get('submitted-by')
.find('a')
.then(($address) => {
cy.wrap($address).should('have.attr', 'href');
cy.wrap($address).invoke('text').should('have.length', 66);
});
cy.get('block').should('not.be.empty');
cy.get('encoded-tnx').should('not.be.empty');
cy.get('tx-type')
.should('not.be.empty')
.invoke('text')
.then((txTypeTxt) => {
if (txTypeTxt == 'Order Submission') {
cy.get('.hljs-attr')
.should('have.length.at.least', 8)
.each(($propertyName) => {
cy.wrap($propertyName).should('not.be.empty');
});
cy.get('.hljs-string')
.should('have.length.at.least', 8)
.each(($propertyValue) => {
cy.wrap($propertyValue).should('not.be.empty');
});
}
});
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
});
});

View File

@ -0,0 +1,28 @@
import '../support/common.functions';
context('Validator page', function () {
describe('Verify elements on page', function () {
const validatorNavigation = 'a[href="/validators"]';
it('Validator page is displayed', function () {
cy.visit('/');
cy.get(validatorNavigation).click();
validateValidatorsDisplayed();
});
it('Validator page is displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(validatorNavigation).click();
validateValidatorsDisplayed();
});
function validateValidatorsDisplayed() {
cy.get('[data-testid="tendermint-header"]').should(
'have.text',
'Tendermint data'
);
cy.get('[data-testid="vega-header"]').should('have.text', 'Vega data');
cy.get('[data-testid="vega-data"]').should('not.be.empty');
}
});
});

View File

@ -1,11 +0,0 @@
Feature: Validators Page
Scenario: Navigate to validators page
Given I am on the homepage
When I navigate to the validators page
Then validators page is correctly displayed
Scenario: Navigate to validators page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the validators page
Then validators page is correctly displayed

View File

@ -1,75 +0,0 @@
/// <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()],
},
})
);
};

View File

@ -0,0 +1,53 @@
Cypress.Commands.add(
'common_validate_blocks_data_displayed',
function (headerTestId) {
cy.get(headerTestId).then(($assetHeaders) => {
const headersLength = Number($assetHeaders.length);
cy.wrap($assetHeaders).each(($header) => {
expect($header).to.not.be.empty;
});
cy.get('.language-json')
.each(($asset) => {
expect($asset).to.not.be.empty;
})
.then(($list) => {
expect($list).to.have.length(headersLength);
});
});
}
);
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
cy.viewport('iphone-x');
cy.visit('/');
cy.get('[data-testid="open-menu"]').click();
});
Cypress.Commands.add('common_verify_json_parameters', function (expectedNum) {
cy.get('.hljs-attr')
.should('have.length.at.least', expectedNum)
.each(($paramName) => {
cy.wrap($paramName).should('not.be.empty');
});
});
Cypress.Commands.add(
'common_verify_json_string_values',
function (expectedNum) {
cy.get('.hljs-string')
.should('have.length.at.least', expectedNum)
.each(($paramValue) => {
cy.wrap($paramValue).should('not.be.empty');
});
}
);
Cypress.Commands.add('common_verify_json_int_values', function (expectedNum) {
cy.get('.hljs-number')
.should('have.length.at.least', expectedNum)
.each(($paramValue) => {
cy.wrap($paramValue).should('not.be.empty');
});
});

View File

@ -0,0 +1,2 @@
// type definitions for Cypress object "cy"
/// <reference types="cypress" />

View File

@ -1,9 +0,0 @@
import BasePage from './base-page';
export default class AssetsPage extends BasePage {
assetHeader = 'asset-header';
validateAssetsDisplayed() {
this.validateBlockDataDisplayed(this.assetHeader);
}
}

View File

@ -1,100 +0,0 @@
export default class BasePage {
transactionsUrl = '/txs';
blocksUrl = '/blocks';
partiesUrl = '/parties';
assetsUrl = '/assets';
genesisUrl = '/genesis';
governanceUrl = '/governance';
marketsUrl = '/markets';
networkParametersUrl = '/network-parameters';
validatorsUrl = '/validators';
blockExplorerHeader = 'explorer-header';
searchField = 'search';
searchButton = 'search-button';
searchError = 'search-error';
openMobileMenuBtn = 'open-menu';
navigateToTxs() {
cy.get(`a[href='${this.transactionsUrl}']`).click();
}
navigateToBlocks() {
cy.get(`a[href='${this.blocksUrl}']`).click();
}
navigateToParties() {
cy.get(`a[href='${this.partiesUrl}']`).click();
}
navigateToAssets() {
cy.get(`a[href*='${this.assetsUrl}']`).click();
}
navigateToGenesis() {
cy.get(`a[href='${this.genesisUrl}']`).click();
}
navigateToGovernance() {
cy.get(`a[href='${this.governanceUrl}']`).click();
}
navigateToMarkets() {
cy.get(`a[href='${this.marketsUrl}']`).click();
}
navigateToNetworkParameters() {
cy.get(`a[href='${this.networkParametersUrl}']`).click();
}
navigateToValidators() {
cy.get(`a[href='${this.validatorsUrl}']`).click();
}
search(searchText: string) {
if (searchText) {
cy.getByTestId(this.searchField).type(searchText);
}
}
clickSearch() {
cy.getByTestId(this.searchButton).click();
}
clickOnToggle() {
cy.getByTestId(this.openMobileMenuBtn).click({ force: true });
}
validateUrl(expectedUrl: string) {
cy.url().should('include', expectedUrl);
}
validateSearchDisplayed() {
cy.getByTestId(this.blockExplorerHeader).should(
'have.text',
'Vega Explorer'
);
cy.getByTestId(this.searchField).should('be.visible');
}
validateSearchErrorDisplayed(errorMessage: string) {
cy.getByTestId(this.searchError).should('have.text', errorMessage);
}
validateBlockDataDisplayed(headerTestId: string) {
cy.getByTestId(headerTestId).then(($assetHeaders) => {
const headersAmount = Number($assetHeaders.length);
cy.wrap($assetHeaders).each(($header) => {
expect($header).to.not.be.empty;
});
cy.get('.language-json')
.each(($asset) => {
expect($asset).to.not.be.empty;
})
.then(($list) => {
expect($list).to.have.length(headersAmount);
});
});
}
}

View File

@ -1,146 +0,0 @@
import BasePage from './base-page';
export default class BlocksPage extends BasePage {
blockRow = 'block-row';
transactionsRow = 'transaction-row';
blockHeight = 'block-height';
numberOfTransactions = 'num-txs';
validatorLink = 'validator-link';
blockTime = 'block-time';
refreshBtn = 'refresh';
blockHeader = 'block-header';
minedByValidator = 'block-validator';
previousBlockBtn = 'previous-block';
nextBlockBtn = 'next-block';
jumpToBlockInput = 'block-input';
jumpToBlockSubmit = 'go-submit';
infiniteScrollWrapper = 'infinite-scroll-wrapper';
private waitForBlocksResponse() {
cy.contains('Loading...').should('not.exist', { timeout: 18000 });
}
validateBlocksPageDisplayed() {
this.waitForBlocksResponse();
cy.getByTestId(this.blockRow).should('have.length.above', 1);
cy.getByTestId(this.blockHeight).first().should('not.be.empty');
cy.getByTestId(this.numberOfTransactions).first().should('not.be.empty');
cy.getByTestId(this.validatorLink).first().should('not.be.empty');
cy.getByTestId(this.blockTime).first().should('not.be.empty');
}
clickOnTopBlockHeight() {
this.waitForBlocksResponse();
cy.getByTestId(this.blockHeight).first().click();
}
validateBlockValidatorPage() {
cy.getByTestId(this.minedByValidator).should('not.be.empty');
cy.getByTestId(this.blockTime).should('not.be.empty');
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.transactionsRow}] > td`).length) {
cy.get(`[data-testid=${this.transactionsRow}] > td`).each(($cell) => {
cy.wrap($cell).should('not.be.empty');
});
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
navigateToPreviousBlock() {
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
this.clickPreviousBlock();
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.lessThan(blockHeight);
});
});
}
navigateToNextBlock() {
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
this.clickNextBlock();
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.greaterThan(blockHeight);
});
});
}
navigateToLastBlockOnPage() {
this.waitForBlocksResponse();
cy.getByTestId(this.infiniteScrollWrapper).children().scrollTo('bottom');
}
navigateToOlderBlocksWithInfiniteScroll(
expectedBlocks: number,
scrollAttempts: number
) {
cy.intercept('*blockchain?maxHeight*').as('blockchain_load');
cy.getByTestId(this.blockHeight)
.last()
.invoke('text')
.then(($initialLastBlockHeight) => {
for (let index = 0; index < scrollAttempts; index++) {
cy.getByTestId(this.infiniteScrollWrapper)
.children()
.children()
.invoke('css', 'height')
.then((scrollTarget) => {
cy.getByTestId(this.infiniteScrollWrapper)
.children()
.scrollTo(0, scrollTarget.toString(), { easing: 'linear' })
.wait('@blockchain_load');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(50); // Need this as although network response has arrived it takes a few millisecs for the css height to expand
});
}
cy.getByTestId(this.blockHeight)
.last()
.invoke('text')
.then(($lastBlockHeight) => {
const totalBlocksLoadedSinceScrollBegan =
parseInt($initialLastBlockHeight) - parseInt($lastBlockHeight);
expect(totalBlocksLoadedSinceScrollBegan).to.be.at.least(
expectedBlocks
);
});
});
}
clickPreviousBlock() {
cy.getByTestId(this.previousBlockBtn).click();
}
clickNextBlock() {
cy.getByTestId(this.nextBlockBtn).click();
}
jumpToBlock(blockNumber: string) {
cy.getByTestId(this.jumpToBlockInput).type(blockNumber);
cy.getByTestId(this.jumpToBlockSubmit).click();
}
verifyPreviousBtnDisabled() {
cy.getByTestId(this.previousBlockBtn).find('button').should('be.disabled');
}
}

View File

@ -1,9 +0,0 @@
import BasePage from './base-page';
export default class GenesisPage extends BasePage {
genesisHeader = 'genesis-header';
genesisFieldsDisplayed() {
this.validateBlockDataDisplayed(this.genesisHeader);
}
}

View File

@ -1,95 +0,0 @@
import BasePage from './base-page';
export default class HomePage extends BasePage {
statsEnvironmentTitle = 'stats-environment';
statsTitle = 'stats-title';
statsValue = 'stats-value';
verifyStatsEnvironment() {
const deployedEnv = Cypress.env('environment').toUpperCase();
cy.getByTestId(this.statsEnvironmentTitle).should(
'have.text',
`/ ${deployedEnv}`
);
}
verifyStatsTitlesDisplayed() {
const statTitles = [
'Status',
'Height',
'Validating nodes',
'Uptime',
'Total nodes',
'Inactive nodes',
'Total staked',
'Backlog',
'Trades / second',
'Orders / block',
'Orders / second',
'Transactions / block',
'Block time',
'Time',
'App',
'Tendermint',
'Up since',
'Chain ID',
];
cy.getByTestId(this.statsTitle).then(($list) => {
cy.wrap($list).should('have.length', 18);
});
cy.getByTestId(this.statsTitle)
.each(($title, index) => {
cy.wrap($title).should('have.text', statTitles[index]);
})
.then(($list) => {
cy.wrap($list).should('have.length', 18);
});
}
verifyStatsValuesDisplayed() {
cy.getByTestId(this.statsValue).eq(0).should('have.text', 'CONNECTED');
cy.getByTestId(this.statsValue).eq(1).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(2).should('have.text', '2');
cy.getByTestId(this.statsValue)
.eq(3)
.invoke('text')
.should('match', /\d+d \d+h \d+m \d+s/i);
cy.getByTestId(this.statsValue).eq(4).should('have.text', '2');
cy.getByTestId(this.statsValue).eq(5).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(6).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(7).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(8).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(9).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(10).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(11).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(12).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(13).should('not.be.empty');
if (Cypress.env('NIGHTLY_RUN') != true) {
cy.getByTestId(this.statsValue)
.eq(14)
.invoke('text')
.should('match', /v\d+\.\d+\.\d+/i);
}
cy.getByTestId(this.statsValue)
.eq(15)
.invoke('text')
.should('match', /\d+\.\d+\.\d+/i);
cy.getByTestId(this.statsValue).eq(16).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(17).should('not.be.empty');
}
verifyStatsBlockHeightUpdating() {
cy.getByTestId(this.statsValue)
.eq(1)
.invoke('text')
.then((blockHeightTxt) => {
cy.getByTestId(this.statsValue)
.eq(1)
.invoke('text')
.should((newBlockHeightTxt) => {
expect(blockHeightTxt).not.to.eq(newBlockHeightTxt);
});
});
}
}

View File

@ -1,9 +0,0 @@
import BasePage from './base-page';
export default class MarketsPage extends BasePage {
marketHeaders = 'markets-header';
validateMarketDataDisplayed() {
this.validateBlockDataDisplayed(this.marketHeaders);
}
}

View File

@ -1,39 +0,0 @@
import BasePage from './base-page';
export default class NetworkParametersPage extends BasePage {
networkParametersHeader = 'network-param-header';
parameters = 'parameters';
jsonParamNameClassName = '.hljs-attr';
jsonParamValueStringClassName = '.hljs-string';
jsonParamValueNumberClassName = '.hljs-number';
parameterKeyValueRow = 'key-value-table-row';
verifyNetworkParametersDisplayed() {
cy.getByTestId(this.networkParametersHeader).should(
'have.text',
'Network Parameters'
);
cy.get(this.jsonParamNameClassName)
.should('have.length.at.least', 18)
.each(($paramName) => {
cy.wrap($paramName).should('not.be.empty');
});
cy.get(this.jsonParamValueStringClassName)
.should('have.length.at.least', 6)
.each(($paramValue) => {
cy.wrap($paramValue).should('not.be.empty');
});
cy.get(this.jsonParamValueNumberClassName)
.should('have.length.at.least', 7)
.each(($paramValue) => {
cy.wrap($paramValue).should('not.be.empty');
});
cy.getByTestId(this.parameterKeyValueRow).each(($row) => {
cy.wrap($row).find('dt').should('not.be.empty');
cy.wrap($row).find('dd').should('not.be.empty');
});
}
}

View File

@ -1,85 +0,0 @@
import BasePage from './base-page';
export default class TransactionsPage extends BasePage {
transactionsList = 'transactions-list';
transactionRow = 'transaction-row';
blockHeight = 'block-height';
numberOfTransactions = 'num-txs';
validatorLink = 'validator-link';
blockTime = 'block-time';
refreshBtn = 'refresh';
txHash = 'hash';
txSubmittedBy = 'submitted-by';
txBlock = 'block';
txEncodedTnx = 'encoded-tnx';
txType = 'tx-type';
validateTransactionsPagedisplayed() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000); // Wait for transactions to load if there are any
cy.getByTestId(this.transactionRow).should('have.length.above', 1);
}
validateRefreshBtn() {
cy.intercept('GET', '**/blockchain').as('get-blockchain');
cy.getByTestId(this.refreshBtn).click();
cy.wait('@get-blockchain').its('response.statusCode').should('eq', 200);
}
validateTxDetailsAreDisplayed() {
// TODO fail test when there are no txs once running with Capsule
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.txHash}]`).length) {
cy.getByTestId(this.txHash).invoke('text').should('have.length', 64);
cy.getByTestId(this.txSubmittedBy)
.find('a')
.then(($address) => {
cy.wrap($address).should('have.attr', 'href');
cy.wrap($address).invoke('text').should('have.length', 66);
});
cy.getByTestId(this.txBlock).should('not.be.empty');
cy.getByTestId(this.txEncodedTnx).should('not.be.empty');
cy.getByTestId(this.txType)
.should('not.be.empty')
.invoke('text')
.then((txTypeTxt) => {
if (txTypeTxt == 'Order Submission') {
cy.get('.hljs-attr')
.should('have.length.at.least', 8)
.each(($propertyName) => {
cy.wrap($propertyName).should('not.be.empty');
});
cy.get('.hljs-string')
.should('have.length.at.least', 8)
.each(($propertyValue) => {
cy.wrap($propertyValue).should('not.be.empty');
});
}
});
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
clickOnTopTransaction() {
// TODO fail test when there are no txs once running with Capsule
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.transactionRow}]`).length) {
cy.getByTestId(this.transactionRow)
.first()
.find('a')
.first()
.click({ force: true });
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
}

View File

@ -1,18 +0,0 @@
import BasePage from './base-page';
export default class ValidatorPage extends BasePage {
tendermintHeader = 'tendermint-header';
vegaHeader = 'vega-header';
tendermintData = 'tendermint-data';
vegaData = 'vega-data';
validateValidatorsDisplayed() {
cy.getByTestId(this.tendermintHeader).should(
'have.text',
'Tendermint data'
);
cy.getByTestId(this.tendermintData).should('not.be.empty');
cy.getByTestId(this.vegaHeader).should('have.text', 'Vega data');
cy.getByTestId(this.vegaData).should('not.be.empty');
}
}

View File

@ -1,12 +0,0 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import AssetsPage from '../pages/assets-page';
const assetPage = new AssetsPage();
When('I navigate to the asset page', () => {
assetPage.navigateToAssets();
});
Then('asset page is correctly displayed', () => {
assetPage.validateAssetsDisplayed();
});

View File

@ -1,50 +0,0 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import BlocksPage from '../pages/blocks-page';
const blocksPage = new BlocksPage();
When('I navigate to the blocks page', () => {
blocksPage.navigateToBlocks();
});
When('I click on top block', () => {
blocksPage.clickOnTopBlockHeight();
});
When('jump to first block', () => {
blocksPage.jumpToBlock('1');
});
Then('blocks page is correctly displayed', () => {
blocksPage.validateBlocksPageDisplayed();
});
Then('validator block page is correctly displayed', () => {
blocksPage.validateBlockValidatorPage();
});
Then('I am on the previous block when I click previous', () => {
blocksPage.navigateToPreviousBlock();
});
Then('previous button is disabled', () => {
blocksPage.verifyPreviousBtnDisabled();
});
Then('I am on the second block when I click next', () => {
blocksPage.navigateToNextBlock();
});
Then('I scroll down to the last block on the page', () => {
blocksPage.navigateToLastBlockOnPage();
});
Then(
'I can expect to see at least {int} blocks if i scroll {int} times',
(expectedBlocks, scrollAttempts) => {
blocksPage.navigateToOlderBlocksWithInfiniteScroll(
expectedBlocks,
scrollAttempts
);
}
);

View File

@ -1,26 +0,0 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
import BasePage from '../pages/base-page';
const basePage = new BasePage();
Given('I am on mobile and open the toggle menu', () => {
cy.viewport('iphone-x');
cy.visit('/');
basePage.clickOnToggle();
});
Given('I am on the homepage', () => {
cy.visit('/');
});
When('I search for {string}', (searchText) => {
basePage.search(searchText);
basePage.clickSearch();
});
Then('I am redirected to page containing id {string}', (expectedUrl) => {
basePage.validateUrl(expectedUrl);
});
Then('search error message {string} is displayed', (expectedErrorMsg) => {
basePage.validateSearchErrorDisplayed(expectedErrorMsg);
});

View File

@ -1,11 +0,0 @@
import { Then } from 'cypress-cucumber-preprocessor/steps';
import HomePage from '../pages/home-page';
const homePage = new HomePage();
Then('the stats for deployed environment are correctly displayed', () => {
homePage.verifyStatsEnvironment();
homePage.verifyStatsTitlesDisplayed();
homePage.verifyStatsValuesDisplayed();
homePage.verifyStatsBlockHeightUpdating();
});

View File

@ -1,12 +0,0 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import MarketsPage from '../pages/markets-page';
const marketsPage = new MarketsPage();
When('I navigate to the markets page', () => {
marketsPage.navigateToMarkets();
});
Then('markets page is correctly displayed', () => {
marketsPage.validateMarketDataDisplayed();
});

View File

@ -1,11 +0,0 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import NetworkPage from '../pages/network-page';
const networkPage = new NetworkPage();
When('I navigate to network parameters page', () => {
networkPage.navigateToNetworkParameters();
});
Then('network parameters page is correctly displayed', () => {
networkPage.verifyNetworkParametersDisplayed();
});

View File

@ -1,30 +0,0 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import TransactionsPage from '../pages/transactions-page';
const transactionsPage = new TransactionsPage();
When('I navigate to the transactions page', () => {
transactionsPage.navigateToTxs();
});
When('I navigate to the blocks page', () => {
transactionsPage.navigateToBlocks();
});
When('I click on the top transaction', () => {
transactionsPage.clickOnTopTransaction();
});
Then('transactions page is correctly displayed', () => {
transactionsPage.validateTransactionsPagedisplayed();
transactionsPage.validateRefreshBtn();
});
Then('blocks page is correctly displayed', () => {
transactionsPage.validateTransactionsPagedisplayed();
transactionsPage.validateRefreshBtn();
});
Then('transaction details are displayed', () => {
transactionsPage.validateTxDetailsAreDisplayed();
});

View File

@ -1,12 +0,0 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import ValidatorPage from '../pages/validators-page';
const validatorsPage = new ValidatorPage();
When('I navigate to the validators page', () => {
validatorsPage.navigateToValidators();
});
Then('validators page is correctly displayed', () => {
validatorsPage.validateValidatorsDisplayed();
});

View File

@ -0,0 +1,87 @@
describe('market selector', () => {
let markets;
before(() => {
cy.intercept('POST', '/query', (req) => {
const { body } = req;
if (body.operationName === 'SimpleMarkets') {
req.alias = `gqlSimpleMarketsQuery`;
}
});
cy.visit('/markets');
cy.wait('@gqlSimpleMarketsQuery').then((response) => {
if (response.response?.body?.data?.markets?.length) {
markets = response.response?.body?.data?.markets;
}
});
});
it('should be properly rendered', () => {
if (markets) {
cy.visit(`/trading/${markets[0].id}`);
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].name
);
cy.getByTestId('arrow-button').click();
cy.getByTestId('market-pane').should('be.visible');
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('contain.text', markets[0].name);
cy.getByTestId('market-pane').children().find('[role="button"]').click();
cy.getByTestId('market-pane').should('not.be.visible');
}
});
it('typing should change list', () => {
if (markets) {
cy.visit(`/trading/${markets[0].id}`);
cy.get('input[placeholder="Search"]').type('{backspace}');
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('have.length', 1);
cy.get('input[placeholder="Search"]').clear();
cy.get('input[placeholder="Search"]').type('a');
const filtered = markets.filter((market) => market.name.match(/a/i));
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('have.length', filtered.length);
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.last()
.click();
cy.location('pathname').should(
'eq',
`/trading/${filtered[filtered.length - 1].id}`
);
cy.get('input[placeholder="Search"]').should(
'have.value',
filtered[filtered.length - 1].name
);
}
});
it('mobile view', () => {
if (markets) {
cy.viewport('iphone-xr');
cy.visit(`/trading/${markets[0].id}`);
cy.get('[role="dialog"]').should('not.exist');
cy.getByTestId('arrow-button').click();
cy.get('[role="dialog"]').should('be.visible');
cy.get('input[placeholder="Search"]').clear();
cy.getByTestId('market-pane')
.children()
.find('[role="button"]')
.should('have.length', markets.length);
cy.pause();
cy.getByTestId('dialog-close').click();
cy.get('input[placeholder="Search"]').should(
'have.value',
markets[0].name
);
}
});
});

View File

@ -2,30 +2,45 @@ import * as React from 'react';
import { useForm, Controller } from 'react-hook-form';
import Box from '@mui/material/Box';
import { Stepper } from '../stepper';
import type { DealTicketQuery_market, Order } from '@vegaprotocol/deal-ticket';
import type { DealTicketQuery_market } from '@vegaprotocol/deal-ticket';
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
import {
ExpirySelector,
SideSelector,
TimeInForceSelector,
TypeSelector,
getDefaultOrder,
useOrderValidation,
useOrderSubmit,
DealTicketAmount,
MarketSelector,
} from '@vegaprotocol/deal-ticket';
import type { Order } from '@vegaprotocol/orders';
import {
OrderTimeInForce,
OrderType,
VegaWalletOrderTimeInForce as OrderTimeInForce,
VegaWalletOrderType as OrderType,
VegaTxStatus,
} from '@vegaprotocol/wallet';
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
import {
getDefaultOrder,
useOrderValidation,
useOrderSubmit,
} from '@vegaprotocol/orders';
import { useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import MarketNameRenderer from '../simple-market-list/simple-market-renderer';
interface DealTicketMarketProps {
market: DealTicketQuery_market;
}
export const DealTicketSteps = ({ market }: DealTicketMarketProps) => {
const navigate = useNavigate();
const setMarket = useCallback(
(marketId) => {
navigate(`/trading/${marketId}`);
},
[navigate]
);
const {
register,
control,
@ -70,7 +85,13 @@ export const DealTicketSteps = ({ market }: DealTicketMarketProps) => {
{
label: 'Select Asset',
description: `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.`,
component: <h1 className="font-bold mb-16">{market.name}</h1>,
component: (
<MarketSelector
market={market}
setMarket={setMarket}
ItemRenderer={MarketNameRenderer}
/>
),
},
{
label: 'Select Order Type',

View File

@ -1,23 +1,23 @@
import React from 'react';
import type { SimpleMarkets_markets } from './__generated__/SimpleMarkets';
import type { MarketNames_markets } from '@vegaprotocol/deal-ticket';
import SimpleMarketExpires from './simple-market-expires';
interface Props {
data: SimpleMarkets_markets;
market: MarketNames_markets;
}
const MarketNameRenderer = ({ data }: Props) => {
const MarketNameRenderer = ({ market }: Props) => {
return (
<div className="flex h-full items-center grid grid-rows-2 grid-flow-col gap-x-8 gap-y-0 grid-cols-[min-content,1fr,1fr]">
<div className="w-60 row-span-2 bg-pink rounded-full w-44 h-44 bg-gradient-to-br from-white-60 to--white-80 opacity-30" />
<div className="col-span-2 uppercase justify-start text-black dark:text-white text-market self-end">
{data.name}{' '}
{market.name}{' '}
<SimpleMarketExpires
tags={data.tradableInstrument.instrument.metadata.tags}
tags={market.tradableInstrument.instrument.metadata.tags}
/>
</div>
<div className="col-span-2 ui-small text-deemphasise dark:text-midGrey self-start leading-3">
{data.tradableInstrument.instrument.product.quoteName}
<div className="col-span-2 text-ui-small text-deemphasise dark:text-midGrey self-start leading-3">
{market.tradableInstrument.instrument.product.quoteName}
</div>
</div>
);

View File

@ -21,7 +21,7 @@ const useColumnDefinitions = ({ onClick }: Props) => {
minWidth: 300,
field: 'name',
cellRenderer: ({ data }: { data: SimpleMarketsType }) => (
<MarketNameRenderer data={data} />
<MarketNameRenderer market={data} />
),
},
{

View File

@ -2,6 +2,7 @@ const { join } = require('path');
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
const theme = require('../../libs/tailwindcss-config/src/theme-lite');
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
const vegaCustomClassesLite = require('../../libs/tailwindcss-config/src/vega-custom-classes-lite');
module.exports = {
content: [
@ -11,5 +12,5 @@ module.exports = {
],
darkMode: 'class',
theme,
plugins: [vegaCustomClasses],
plugins: [vegaCustomClasses, vegaCustomClassesLite],
};

File diff suppressed because it is too large Load Diff

View File

@ -38,7 +38,7 @@
"tranche_end": "2022-11-26T13:48:10.000Z",
"total_added": "100",
"total_removed": "0",
"locked_amount": "38.375643708777274",
"locked_amount": "37.074508498224257",
"deposits": [
{
"amount": "100",
@ -242,7 +242,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1100",
"total_removed": "673.04388635",
"locked_amount": "284.894035388127847",
"locked_amount": "270.58154807204466",
"deposits": [
{
"amount": "1000",

View File

@ -69,7 +69,7 @@
"tranche_end": "2022-10-12T00:53:20.000Z",
"total_added": "1010.000000000000000001",
"total_removed": "668.4622323651",
"locked_amount": "261.58452340182647770025899457762557077",
"locked_amount": "248.4430577752410060002459832255200406",
"deposits": [
{
"amount": "1000",

View File

@ -18,6 +18,7 @@ module.exports = defineConfig({
env: {
ethProviderUrl: 'http://localhost:8545/',
ethWalletPublicKey: '0xEe7D375bcB50C26d52E1A4a472D8822A2A22d94F',
ethWalletPublicKeyTruncated: '0xEe7D…d94F',
ethStakingBridgeContractAddress:
'0x9135f5afd6F055e731bca2348429482eE614CFfA',
vegaWalletName: 'capsule_wallet',

View File

@ -1,31 +1,28 @@
/// <reference types="cypress" />
import navigation from '../../locators/navigation.locators';
import staking from '../../locators/staking.locators';
import wallet from '../../locators/wallet.locators';
import '../../support/staking.functions';
import '../../support/vega-wallet.functions';
import '../../support/eth-wallet.functions';
import '../../support/wallet-teardown.functions';
const pageSpinner = 'splash-loader';
const menuBar = 'nav';
const validatorList = '[data-testid="node-list-item-name"]';
const removeStakeRadioButton = '[data-testid="remove-stake-radio"]';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const stakeNextEpochValue = '[data-testid="stake-next-epoch"]';
const vegaWalletContainer = '[data-testid="vega-wallet"]';
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
context('Staking Tab - with eth and vega wallets connected', function () {
context('Staking Flow - with eth and vega wallets connected', function () {
before('visit staking tab and connect vega wallet', function () {
cy.vega_wallet_import();
cy.visit('/');
cy.get(navigation.section, { timeout: 20000 }).should('be.visible');
cy.get(menuBar, { timeout: 20000 }).should('be.visible');
cy.vega_wallet_connect();
cy.vega_wallet_set_specified_approval_amount('1000');
cy.reload();
cy.get(navigation.section, { timeout: 20000 }).should('be.visible');
cy.get(menuBar, { timeout: 20000 }).should('be.visible');
cy.ethereum_wallet_connect();
cy.get(navigation.staking).first().click();
cy.get(navigation.spinner, { timeout: 20000 }).should('not.exist');
cy.get(staking.validatorNames).first().invoke('text').as('validatorName');
cy.get(staking.validatorNames)
.last()
.invoke('text')
.as('otherValidatorName');
cy.navigate_to('staking');
cy.get(pageSpinner, { timeout: 20000 }).should('not.exist');
cy.get(validatorList).first().invoke('text').as('validatorName');
cy.get(validatorList).last().invoke('text').as('otherValidatorName');
});
describe('Eth wallet - contains VEGA tokens', function () {
@ -33,7 +30,7 @@ context('Staking Tab - with eth and vega wallets connected', function () {
'teardown wallet & drill into a specific validator',
function () {
cy.vega_wallet_teardown();
cy.get(navigation.staking).first().click();
cy.navigate_to('staking');
}
);
@ -47,37 +44,37 @@ context('Staking Tab - with eth and vega wallets connected', function () {
);
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('2');
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('1.000000000000000000');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.staking_validator_page_check_stake_next_epoch_value('2.0');
cy.staking_validator_page_check_stake_this_epoch_value('2.0');
staking_validator_page_check_stake_next_epoch_value('2.0');
staking_validator_page_check_stake_this_epoch_value('2.0');
});
it('Able to stake against mulitple validators', function () {
cy.staking_page_associate_tokens('5');
cy.vega_wallet_check_unstaked_value_is('5.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('2');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.get(navigation.staking).first().click();
cy.get(staking.validatorNames).contains(this.otherValidatorName).click();
cy.navigate_to('staking');
cy.get(validatorList).contains(this.otherValidatorName).click();
cy.staking_validator_page_add_stake('1');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.otherValidatorName,
'1.000000000000000000'
);
@ -89,37 +86,37 @@ context('Staking Tab - with eth and vega wallets connected', function () {
cy.vega_wallet_check_unstaked_value_is('4.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('3');
cy.staking_validator_page_check_stake_next_epoch_value('3.0');
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
staking_validator_page_check_stake_next_epoch_value('3.0');
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'3.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('1.000000000000000000');
cy.get(navigation.staking).first().click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.navigate_to('staking');
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_removeStake('1');
cy.staking_validator_page_check_stake_next_epoch_value('2.0');
cy.staking_validator_page_check_stake_this_epoch_value('3.0');
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
staking_validator_page_check_stake_next_epoch_value('2.0');
staking_validator_page_check_stake_this_epoch_value('3.0');
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.vega_wallet_check_validator_stake_this_epoch_value_is(
vega_wallet_check_validator_stake_this_epoch_value_is(
this.validatorName,
'3.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('2.000000000000000000');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.staking_validator_page_check_stake_next_epoch_value('2.0');
cy.staking_validator_page_check_stake_this_epoch_value('2.0');
staking_validator_page_check_stake_next_epoch_value('2.0');
staking_validator_page_check_stake_this_epoch_value('2.0');
});
it('Able to remove a full stake against a validator', function () {
@ -127,33 +124,33 @@ context('Staking Tab - with eth and vega wallets connected', function () {
cy.vega_wallet_check_unstaked_value_is('3.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('1');
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'1.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('2.000000000000000000');
cy.get(navigation.staking).first().click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.navigate_to('staking');
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_removeStake('1');
cy.staking_validator_page_check_stake_next_epoch_value('0.0');
staking_validator_page_check_stake_next_epoch_value('0.0');
cy.vega_wallet_check_validator_stake_this_epoch_value_is(
vega_wallet_check_validator_stake_this_epoch_value_is(
this.validatorName,
'1.000000000000000000'
);
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'0.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('3.000000000000000000');
cy.staking_validator_page_check_stake_next_epoch_value('0.0');
cy.staking_validator_page_check_stake_this_epoch_value('0.0');
cy.vega_wallet_check_validator_no_longer_showing(this.validatorName);
staking_validator_page_check_stake_next_epoch_value('0.0');
staking_validator_page_check_stake_this_epoch_value('0.0');
vega_wallet_check_validator_no_longer_showing(this.validatorName);
});
it.skip('Unable to remove a stake with a negative value for a validator', function () {
@ -161,22 +158,22 @@ context('Staking Tab - with eth and vega wallets connected', function () {
cy.vega_wallet_check_unstaked_value_is('3.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('2');
cy.staking_validator_page_check_stake_next_epoch_value('2.0');
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
staking_validator_page_check_stake_next_epoch_value('2.0');
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('1.000000000000000000');
cy.get(navigation.staking).first().click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(staking.removeStakeRadioButton).click({ force: true });
cy.get(staking.tokenAmountInput).type('-0.1');
cy.navigate_to('staking');
cy.get(validatorList).contains(this.validatorName).click();
cy.get(removeStakeRadioButton).click({ force: true });
cy.get(tokenAmountInputBox).type('-0.1');
cy.contains('Waiting for next epoch to start', { timeout: 10000 });
cy.get(staking.tokenInputSubmit)
cy.get(tokenSubmitButton)
.should('be.disabled', { timeout: 8000 })
.and('contain', `Remove -0.1 $VEGA tokens at the end of epoch`)
.and('be.visible');
@ -187,22 +184,22 @@ context('Staking Tab - with eth and vega wallets connected', function () {
cy.vega_wallet_check_unstaked_value_is('3.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('2');
cy.staking_validator_page_check_stake_next_epoch_value('2.0');
cy.vega_wallet_check_validator_stake_next_epoch_value_is(
staking_validator_page_check_stake_next_epoch_value('2.0');
vega_wallet_check_validator_stake_next_epoch_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.vega_wallet_check_unstaked_value_is('1.000000000000000000');
cy.get(navigation.staking).first().click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(staking.removeStakeRadioButton).click({ force: true });
cy.get(staking.tokenAmountInput).type(4);
cy.navigate_to('staking');
cy.get(validatorList).contains(this.validatorName).click();
cy.get(removeStakeRadioButton).click({ force: true });
cy.get(tokenAmountInputBox).type(4);
cy.contains('Waiting for next epoch to start', { timeout: 10000 });
cy.get(staking.tokenInputSubmit)
cy.get(tokenSubmitButton)
.should('be.disabled', { timeout: 8000 })
.and('contain', `Remove 4 $VEGA tokens at the end of epoch`)
.and('be.visible');
@ -213,22 +210,22 @@ context('Staking Tab - with eth and vega wallets connected', function () {
cy.vega_wallet_check_unstaked_value_is('3.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('2');
cy.vega_wallet_check_unstaked_value_is('1.000000000000000000');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.get(navigation.staking).first().click();
cy.navigate_to('staking');
cy.staking_page_disassociate_all_tokens();
cy.ethereum_wallet_check_associated_vega_key_is_no_longer_showing(
vegaWalletPublicKeyShort
);
cy.ethereum_wallet_check_associated_value_is('0.0');
cy.vega_wallet_check_associated_value_is('0.000000000000000000');
cy.vega_wallet_check_validator_no_longer_showing(this.validatorName);
vega_wallet_check_validator_no_longer_showing(this.validatorName);
});
it('Disassociating some tokens - prioritizes unstaked tokens', function () {
@ -236,19 +233,19 @@ context('Staking Tab - with eth and vega wallets connected', function () {
cy.vega_wallet_check_unstaked_value_is('3.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.get(staking.validatorNames).contains(this.validatorName).click();
cy.get(validatorList).contains(this.validatorName).click();
cy.staking_validator_page_add_stake('2');
cy.vega_wallet_check_unstaked_value_is('1.000000000000000000');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.validatorName,
'2.000000000000000000'
);
cy.get(navigation.staking).first().click();
cy.navigate_to('staking');
cy.staking_page_disassociate_tokens('1');
cy.ethereum_wallet_check_associated_value_is('2.0');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.vega_wallet_check_validator_staked_value_is(
vega_wallet_check_validator_staked_value_is(
this.validatorName,
'2.000000000000000000'
);
@ -261,78 +258,78 @@ context('Staking Tab - with eth and vega wallets connected', function () {
}
);
});
cy.staking_validator_page_check_stake_next_epoch_value = (expectedVal) => {
cy.highlight(
`Checking Staking Page - Validator Stake Next Epoch Value is ${expectedVal}`
);
cy.get(staking.stakeNextEpochValue, { timeout: 10000 })
.contains(expectedVal, { timeout: 10000 })
.should('be.visible');
};
cy.staking_validator_page_check_stake_this_epoch_value = (expectedVal) => {
cy.highlight(
`Checking Staking Page - Validator Stake This Epoch Value is ${expectedVal}`
);
cy.get(staking.stakeThisEpochValue, { timeout: 10000 })
.contains(expectedVal, { timeout: 10000 })
.should('be.visible');
};
cy.vega_wallet_check_validator_stake_next_epoch_value_is = (
validatorName,
expectedVal
) => {
cy.highlight(
`Checking vega wallet - Stake Next Epoch Value for ${validatorName} is ${expectedVal}`
);
cy.get(wallet.vegawallet).within(() => {
cy.contains(`${validatorName} (Next epoch)`, { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
cy.vega_wallet_check_validator_stake_this_epoch_value_is = (
validatorName,
expectedVal
) => {
cy.highlight(
`Checking vega wallet - Stake This Epoch Value for ${validatorName} is ${expectedVal}`
);
cy.get(wallet.vegawallet).within(() => {
cy.contains(`${validatorName} (This Epoch)`, { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
cy.vega_wallet_check_validator_no_longer_showing = (validatorName) => {
cy.highlight(
`Checking Validator and therefore stake removed for ${validatorName}`
);
cy.get(wallet.vegawallet).within(() => {
cy.contains(`${validatorName}`, { timeout: 40000 }).should('not.exist', {
timeout: 40000,
});
});
};
cy.vega_wallet_check_validator_staked_value_is = (
validatorName,
expectedVal
) => {
cy.highlight(
`Checking Validator Stake Value for ${validatorName} is ${expectedVal}`
);
cy.get(wallet.vegawallet).within(() => {
cy.contains(`${validatorName}`, { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
});
function staking_validator_page_check_stake_next_epoch_value(expectedVal) {
cy.highlight(
`Checking Staking Page - Validator Stake Next Epoch Value is ${expectedVal}`
);
cy.get(stakeNextEpochValue, { timeout: 10000 })
.contains(expectedVal, { timeout: 10000 })
.should('be.visible');
}
function staking_validator_page_check_stake_this_epoch_value(expectedVal) {
cy.highlight(
`Checking Staking Page - Validator Stake This Epoch Value is ${expectedVal}`
);
cy.get(stakeNextEpochValue, { timeout: 10000 })
.contains(expectedVal, { timeout: 10000 })
.should('be.visible');
}
function vega_wallet_check_validator_stake_next_epoch_value_is(
validatorName,
expectedVal
) {
cy.highlight(
`Checking vega wallet - Stake Next Epoch Value for ${validatorName} is ${expectedVal}`
);
cy.get(vegaWalletContainer).within(() => {
cy.contains(`${validatorName} (Next epoch)`, { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
}
function vega_wallet_check_validator_stake_this_epoch_value_is(
validatorName,
expectedVal
) {
cy.highlight(
`Checking vega wallet - Stake This Epoch Value for ${validatorName} is ${expectedVal}`
);
cy.get(vegaWalletContainer).within(() => {
cy.contains(`${validatorName} (This Epoch)`, { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
}
function vega_wallet_check_validator_no_longer_showing(validatorName) {
cy.highlight(
`Checking Validator and therefore stake removed for ${validatorName}`
);
cy.get(vegaWalletContainer).within(() => {
cy.contains(`${validatorName}`, { timeout: 40000 }).should('not.exist', {
timeout: 40000,
});
});
}
function vega_wallet_check_validator_staked_value_is(
validatorName,
expectedVal
) {
cy.highlight(
`Checking Validator Stake Value for ${validatorName} is ${expectedVal}`
);
cy.get(vegaWalletContainer).within(() => {
cy.contains(`${validatorName}`, { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
}

View File

@ -1,118 +1,98 @@
/// <reference types="cypress" />
import navigation from '../../locators/navigation.locators';
import staking from '../../locators/staking.locators';
import wallet from '../../locators/wallet.locators';
import '../../support/staking.functions';
import '../../support/vega-wallet.functions';
import '../../support/eth-wallet.functions';
import '../../support/wallet-teardown.functions';
const pageSpinner = 'splash-loader';
const menuBar = 'nav';
const validatorList = '[data-testid="node-list-item-name"]';
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
context('Staking Tab - with eth and vega wallets connected', function () {
before('visit staking tab and connect vega wallet', function () {
cy.vega_wallet_import();
cy.visit('/');
cy.get(navigation.section, { timeout: 20000 }).should('be.visible');
cy.vega_wallet_connect();
cy.vega_wallet_set_specified_approval_amount('1000');
cy.reload();
cy.get(navigation.section, { timeout: 20000 }).should('be.visible');
cy.ethereum_wallet_connect();
cy.get(navigation.staking).first().click();
cy.get(navigation.spinner, { timeout: 20000 }).should('not.exist');
cy.get(staking.validatorNames).first().invoke('text').as('validatorName');
});
describe('Eth wallet - contains VEGA tokens', function () {
beforeEach(
'teardown wallet & drill into a specific validator',
function () {
cy.vega_wallet_teardown();
cy.get(navigation.staking).first().click();
cy.get(navigation.spinner, { timeout: 20000 }).should('not.exist');
}
);
it('Able to associate tokens - from staking page', function () {
cy.staking_page_associate_tokens('2');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'2.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('2.0');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.vega_wallet_check_unstaked_value_is('2.000000000000000000');
context(
'Token association flow - with eth and vega wallets connected',
function () {
before('visit staking tab and connect vega wallet', function () {
cy.vega_wallet_import();
cy.visit('/');
cy.get(menuBar, { timeout: 20000 }).should('be.visible');
cy.vega_wallet_connect();
cy.vega_wallet_set_specified_approval_amount('1000');
cy.reload();
cy.get(menuBar, { timeout: 20000 }).should('be.visible');
cy.ethereum_wallet_connect();
cy.navigate_to('staking');
cy.get(pageSpinner, { timeout: 20000 }).should('not.exist');
cy.get(validatorList).first().invoke('text').as('validatorName');
});
it('Able to disassociate tokens - from staking page', function () {
cy.staking_page_associate_tokens('2');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'2.000000000000000000'
describe('Eth wallet - contains VEGA tokens', function () {
beforeEach(
'teardown wallet & drill into a specific validator',
function () {
cy.vega_wallet_teardown();
cy.navigate_to('staking');
cy.get(pageSpinner, { timeout: 20000 }).should('not.exist');
}
);
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_tokens('1');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'1.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('1.0');
cy.vega_wallet_check_associated_value_is('1.000000000000000000');
});
it('Able to access associate token form - from eth wallet', function () {
cy.get(wallet.ethWallet).within(() =>
cy.get(wallet.ethWalletAssociate).click()
);
cy.get(staking.stakeAssociateWalletRadio, { timeout: 30000 }).should(
'be.enabled'
);
});
it('Able to associate tokens', function () {
cy.staking_page_associate_tokens('2');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'2.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('2.0');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.vega_wallet_check_unstaked_value_is('2.000000000000000000');
});
it('Able to access disassociate token form - from eth wallet', function () {
cy.get(wallet.ethWallet).within(() =>
cy.get(wallet.ethWalletDisassociate).click()
);
cy.get(staking.stakeAssociateWalletRadio, { timeout: 30000 }).should(
'be.enabled'
);
});
it('Able to disassociate tokens', function () {
cy.staking_page_associate_tokens('2');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'2.000000000000000000'
);
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_tokens('1');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'1.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('1.0');
cy.vega_wallet_check_associated_value_is('1.000000000000000000');
});
it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () {
cy.staking_page_associate_tokens('1001', true);
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'1,001.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('1,001.00');
cy.vega_wallet_check_associated_value_is('1,001.000000000000000000');
});
it('Able to associate more tokens than the approved amount of 1000 - requires re-approval', function () {
cy.staking_page_associate_tokens('1001', true);
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'1,001.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('1,001.00');
cy.vega_wallet_check_associated_value_is('1,001.000000000000000000');
});
it('Able to disassociate a partial amount of tokens currently associated', function () {
cy.staking_page_associate_tokens('2');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_tokens('1');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'1.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('1.0');
cy.vega_wallet_check_associated_value_is('1.000000000000000000');
});
it('Able to disassociate a partial amount of tokens currently associated', function () {
cy.staking_page_associate_tokens('2');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_tokens('1');
cy.ethereum_wallet_check_associated_vega_key_value_is(
vegaWalletPublicKeyShort,
'1.000000000000000000'
);
cy.ethereum_wallet_check_associated_value_is('1.0');
cy.vega_wallet_check_associated_value_is('1.000000000000000000');
});
it('Able to disassociate all tokens', function () {
cy.staking_page_associate_tokens('2');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_all_tokens();
cy.ethereum_wallet_check_associated_vega_key_is_no_longer_showing(
vegaWalletPublicKeyShort
);
cy.ethereum_wallet_check_associated_value_is('0.0');
cy.vega_wallet_check_associated_value_is('0.000000000000000000');
it('Able to disassociate all tokens', function () {
cy.staking_page_associate_tokens('2');
cy.vega_wallet_check_associated_value_is('2.000000000000000000');
cy.get('button').contains('Select a validator to nominate').click();
cy.staking_page_disassociate_all_tokens();
cy.ethereum_wallet_check_associated_vega_key_is_no_longer_showing(
vegaWalletPublicKeyShort
);
cy.ethereum_wallet_check_associated_value_is('0.0');
cy.vega_wallet_check_associated_value_is('0.000000000000000000');
});
});
});
});
}
);

View File

@ -1,30 +1,21 @@
import navigation from '../locators/navigation.locators';
import governance from '../locators/governance.locators';
const noProposals = '[data-testid="no-proposals"]';
context('Governance Page - verify elements on page', function () {
before('navigate to governance page', function () {
cy.visit('/')
.get(navigation.section)
.within(() => {
cy.get(navigation.governance).click();
});
cy.visit('/').navigate_to('governance');
});
describe('with no network change proposals', function () {
it('should have governance tab highlighted', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.governance).should('have.attr', 'aria-current');
});
cy.verify_tab_highlighted('governance');
});
it('should have GOVERNANCE header visible', function () {
cy.get(governance.pageHeader)
.should('be.visible')
.and('have.text', 'Governance');
cy.verify_page_header('Governance');
});
it('should have information box visible', function () {
cy.get(governance.noProposals)
cy.get(noProposals)
.should('be.visible')
.and('have.text', 'There are no active network change proposals');
});

View File

@ -1,5 +1,24 @@
import navigation from '../locators/navigation.locators';
import home from '../locators/home.locators';
const navSection = 'nav';
const navHome = '[href="/"]';
const navVesting = '[href="/vesting"]';
const navStaking = '[href="/staking"]';
const navRewards = '[href="/rewards"]';
const navWithdraw = '[href="/withdraw"]';
const navGovernance = '[href="/governance"]';
const tokenDetailsTable = '.token-details';
const address = '[data-testid="token-address"]';
const contract = '[data-testid="token-contract"]';
const totalSupply = '[data-testid="total-supply"]';
const circulatingSupply = '[data-testid="circulating-supply"]';
const staked = '[data-testid="staked"]';
const tranchesLink = '[data-testid="tranches-link"]';
const redeemBtn = '[data-testid="check-vesting-page-btn"]';
const getVegaWalletLink = '[data-testid="get-vega-wallet-link"]';
const associateVegaLink =
'[data-testid="associate-vega-tokens-link-on-homepage"]';
const stakingBtn = '[data-testid="staking-button-on-homepage"]';
const governanceBtn = '[data-testid="governance-button-on-homepage"]';
const vegaTokenAddress = Cypress.env('vegaTokenAddress');
const vegaTokenContractAddress = Cypress.env('vegaTokenContractAddress');
@ -11,111 +30,111 @@ context('Home Page - verify elements on page', function () {
describe('with wallets disconnected', function () {
before('wait for page to load', function () {
cy.get(navigation.section, { timeout: 10000 }).should('be.visible');
cy.get(navSection, { timeout: 10000 }).should('be.visible');
});
describe('Navigation tabs', function () {
it('should have HOME tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.home).should('be.visible');
cy.get(navSection).within(() => {
cy.get(navHome).should('be.visible');
});
});
it('should have VESTING tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.vesting).should('be.visible');
cy.get(navSection).within(() => {
cy.get(navVesting).should('be.visible');
});
});
it('should have STAKING tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.staking).should('be.visible');
cy.get(navSection).within(() => {
cy.get(navStaking).should('be.visible');
});
});
it('should have REWARDS tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.rewards).should('be.visible');
cy.get(navSection).within(() => {
cy.get(navRewards).should('be.visible');
});
});
it('should have WITHDRAW tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.withdraw).should('be.visible');
cy.get(navSection).within(() => {
cy.get(navWithdraw).should('be.visible');
});
});
it('should have GOVERNANCE tab', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.governance).should('be.visible');
cy.get(navSection).within(() => {
cy.get(navGovernance).should('be.visible');
});
});
});
describe('THE $VEGA TOKEN table', function () {
it('should have TOKEN ADDRESS', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.address)
cy.get(tokenDetailsTable).within(() => {
cy.get(address)
.should('be.visible')
.invoke('text')
.should('be.equal', vegaTokenAddress);
});
});
it('should have VESTING CONTRACT', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.contract)
cy.get(tokenDetailsTable).within(() => {
cy.get(contract)
.should('be.visible')
.invoke('text')
.should('be.equal', vegaTokenContractAddress);
});
});
it('should have TOTAL SUPPLY', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.totalSupply).should('be.visible');
cy.get(tokenDetailsTable).within(() => {
cy.get(totalSupply).should('be.visible');
});
});
it('should have CIRCULATING SUPPLY', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.circulatingSupply).should('be.visible');
cy.get(tokenDetailsTable).within(() => {
cy.get(circulatingSupply).should('be.visible');
});
});
it('should have STAKED $VEGA', function () {
cy.get(home.tokenDetailsTable).within(() => {
cy.get(home.staked).should('be.visible');
cy.get(tokenDetailsTable).within(() => {
cy.get(staked).should('be.visible');
});
});
});
describe('links and buttons', function () {
it('should have TRANCHES link', function () {
cy.get(home.tranchesLink)
cy.get(tranchesLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/tranches');
});
it('should have REDEEM button', function () {
cy.get(home.redeemBtn)
cy.get(redeemBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/vesting');
});
it('should have GET VEGA WALLET link', function () {
cy.get(home.getVegaWalletLink)
cy.get(getVegaWalletLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', 'https://vega.xyz/wallet');
});
it('should have ASSOCIATE VEGA TOKENS link', function () {
cy.get(home.associateVegaLink)
cy.get(associateVegaLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/staking/associate');
});
it('should have STAKING button', function () {
cy.get(home.stakingBtn)
cy.get(stakingBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/staking');
});
it('should have GOVERNANCE button', function () {
cy.get(home.governanceBtn)
cy.get(governanceBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')

View File

@ -1,30 +1,22 @@
import navigation from '../locators/navigation.locators';
import rewards from '../locators/rewards.locators';
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
const warning = '[data-testid="callout"]';
context('Rewards Page - verify elements on page', function () {
before('navigate to rewards page', function () {
cy.visit('/')
.get(navigation.section)
.within(() => {
cy.get(navigation.rewards).click();
});
cy.visit('/').navigate_to('rewards');
});
describe('with wallets disconnected', function () {
it('should have REWARDS tab highlighted', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.rewards).should('have.attr', 'aria-current');
});
cy.verify_tab_highlighted('rewards');
});
it('should have rewards header visible', function () {
cy.get(rewards.pageHeader)
.should('be.visible')
.and('have.text', 'Rewards');
cy.verify_page_header('Rewards');
});
it('should have epoch warning', function () {
cy.get(rewards.warning)
cy.get(warning)
.should('be.visible')
.and(
'have.text',
@ -33,7 +25,7 @@ context('Rewards Page - verify elements on page', function () {
});
it('should have connect Vega wallet button', function () {
cy.get(rewards.connectToVegaBtn)
cy.get(connectToVegaBtn)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});

View File

@ -1,31 +1,30 @@
import navigation from '../locators/navigation.locators';
import staking from '../locators/staking.locators';
const guideLink = '[data-testid="staking-guide-link"]';
const step1 = '[data-testid="staking-step-1"]';
const step2 = '[data-testid="staking-step-2"]';
const step3 = '[data-testid="staking-step-3"]';
const sectionHeader = 'h2';
const connectToEthBtn = '[data-testid="connect-to-eth-btn"]';
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
const link = '[data-testid="link"]';
const warning = '[data-testid="callout"]';
context('Staking Page - verify elements on page', function () {
before('navigate to staking page', function () {
cy.visit('/')
.get(navigation.section)
.within(() => {
cy.get(navigation.staking).click();
});
cy.visit('/').navigate_to('staking');
});
describe('with wallets disconnected', function () {
describe('description section', function () {
it('should have staking tab highlighted', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.staking).should('have.attr', 'aria-current');
});
cy.verify_tab_highlighted('staking');
});
it('should have STAKING ON VEGA header visible', function () {
cy.get(staking.pageHeader)
.should('be.visible')
.and('have.text', 'Staking on Vega');
cy.verify_page_header('Staking on Vega');
});
it('should have Staking Guide link visible', function () {
cy.get(staking.guideLink)
cy.get(guideLink)
.should('be.visible')
.and('have.text', 'Read more about staking on Vega')
.and(
@ -38,16 +37,16 @@ context('Staking Page - verify elements on page', function () {
describe('step 1 section', function () {
it('should have header visible', function () {
cy.get(staking.step1).within(() => {
cy.get(staking.sectionHeader)
cy.get(step1).within(() => {
cy.get(sectionHeader)
.should('be.visible')
.and('have.text', 'Step 1. Connect to a Vega Wallet');
});
});
it('should have text visible', function () {
cy.get(staking.step1).within(() => {
cy.get(staking.link)
cy.get(step1).within(() => {
cy.get(link)
.should('be.visible')
.and('have.text', 'Vega Wallet')
.and('have.attr', 'href', 'https://vega.xyz/wallet');
@ -55,16 +54,16 @@ context('Staking Page - verify elements on page', function () {
});
it('should have connect to eth button visible', function () {
cy.get(staking.step1).within(() => {
cy.get(staking.connectToEthBtn)
cy.get(step1).within(() => {
cy.get(connectToEthBtn)
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet');
});
});
it('should have connect to vega button visible', function () {
cy.get(staking.step1).within(() => {
cy.get(staking.connectToVegaBtn)
cy.get(step1).within(() => {
cy.get(connectToVegaBtn)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});
@ -73,16 +72,16 @@ context('Staking Page - verify elements on page', function () {
describe('step 2 section', function () {
it('should have header visible', function () {
cy.get(staking.step2).within(() => {
cy.get(staking.sectionHeader)
cy.get(step2).within(() => {
cy.get(sectionHeader)
.should('be.visible')
.and('have.text', 'Step 2. Associate tokens with a Vega Wallet');
});
});
it('should have warning visible', function () {
cy.get(staking.step2).within(() => {
cy.get(staking.warning)
cy.get(step2).within(() => {
cy.get(warning)
.should('be.visible')
.and(
'have.text',
@ -94,8 +93,8 @@ context('Staking Page - verify elements on page', function () {
describe('step 3 section', function () {
it('should have header visible', function () {
cy.get(staking.step3).within(() => {
cy.get(staking.sectionHeader)
cy.get(step3).within(() => {
cy.get(sectionHeader)
.should('be.visible')
.and(
'have.text',

View File

@ -1,34 +1,26 @@
import navigation from '../locators/navigation.locators';
import vesting from '../locators/vesting.locators';
const connectPrompt = '[data-testid="eth-connect-prompt"]';
const connectButton = '[data-testid="connect-to-eth-btn"]';
context('Vesting Page - verify elements on page', function () {
before('navigate to vesting page', function () {
cy.visit('/')
.get(navigation.section)
.within(() => {
cy.get(navigation.vesting).click();
});
cy.visit('/').navigate_to('vesting');
});
describe('with wallets disconnected', function () {
it('should have vesting tab highlighted', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.vesting).should('have.attr', 'aria-current');
});
cy.verify_tab_highlighted('vesting');
});
it('should have VESTING header visible', function () {
cy.get(vesting.pageHeader)
.should('be.visible')
.and('have.text', 'Vesting');
cy.verify_page_header('Vesting');
});
it('should have connect Eth wallet info', function () {
cy.get(vesting.connectPrompt).should('be.visible');
cy.get(connectPrompt).should('be.visible');
});
it('should have connect Eth wallet button', function () {
cy.get(vesting.connectButton)
cy.get(connectButton)
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet');
});

View File

@ -0,0 +1,251 @@
const walletContainer = '[data-testid="ethereum-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
const connectToEthButton = '[data-testid="connect-to-eth-wallet-button"]';
const connectorList = '[data-testid="web3-connector-list"]';
const associate = '[href="/staking/associate"]';
const disassociate = '[href="/staking/disassociate"]';
const disconnect = '[data-testid="disconnect-from-eth-wallet-button"]';
const accountNo = '[data-testid="ethereum-account-truncated"]';
const currencyTitle = '[data-testid="currency-title"]';
const currencyValue = '[data-testid="currency-value"]';
const vegaInVesting = '[data-testid="vega-in-vesting-contract"]';
const vegaInWallet = '[data-testid="vega-in-wallet"]';
const progressBar = '[data-testid="progress-bar"]';
const currencyLocked = '[data-testid="currency-locked"]';
const currencyUnlocked = '[data-testid="currency-unlocked"]';
const dialog = '[role="dialog"]';
const dialogHeader = '[data-testid="dialog-title"]';
const dialogCloseBtn = '[data-testid="dialog-close"]';
context('Ethereum Wallet - verify elements on widget', function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('with wallets disconnected', function () {
before('wait for widget to load', function () {
cy.get(walletContainer, { timeout: 10000 }).should('be.visible');
});
it('should have ETHEREUM KEY header visible', function () {
cy.get(walletContainer).within(() => {
cy.get(walletHeader)
.should('be.visible')
.and('have.text', 'Ethereum key');
});
});
it('should have Connect Ethereum button visible', function () {
cy.get(walletContainer).within(() => {
cy.get(connectToEthButton)
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet to associate $VEGA');
});
});
});
describe('when Connect Ethereum clicked', function () {
before('', function () {
cy.get(connectToEthButton).click();
});
it('should have Connect Ethereum header visible', function () {
cy.get(dialog).within(() => {
cy.get(dialogHeader)
.should('be.visible')
.and('have.text', 'Connect to your Ethereum wallet');
});
});
it('should have connector list visible', function () {
const connectList = [
'Unknown',
'MetaMask, Brave or other injected web wallet',
'WalletConnect',
];
cy.get(connectorList).within(() => {
cy.get('button').each(($btn, i) => {
cy.wrap($btn).should('be.visible').and('have.text', connectList[i]);
});
});
});
after('close popup', function () {
cy.get(dialog)
.within(() => {
cy.get(dialogCloseBtn).click();
})
.should('not.exist');
});
});
describe('when Ethereum wallet connected', function () {
before('connect to Ethereum wallet', function () {
cy.ethereum_wallet_connect();
});
it('should have ETHEREUM KEY header visible', function () {
cy.get(walletContainer).within(() => {
cy.get(walletHeader)
.should('be.visible')
.and('have.text', 'Ethereum key');
});
});
it('should have account number visible', function () {
cy.get(walletContainer).within(() => {
cy.get(accountNo)
.should('be.visible')
.and('have.text', Cypress.env('ethWalletPublicKeyTruncated'));
});
});
it('should have Associate button visible', function () {
cy.get(walletContainer).within(() => {
cy.get(associate).should('be.visible').and('have.text', 'Associate');
});
});
it('should have Disassociate button visible', function () {
cy.get(walletContainer).within(() => {
cy.get(disassociate)
.should('be.visible')
.and('have.text', 'Disassociate');
});
});
it('should have Disconnect button visible', function () {
cy.get(walletContainer).within(() => {
cy.get(disconnect).should('be.visible').and('have.text', 'Disconnect');
});
});
describe('VEGA IN VESTING CONTRACT', function () {
it('should have currency title visible', function () {
cy.get(vegaInVesting).within(() => {
cy.get(currencyTitle)
.should('be.visible')
.and('have.text', 'VEGAIn vesting contract');
});
});
it('should have currency value visible', function () {
cy.get(vegaInVesting).within(() => {
cy.get(currencyValue)
.should('be.visible')
.invoke('text')
.should('match', /\d{0,3}(,\d{3})*\.\d{18}$/);
});
});
it('should have progress bar visible', function () {
cy.get(vegaInVesting).within(() => {
cy.get(progressBar).should('be.visible');
});
});
it('should have locked currency visible', function () {
cy.get(vegaInVesting).within(() => {
cy.get(currencyLocked)
.should('be.visible')
.invoke('text')
.should('match', /\d{0,3}(,\d{3})*\.\d{2}$/);
});
});
it('should have unlocked currency visible', function () {
cy.get(vegaInVesting).within(() => {
cy.get(currencyUnlocked)
.should('be.visible')
.invoke('text')
.should('match', /\d{0,3}(,\d{3})*\.\d{2}$/);
});
});
it('should match total & locked/unlocked currency value', function () {
cy.get(vegaInVesting)
.within(() => {
cy.get(currencyValue)
.invoke('text')
.convert_token_value_to_number()
.as('value');
cy.get(currencyLocked)
.invoke('text')
.convert_token_value_to_number()
.as('locked');
cy.get(currencyUnlocked)
.invoke('text')
.convert_token_value_to_number()
.as('unlocked');
})
.then(function () {
expect(this.value).to.equal(this.locked + this.unlocked);
});
});
});
describe('VEGA IN WALLET', function () {
it('should have currency title visible', function () {
cy.get(vegaInWallet).within(() => {
cy.get(currencyTitle)
.should('be.visible')
.and('have.text', 'VEGAIn Wallet');
});
});
it('should have currency value visible', function () {
cy.get(vegaInWallet).within(() => {
cy.get(currencyValue)
.should('be.visible')
.invoke('text')
.should('match', /\d{0,3}(,\d{3})*\.\d{18}$/);
});
});
it('should have progress bar visible', function () {
cy.get(vegaInWallet).within(() => {
cy.get(progressBar).should('be.visible');
});
});
it('should have locked currency visible', function () {
cy.get(vegaInWallet).within(() => {
cy.get(currencyLocked)
.should('be.visible')
.invoke('text')
.should('match', /\d{0,3}(,\d{3})*\.\d{2}$/);
});
});
it('should have unlocked currency visible', function () {
cy.get(vegaInWallet).within(() => {
cy.get(currencyUnlocked)
.should('be.visible')
.invoke('text')
.should('match', /\d{0,3}(,\d{3})*\.\d{2}$/);
});
});
it('should match total & locked/unlocked currency value', function () {
cy.get(vegaInWallet)
.within(() => {
cy.get(currencyValue)
.invoke('text')
.convert_token_value_to_number()
.as('value');
cy.get(currencyLocked)
.invoke('text')
.convert_token_value_to_number()
.as('locked');
cy.get(currencyUnlocked)
.invoke('text')
.convert_token_value_to_number()
.as('unlocked');
})
.then(function () {
expect(this.value).to.equal(this.locked + this.unlocked);
});
});
});
});
});

View File

@ -1,36 +1,28 @@
import navigation from '../locators/navigation.locators';
import withdraw from '../locators/withdraw.locators';
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
const warning = '[data-testid="callout"]';
context('Withdraw Page - verify elements on page', function () {
before('navigate to withdraw page', function () {
cy.visit('/')
.get(navigation.section)
.within(() => {
cy.get(navigation.withdraw).click();
});
cy.visit('/').navigate_to('withdraw');
});
describe('with wallets disconnected', function () {
it('should have withdraw tab highlighted', function () {
cy.get(navigation.section).within(() => {
cy.get(navigation.withdraw).should('have.attr', 'aria-current');
});
cy.verify_tab_highlighted('withdraw');
});
it('should have WITHDRAW header visible', function () {
cy.get(withdraw.pageHeader)
.should('be.visible')
.and('have.text', 'Withdraw');
cy.verify_page_header('Withdraw');
});
it('should have connect Vega wallet button', function () {
cy.get(withdraw.connectToVegaBtn)
cy.get(connectToVegaBtn)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});
it('should have withdraw information box', function () {
cy.get(withdraw.warning).should('be.visible');
cy.get(warning).should('be.visible');
});
});
});

View File

@ -1,7 +0,0 @@
export default {
pageHeader: 'header h1',
sectionHeader: 'h2',
link: '[data-testid="link"]',
warning: '[data-testid="callout"]',
connectToVegaBtn: '[data-testid="connect-to-vega-wallet-btn"]',
};

View File

@ -1,6 +0,0 @@
import common from './common.locators';
export default {
...common,
noProposals: '[data-testid="no-proposals"]',
};

View File

@ -1,15 +0,0 @@
export default {
tokenDetailsTable: '.token-details',
address: '[data-testid="token-address"]',
contract: '[data-testid="token-contract"]',
totalSupply: '[data-testid="total-supply"]',
circulatingSupply: '[data-testid="circulating-supply"]',
staked: '[data-testid="staked"]',
tranchesLink: '[data-testid="tranches-link"]',
redeemBtn: '[data-testid="check-vesting-page-btn"]',
getVegaWalletLink: '[data-testid="get-vega-wallet-link"]',
associateVegaLink: '[data-testid="associate-vega-tokens-link-on-homepage"]',
stakingBtn: '[data-testid="staking-button-on-homepage"]',
governanceBtn: '[data-testid="governance-button-on-homepage"]',
};

View File

@ -1,13 +0,0 @@
import common from './common.locators';
export default {
...common,
section: 'nav',
home: '[href="/"]',
vesting: '[href="/vesting"]',
staking: '[href="/staking"]',
rewards: '[href="/rewards"]',
withdraw: '[href="/withdraw"]',
governance: '[href="/governance"]',
spinner: 'splash-loader',
};

View File

@ -1,5 +0,0 @@
import common from './common.locators';
export default {
...common,
};

View File

@ -1,26 +0,0 @@
import common from './common.locators';
export default {
...common,
guideLink: '[data-testid="staking-guide-link"]',
step1: '[data-testid="staking-step-1"]',
step2: '[data-testid="staking-step-2"]',
step3: '[data-testid="staking-step-3"]',
connectToEthBtn: '[data-testid="connect-to-eth-btn"]',
connectToVegaBtn: '[data-testid="connect-to-vega-wallet-btn"]',
validatorNames: '[data-testid="node-list-item-name"]',
epochEndingText: '[data-testid="epoch-countdown"]',
addStakeRadioButton: '[data-testid="add-stake-radio"]',
removeStakeRadioButton: '[data-testid="remove-stake-radio"]',
tokenAmountInput: '[data-testid="token-amount-input"]',
tokenInputApprove: '[data-testid="token-input-approve-button"]',
tokenInputSubmit: '[data-testid="token-input-submit-button"]',
stakedAmounts: '[data-testid="staked-validator-item"]',
stakeNextEpochValue: '[data-testid="stake-next-epoch"]',
stakeThisEpochValue: '[data-testid="stake-this-epoch"]',
stakeMaximumTokens: '[data-testid="token-amount-use-maximum"]',
stakeAssociateWalletRadio: '[data-testid="associate-radio-wallet"]',
disassociateButton: '[data-testid="disassociate-tokens-btn"]',
associateMoreTokensButton: '[data-testid="associate-more-tokens-btn"]',
associateButton: '[data-testid="associate-tokens-btn"]',
};

View File

@ -1,7 +0,0 @@
import common from './common.locators';
export default {
...common,
connectPrompt: '[data-testid="eth-connect-prompt"]',
connectButton: '[data-testid="connect-to-eth-btn"]',
};

View File

@ -1,14 +0,0 @@
import common from './common.locators';
export default {
...common,
connectRestForm: '[data-testid="rest-connector-form"]',
name: '#wallet',
passphrase: '#passphrase',
vegawallet: '[data-testid="vega-wallet"]',
ethWallet: '[data-testid="ethereum-wallet"]',
ethWalletConnectToEth: '[data-testid="connect-to-eth-wallet-button"]',
ethWalletConnect: '[data-testid="web3-connector-Unknown"]',
ethWalletAssociate: '[href="/staking/associate"]',
ethWalletDisassociate: '[href="/staking/disassociate"]',
};

View File

@ -1,5 +0,0 @@
import common from './common.locators';
export default {
...common,
};

View File

@ -0,0 +1,33 @@
Cypress.Commands.add(
'convert_token_value_to_number',
{ prevSubject: true },
(subject) => {
return parseFloat(subject.replace(/,/g, ''));
}
);
const navigation = {
section: 'nav',
home: '[href="/"]',
vesting: '[href="/vesting"]',
staking: '[href="/staking"]',
rewards: '[href="/rewards"]',
withdraw: '[href="/withdraw"]',
governance: '[href="/governance"]',
};
Cypress.Commands.add('navigate_to', (page) => {
return cy.get(navigation.section).within(() => {
cy.get(navigation[page]).click();
});
});
Cypress.Commands.add('verify_tab_highlighted', (page) => {
return cy.get(navigation.section).within(() => {
cy.get(navigation[page]).should('have.attr', 'aria-current');
});
});
Cypress.Commands.add('verify_page_header', (text) => {
return cy.get('header h1').should('be.visible').and('have.text', text);
});

View File

@ -1,51 +0,0 @@
import wallet from '../locators/wallet.locators';
cy.ethereum_wallet_connect = () => {
cy.highlight('Connecting Eth Wallet');
cy.get(wallet.ethWalletConnectToEth).within(() => {
cy.contains('Connect Ethereum wallet to associate $VEGA')
.should('be.visible')
.click();
});
cy.get(wallet.ethWalletConnect).click();
cy.get(wallet.ethWalletConnect, { timeout: 60000 }).should('not.exist');
cy.get(wallet.ethWallet).within(() => {
// this check is required since it ensures the wallet is fully (not partially) loaded
cy.contains('Locked', { timeout: 15000 }).should('be.visible');
});
};
cy.ethereum_wallet_check_associated_value_is = (expectedVal) => {
cy.highlight(`Checking Eth Wallet - Associated Value is ${expectedVal}`);
cy.get(wallet.ethWallet).within(() => {
cy.contains('Associated', { timeout: 20000 })
.parent()
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
cy.ethereum_wallet_check_associated_vega_key_value_is = (
vegaShortPublicKey,
expectedVal
) => {
cy.highlight(
`Checking Eth Wallet - Vega Key Associated Value is ${expectedVal} for key ${vegaShortPublicKey}`
);
cy.get(wallet.ethWallet).within(() => {
cy.contains(vegaShortPublicKey, { timeout: 20000 })
.parent()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
cy.ethereum_wallet_check_associated_vega_key_is_no_longer_showing = (
vegaShortPublicKey
) => {
cy.highlight('Checking Eth Wallet - Vega Key Associated is not showing');
cy.get(wallet.ethWallet).within(() => {
cy.contains(vegaShortPublicKey, { timeout: 20000 }).should('not.exist');
});
};

View File

@ -1,5 +1,11 @@
import '@vegaprotocol/cypress';
import './common.functions.js';
import './staking.functions.js';
import './wallet-eth.functions.js';
import './wallet-teardown.functions.js';
import './wallet-vega.functions.js';
// Hide fetch/XHR requests - They create a lot of noise in command log
const app = window.top;
if (!app.document.head.querySelector('[data-hide-command-log-request]')) {

View File

@ -1,11 +1,27 @@
import staking from '../locators/staking.locators';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const tokenInputApprove = '[data-testid="token-input-approve-button"]';
const addStakeRadioButton = '[data-testid="add-stake-radio"]';
const removeStakeRadioButton = '[data-testid="remove-stake-radio"]';
const ethWalletAssociateButton = '[href="/staking/associate"]';
const ethWalletDissociateButton = '[href="/staking/disassociate"]';
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const stakeMaximumTokens = '[data-testid="token-amount-use-maximum"]';
cy.staking_validator_page_add_stake = (stake) => {
Cypress.Commands.add('wait_for_begining_of_epoch', () => {
cy.highlight(`Waiting for next epoch to start`);
cy.contains('Waiting for next epoch to start', { timeout: 10000 }).should(
'not.exist'
);
cy.contains('Waiting for next epoch to start', { timeout: 20000 });
});
Cypress.Commands.add('staking_validator_page_add_stake', (stake) => {
cy.highlight(`Adding a stake of ${stake}`);
cy.get(staking.addStakeRadioButton).click({ force: true });
cy.get(staking.tokenAmountInput).type(stake);
cy.contains('Waiting for next epoch to start', { timeout: 10000 });
cy.get(staking.tokenInputSubmit, { timeout: 8000 })
cy.get(addStakeRadioButton).click({ force: true });
cy.get(tokenAmountInputBox).type(stake);
cy.wait_for_begining_of_epoch();
cy.get(tokenSubmitButton, { timeout: 8000 })
.should('be.enabled')
.and('contain', `Add ${stake} $VEGA tokens`)
.and('be.visible')
@ -14,14 +30,14 @@ cy.staking_validator_page_add_stake = (stake) => {
'At the beginning of the next epoch your $VEGA will be nominated to the validator',
{ timeout: 20000 }
).should('be.visible');
};
});
cy.staking_validator_page_removeStake = (stake) => {
Cypress.Commands.add('staking_validator_page_removeStake', (stake) => {
cy.highlight(`Removing a stake of ${stake}`);
cy.get(staking.removeStakeRadioButton).click({ force: true });
cy.get(staking.tokenAmountInput).type(stake);
cy.contains('Waiting for next epoch to start', { timeout: 10000 });
cy.get(staking.tokenInputSubmit)
cy.get(removeStakeRadioButton).click({ force: true });
cy.get(tokenAmountInputBox).type(stake);
cy.wait_for_begining_of_epoch();
cy.get(tokenSubmitButton)
.should('be.enabled', { timeout: 8000 })
.and('contain', `Remove ${stake} $VEGA tokens at the end of epoch`)
.and('be.visible')
@ -29,53 +45,52 @@ cy.staking_validator_page_removeStake = (stake) => {
cy.contains(`${stake} $VEGA has been removed from validator`).should(
'be.visible'
);
};
});
cy.staking_page_associate_tokens = (amount, approve = false) => {
cy.highlight(`Associating ${amount} tokens`);
cy.get(staking.associateButton).click();
cy.get(staking.stakeAssociateWalletRadio, { timeout: 30000 }).click();
cy.get(staking.tokenAmountInput, { timeout: 10000 }).type(amount);
if (approve) {
cy.get(staking.tokenInputApprove, { timeout: 40000 })
.should('be.enabled')
.click();
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
'be.visible'
);
cy.contains('Approve $VEGA Tokens for staking on Vega', {
timeout: 40000,
}).should('not.exist');
Cypress.Commands.add(
'staking_page_associate_tokens',
(amount, approve = false) => {
cy.highlight(`Associating ${amount} tokens`);
cy.get(ethWalletAssociateButton).first().click();
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(tokenAmountInputBox, { timeout: 10000 }).type(amount);
if (approve) {
cy.get(tokenInputApprove, { timeout: 40000 })
.should('be.enabled')
.click();
cy.contains('Approve $VEGA Tokens for staking on Vega').should(
'be.visible'
);
cy.contains('Approve $VEGA Tokens for staking on Vega', {
timeout: 40000,
}).should('not.exist');
}
cy.get(tokenSubmitButton, { timeout: 40000 }).should('be.enabled').click();
cy.contains('can now participate in governance and nominate a validator', {
timeout: 60000,
}).should('be.visible');
}
cy.get(staking.tokenInputSubmit, { timeout: 40000 })
.should('be.enabled')
.click();
cy.contains('can now participate in governance and nominate a validator', {
timeout: 60000,
}).should('be.visible');
};
);
cy.staking_page_disassociate_tokens = (amount) => {
Cypress.Commands.add('staking_page_disassociate_tokens', (amount) => {
cy.highlight(`Disassociating ${amount} tokens via Staking Page`);
cy.get(staking.disassociateButton).click();
cy.get(staking.stakeAssociateWalletRadio, { timeout: 30000 }).click();
cy.get(staking.tokenAmountInput, { timeout: 10000 }).type(amount);
cy.get(ethWalletDissociateButton).first().click();
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(tokenAmountInputBox, { timeout: 10000 }).type(amount);
cy.get(staking.tokenInputSubmit, { timeout: 40000 })
.should('be.enabled')
.click();
cy.get(tokenSubmitButton, { timeout: 40000 }).should('be.enabled').click();
cy.contains(`${amount} $VEGA tokens have been returned to Ethereum wallet`, {
timeout: 60000,
}).should('be.visible');
};
});
cy.staking_page_disassociate_all_tokens = () => {
Cypress.Commands.add('staking_page_disassociate_all_tokens', () => {
cy.highlight(`Disassociating all tokens via Staking Page`);
cy.get(staking.disassociateButton).click();
cy.get(staking.stakeAssociateWalletRadio, { timeout: 20000 }).click();
cy.get(staking.stakeMaximumTokens, { timeout: 60000 }).click();
cy.get(staking.tokenInputSubmit, { timeout: 10000 }).click();
cy.get(ethWalletDissociateButton).first().click();
cy.get(associateWalletRadioButton, { timeout: 20000 }).click();
cy.get(stakeMaximumTokens, { timeout: 60000 }).click();
cy.get(tokenSubmitButton, { timeout: 10000 }).click();
cy.contains('$VEGA tokens have been returned to Ethereum wallet', {
timeout: 60000,
}).should('be.visible');
};
});

View File

@ -0,0 +1,57 @@
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const connectToEthButton = '[data-testid="connect-to-eth-wallet-button"]';
const capsuleWalletConnectButton = '[data-testid="web3-connector-Unknown"]';
Cypress.Commands.add('ethereum_wallet_connect', () => {
cy.highlight('Connecting Eth Wallet');
cy.get(connectToEthButton).within(() => {
cy.contains('Connect Ethereum wallet to associate $VEGA')
.should('be.visible')
.click();
});
cy.get(capsuleWalletConnectButton).click();
cy.get(capsuleWalletConnectButton, { timeout: 60000 }).should('not.exist');
cy.get(ethWalletContainer).within(() => {
// this check is required since it ensures the wallet is fully (not partially) loaded
cy.contains('Locked', { timeout: 15000 }).should('be.visible');
});
});
Cypress.Commands.add(
'ethereum_wallet_check_associated_value_is',
(expectedVal) => {
cy.highlight(`Checking Eth Wallet - Associated Value is ${expectedVal}`);
cy.get(ethWalletContainer).within(() => {
cy.contains('Associated', { timeout: 20000 })
.parent()
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
}
);
Cypress.Commands.add(
'ethereum_wallet_check_associated_vega_key_value_is',
(vegaShortPublicKey, expectedVal) => {
cy.highlight(
`Checking Eth Wallet - Vega Key Associated Value is ${expectedVal} for key ${vegaShortPublicKey}`
);
cy.get(ethWalletContainer).within(() => {
cy.contains(vegaShortPublicKey, { timeout: 20000 })
.parent()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
}
);
Cypress.Commands.add(
'ethereum_wallet_check_associated_vega_key_is_no_longer_showing',
(vegaShortPublicKey) => {
cy.highlight('Checking Eth Wallet - Vega Key Associated is not showing');
cy.get(ethWalletContainer).within(() => {
cy.contains(vegaShortPublicKey, { timeout: 20000 }).should('not.exist');
});
}
);

View File

@ -64,27 +64,30 @@ Cypress.Commands.add(
}
);
cy.vega_wallet_teardown_staking = (stakingBridgeContract) => {
cy.highlight('Tearing down staking tokens from vega wallet if present');
cy.wrap(
stakingBridgeContract.stakeBalance(ethWalletPubKey, vegaWalletPubKey),
{
timeout: transactionTimeout,
log: false,
}
).then((stake_amount) => {
if (String(stake_amount) != '0') {
cy.wrap(
stakingBridgeContract.removeStake(stake_amount, vegaWalletPubKey),
{ timeout: transactionTimeout, log: false }
).then((tx) => {
cy.wait_for_transaction(tx);
});
}
});
};
Cypress.Commands.add(
'vega_wallet_teardown_staking',
(stakingBridgeContract) => {
cy.highlight('Tearing down staking tokens from vega wallet if present');
cy.wrap(
stakingBridgeContract.stakeBalance(ethWalletPubKey, vegaWalletPubKey),
{
timeout: transactionTimeout,
log: false,
}
).then((stake_amount) => {
if (String(stake_amount) != '0') {
cy.wrap(
stakingBridgeContract.removeStake(stake_amount, vegaWalletPubKey),
{ timeout: transactionTimeout, log: false }
).then((tx) => {
cy.wait_for_transaction(tx);
});
}
});
}
);
cy.vega_wallet_teardown_vesting = (vestingContract) => {
Cypress.Commands.add('vega_wallet_teardown_vesting', (vestingContract) => {
cy.highlight('Tearing down vesting tokens from vega wallet if present');
cy.wrap(vestingContract.stakeBalance(ethWalletPubKey, vegaWalletPubKey), {
timeout: transactionTimeout,
@ -99,8 +102,8 @@ cy.vega_wallet_teardown_vesting = (vestingContract) => {
});
}
});
};
});
cy.wait_for_transaction = (tx) => {
Cypress.Commands.add('wait_for_transaction', (tx) => {
cy.wrap(tx.wait(1).catch(cy.log), { timeout: transactionTimeout });
};
});

View File

@ -1,10 +1,10 @@
import wallet from '../locators/wallet.locators';
const vegaWalletContainer = '[data-testid="vega-wallet"]';
const restConnectorForm = '[data-testid="rest-connector-form"]';
const vegaWalletName = Cypress.env('vegaWalletName');
const vegaWalletLocation = Cypress.env('vegaWalletLocation');
const vegaWalletPassphrase = Cypress.env('vegaWalletPassphrase');
cy.vega_wallet_import = () => {
Cypress.Commands.add('vega_wallet_import', () => {
cy.highlight(`Importing Vega Wallet ${vegaWalletName}`);
cy.exec(`vegawallet init -f --home ${vegaWalletLocation}`);
cy.exec(
@ -14,11 +14,11 @@ cy.vega_wallet_import = () => {
cy.exec(
`vegawallet service run --network DV --automatic-consent --home ${vegaWalletLocation}`
);
};
});
cy.vega_wallet_connect = () => {
Cypress.Commands.add('vega_wallet_connect', () => {
cy.highlight('Connecting Vega Wallet');
cy.get(wallet.vegawallet).within(() => {
cy.get(vegaWalletContainer).within(() => {
cy.get('button')
.contains('Connect Vega wallet to use associated $VEGA')
.should('be.enabled')
@ -26,31 +26,31 @@ cy.vega_wallet_connect = () => {
.click({ force: true });
});
cy.get('button').contains('rest provider').click();
cy.get(wallet.connectRestForm).within(() => {
cy.get(wallet.name).click().type(vegaWalletName);
cy.get(wallet.passphrase).click().type(vegaWalletPassphrase);
cy.get(restConnectorForm).within(() => {
cy.get('#wallet').click().type(vegaWalletName);
cy.get('#passphrase').click().type(vegaWalletPassphrase);
cy.get('button').contains('Connect').click();
});
cy.contains(`${vegaWalletName} key`, { timeout: 20000 }).should('be.visible');
};
});
cy.vega_wallet_check_unstaked_value_is = (expectedVal) => {
Cypress.Commands.add('vega_wallet_check_unstaked_value_is', (expectedVal) => {
cy.highlight(`Checking vega wallet - Unstaked Value is ${expectedVal}`);
cy.get(wallet.vegawallet).within(() => {
cy.get(vegaWalletContainer).within(() => {
cy.contains('Unstaked', { timeout: 40000 })
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
});
cy.vega_wallet_check_associated_value_is = (expectedVal) => {
Cypress.Commands.add('vega_wallet_check_associated_value_is', (expectedVal) => {
cy.highlight(`Checking vega wallet - Associated Value is ${expectedVal}`);
cy.get(wallet.vegawallet).within(() => {
cy.get(vegaWalletContainer).within(() => {
cy.contains('Associated', { timeout: 40000 })
.parent()
.siblings()
.contains(expectedVal, { timeout: 40000 })
.should('be.visible');
});
};
});

View File

@ -128,7 +128,7 @@ const ConnectedKey = () => {
return (
<>
{totalVestedBalance.plus(totalLockedBalance).isEqualTo(0) ? null : (
<>
<section data-testid="vega-in-vesting-contract">
<WalletCardAsset
image={vegaVesting}
decimals={appState.decimals}
@ -145,7 +145,7 @@ const ConnectedKey = () => {
rightLabel={t('Unlocked')}
light={false}
/>
</>
</section>
)}
{!Object.keys(appState.associationBreakdown.vestingAssociations)
.length ? null : (
@ -154,22 +154,24 @@ const ConnectedKey = () => {
notAssociated={notAssociatedInContract}
/>
)}
<WalletCardAsset
image={vegaWhite}
decimals={appState.decimals}
name="VEGA"
symbol="In Wallet"
balance={walletWithAssociations}
dark={true}
/>
{!Object.keys(
appState.associationBreakdown.stakingAssociations
) ? null : (
<AssociatedAmounts
associations={appState.associationBreakdown.stakingAssociations}
notAssociated={walletBalance}
<section data-testid="vega-in-wallet">
<WalletCardAsset
image={vegaWhite}
decimals={appState.decimals}
name="VEGA"
symbol="In Wallet"
balance={walletWithAssociations}
dark={true}
/>
)}
{!Object.keys(
appState.associationBreakdown.stakingAssociations
) ? null : (
<AssociatedAmounts
associations={appState.associationBreakdown.stakingAssociations}
notAssociated={walletBalance}
/>
)}
</section>
<WalletCardActions>
<Link
className={getButtonClasses('flex-1 mr-4', 'secondary')}
@ -201,7 +203,12 @@ export const EthWallet = () => {
<h1 className="text-h3 uppercase">{t('ethereumKey')}</h1>
{account && (
<div className="px-4 text-right">
<div className="font-mono">{truncateMiddle(account)}</div>
<div
className="font-mono"
data-testid="ethereum-account-truncated"
>
{truncateMiddle(account)}
</div>
{pendingTxs && (
<div>
<button
@ -245,6 +252,7 @@ export const EthWallet = () => {
<button
className="mt-4 underline"
onClick={() => connector.deactivate()}
data-testid="disconnect-from-eth-wallet-button"
>
{t('disconnect')}
</button>

View File

@ -99,6 +99,7 @@ export const LockedProgress = ({
'border-black': light,
'border-white': !light,
})}
data-testid="progress-bar"
>
<ProgressBar percentage={lockedPercentage} bgColor={leftColor} />
<ProgressBar percentage={unlockedPercentage} bgColor={rightColor} />
@ -119,8 +120,12 @@ export const LockedProgress = ({
</ProgressContents>
<ProgressContents light={light}>
<span>{formatNumber(locked, decimals)}</span>
<span>{formatNumber(unlocked, decimals)}</span>
<span data-testid="currency-locked">
{formatNumber(locked, decimals)}
</span>
<span data-testid="currency-unlocked">
{formatNumber(unlocked, decimals)}
</span>
</ProgressContents>
</div>
);

View File

@ -47,7 +47,10 @@ interface WalletCardHeaderProps {
export const WalletCardHeader = ({ children }: WalletCardHeaderProps) => {
return (
<div className="grid grid-cols-1 sm:grid-cols-[auto_1fr] gap-4">
<div
className="grid grid-cols-1 sm:grid-cols-[auto_1fr] gap-4"
data-testid="wallet-header"
>
{children}
</div>
);
@ -151,7 +154,10 @@ export const WalletCardAsset = ({
}`}
/>
<div>
<div className="flex font-medium align-center">
<div
className="flex font-medium align-center"
data-testid="currency-title"
>
<h1
className={`text-h5 mb-0 px-8 uppercase leading-none ${
dark ? 'text-white' : 'text-black'
@ -167,7 +173,10 @@ export const WalletCardAsset = ({
{subheading || symbol}
</h2>
</div>
<div className="px-8 text-h5 basis-full font-mono">
<div
className="px-8 text-h5 basis-full font-mono"
data-testid="currency-value"
>
<span>{integers}.</span>
<span className={dark ? 'text-white-60' : 'text-black-60'}>
{decimalsPlaces}

View File

@ -68,7 +68,7 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
return (
<Splash>
<div className="flex flex-col items-center gap-12">
<p>Something went wrong: {error.message}</p>
<p className="text-white">Something went wrong: {error.message}</p>
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
</div>
</Splash>
@ -79,7 +79,9 @@ export const Web3Content = ({ children, appChainId }: Web3ContentProps) => {
return (
<Splash>
<div className="flex flex-col items-center gap-12">
<p>This app only works on chain ID: {appChainId}</p>
<p className="text-white">
This app only works on chain ID: {appChainId}
</p>
<Button onClick={() => connector.deactivate()}>Disconnect</Button>
</div>
</Splash>

View File

@ -18,21 +18,25 @@ describe('accounts', () => {
cy.getByTestId('tab-accounts').should('be.visible');
cy.getByTestId('tab-accounts')
.should('be.visible')
.get(`[row-id='General-tEURO-null']`)
.find('[col-id="asset.symbol"]')
.should('have.text', 'tEURO');
cy.getByTestId('tab-accounts')
.should('be.visible')
.get(`[row-id='General-tEURO-null']`)
.find('[col-id="type"]')
.should('have.text', 'General');
cy.getByTestId('tab-accounts')
.should('be.visible')
.get(`[row-id='General-tEURO-null']`)
.find('[col-id="market.name"]')
.should('have.text', '—');
cy.getByTestId('tab-accounts')
.should('be.visible')
.get(`[row-id='General-tEURO-null']`)
.find('[col-id="balance"]')
.should('have.text', '1,000.00000');

View File

@ -33,7 +33,7 @@ describe('deal ticket orders', () => {
const orderTIFDropDown = 'order-tif';
const placeOrderBtn = 'place-order';
const orderStatusHeader = 'order-status-header';
const orderTransactionHash = 'tx-hash';
const orderTransactionHash = 'tx-block-explorer';
before(() => {
cy.mockGQL((req) => {

View File

@ -19,6 +19,7 @@ describe('positions', () => {
cy.getByTestId('tab-positions').should('be.visible');
cy.getByTestId('tab-positions')
.get('[col-id="market.tradableInstrument.instrument.code"]')
.should('be.visible')
.each(($marketSymbol) => {
cy.wrap($marketSymbol).invoke('text').should('not.be.empty');
});

View File

@ -1,6 +1,6 @@
import merge from 'lodash/merge';
import type { PartialDeep } from 'type-fest';
import type { Orders, Orders_party_orders } from '@vegaprotocol/order-list';
import type { Orders, Orders_party_orders } from '@vegaprotocol/orders';
import {
OrderStatus,
OrderTimeInForce,

View File

@ -3,7 +3,7 @@
// @generated
// This file was automatically generated and should not be edited.
import { MarketTradingMode } from "@vegaprotocol/types";
import { MarketTradingMode, MarketState } from "@vegaprotocol/types";
// ====================================================
// GraphQL query operation: MarketsLanding
@ -27,6 +27,10 @@ export interface MarketsLanding_markets {
* Current mode of execution of the market
*/
tradingMode: MarketTradingMode;
/**
* Current state of the market
*/
state: MarketState;
/**
* timestamps for state changes in the market
*/

View File

@ -1,7 +1,7 @@
import { gql, useQuery } from '@apollo/client';
import { MarketTradingMode } from '@vegaprotocol/types';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import sortBy from 'lodash/sortBy';
import orderBy from 'lodash/orderBy';
import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useGlobalStore } from '../stores';
@ -12,6 +12,7 @@ const MARKETS_QUERY = gql`
markets {
id
tradingMode
state
marketTimestamps {
open
}
@ -20,13 +21,13 @@ const MARKETS_QUERY = gql`
`;
const marketList = ({ markets }: MarketsLanding) =>
sortBy(
orderBy(
markets?.filter(
({ marketTimestamps, tradingMode }) =>
marketTimestamps.open && tradingMode === MarketTradingMode.Continuous
) || [],
'marketTimestamps.open',
'id'
['state', 'marketTimestamps.open', 'id'],
['asc', 'asc', 'asc']
);
export function Index() {

View File

@ -81,6 +81,7 @@ const MarketPage = ({ id }: { id?: string }) => {
return (
<PageQueryContainer<Market, MarketVariables>
query={MARKET_QUERY}
data-testid="market"
options={{
variables: {
marketId,

View File

@ -6,7 +6,7 @@ import {
DealTicketContainer,
MarketInfoContainer,
} from '@vegaprotocol/deal-ticket';
import { OrderListContainer } from '@vegaprotocol/order-list';
import { OrderListContainer } from '@vegaprotocol/orders';
import { TradesContainer } from '@vegaprotocol/trades';
import { PositionsContainer } from '@vegaprotocol/positions';
import { OrderbookContainer } from '@vegaprotocol/market-depth';

View File

@ -1,7 +1,7 @@
import { Web3Container } from '../../components/web3-container';
import { t } from '@vegaprotocol/react-helpers';
import { PositionsContainer } from '@vegaprotocol/positions';
import { OrderListContainer } from '@vegaprotocol/order-list';
import { OrderListContainer } from '@vegaprotocol/orders';
import { AccountsContainer } from '@vegaprotocol/accounts';
import { AnchorButton, Tab, Tabs } from '@vegaprotocol/ui-toolkit';
import { WithdrawalsContainer } from './withdrawals/withdrawals-container';

View File

@ -0,0 +1,67 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
// ====================================================
// GraphQL query operation: MarketNames
// ====================================================
export interface MarketNames_markets_tradableInstrument_instrument_metadata {
__typename: "InstrumentMetadata";
/**
* An arbitrary list of tags to associated to associate to the Instrument (string list)
*/
tags: string[] | null;
}
export interface MarketNames_markets_tradableInstrument_instrument_product {
__typename: "Future";
/**
* String representing the quote (e.g. BTCUSD -> USD is quote)
*/
quoteName: string;
}
export interface MarketNames_markets_tradableInstrument_instrument {
__typename: "Instrument";
/**
* Metadata for this instrument
*/
metadata: MarketNames_markets_tradableInstrument_instrument_metadata;
/**
* A reference to or instance of a fully specified product, including all required product parameters for that product (Product union)
*/
product: MarketNames_markets_tradableInstrument_instrument_product;
}
export interface MarketNames_markets_tradableInstrument {
__typename: "TradableInstrument";
/**
* An instance of or reference to a fully specified instrument.
*/
instrument: MarketNames_markets_tradableInstrument_instrument;
}
export interface MarketNames_markets {
__typename: "Market";
/**
* Market ID
*/
id: string;
/**
* Market full name
*/
name: string;
/**
* An instance of or reference to a tradable instrument.
*/
tradableInstrument: MarketNames_markets_tradableInstrument;
}
export interface MarketNames {
/**
* One or more instruments that are trading on the VEGA network
*/
markets: MarketNames_markets[] | null;
}

View File

@ -1,2 +1,3 @@
export * from './DealTicketQuery';
export * from './MarketInfoQuery';
export * from './MarketNames';

View File

@ -1,11 +1,11 @@
import type { UseFormRegister } from 'react-hook-form';
import { OrderType } from '@vegaprotocol/wallet';
import type { Order } from '../utils/get-default-order';
import { VegaWalletOrderType } from '@vegaprotocol/wallet';
import type { Order } from '@vegaprotocol/orders';
import { DealTicketMarketAmount } from './deal-ticket-market-amount';
import { DealTicketLimitAmount } from './deal-ticket-limit-amount';
export interface DealTicketAmountProps {
orderType: OrderType;
orderType: VegaWalletOrderType;
step: number;
register: UseFormRegister<Order>;
quoteName: string;
@ -17,9 +17,9 @@ export const DealTicketAmount = ({
...props
}: DealTicketAmountProps) => {
switch (orderType) {
case OrderType.Market:
case VegaWalletOrderType.Market:
return <DealTicketMarketAmount {...props} />;
case OrderType.Limit:
case VegaWalletOrderType.Limit:
return <DealTicketLimitAmount {...props} />;
default: {
throw new Error('Invalid ticket type');

View File

@ -1,6 +1,6 @@
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { validateSize } from '../utils/validate-size';
import { validateSize } from '@vegaprotocol/orders';
import type { DealTicketAmountProps } from './deal-ticket-amount';
export type DealTicketLimitAmountProps = Omit<

View File

@ -1,11 +1,10 @@
import type { ReactNode } from 'react';
import { useEffect, useState } from 'react';
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
import { OrderStatus } from '@vegaprotocol/types';
import { VegaOrderTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
import { useState } from 'react';
import { VegaTransactionDialog, VegaTxStatus } from '@vegaprotocol/wallet';
import { DealTicket } from './deal-ticket';
import { useOrderSubmit } from '../hooks/use-order-submit';
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
import { useOrderSubmit } from '@vegaprotocol/orders';
import { OrderStatus } from '@vegaprotocol/types';
export interface DealTicketManagerProps {
market: DealTicketQuery_market;
@ -18,43 +17,20 @@ export const DealTicketManager = ({
}: DealTicketManagerProps) => {
const [orderDialogOpen, setOrderDialogOpen] = useState(false);
const { submit, transaction, finalizedOrder, reset } = useOrderSubmit(market);
const getDialogIntent = (status: VegaTxStatus) => {
if (finalizedOrder) {
if (
finalizedOrder.status === OrderStatus.Active ||
finalizedOrder.status === OrderStatus.Filled ||
finalizedOrder.status === OrderStatus.PartiallyFilled
) {
return Intent.Success;
}
if (finalizedOrder.status === OrderStatus.Parked) {
return Intent.Warning;
}
return Intent.Danger;
const getDialogTitle = (status?: string) => {
switch (status) {
case OrderStatus.Active:
return 'Order submitted';
case OrderStatus.Filled:
return 'Order filled';
case OrderStatus.PartiallyFilled:
return 'Order partially filled';
case OrderStatus.Parked:
return 'Order parked';
default:
return 'Submission failed';
}
if (status === VegaTxStatus.Requested) {
return Intent.Warning;
}
if (status === VegaTxStatus.Error) {
return Intent.Danger;
}
return Intent.None;
};
useEffect(() => {
if (transaction.status !== VegaTxStatus.Default || finalizedOrder) {
setOrderDialogOpen(true);
} else {
setOrderDialogOpen(false);
}
}, [finalizedOrder, transaction.status]);
return (
<>
{children || (
@ -69,23 +45,15 @@ export const DealTicketManager = ({
}
/>
)}
<Dialog
open={orderDialogOpen}
onChange={(isOpen) => {
setOrderDialogOpen(isOpen);
// If closing reset
if (!isOpen) {
reset();
}
}}
intent={getDialogIntent(transaction.status)}
>
<VegaOrderTransactionDialog
transaction={transaction}
finalizedOrder={finalizedOrder}
/>
</Dialog>
<VegaTransactionDialog
key={`submit-order-dialog-${transaction.txHash}`}
orderDialogOpen={orderDialogOpen}
setOrderDialogOpen={setOrderDialogOpen}
finalizedOrder={finalizedOrder}
transaction={transaction}
reset={reset}
title={getDialogTitle(finalizedOrder?.status)}
/>
</>
);
};

View File

@ -1,6 +1,6 @@
import { FormGroup, Input } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { validateSize } from '../utils/validate-size';
import { validateSize } from '@vegaprotocol/orders';
import type { DealTicketAmountProps } from './deal-ticket-amount';
export type DealTicketMarketAmountProps = Omit<

View File

@ -1,7 +1,7 @@
import {
VegaWalletContext,
OrderTimeInForce,
OrderType,
VegaWalletOrderTimeInForce,
VegaWalletOrderType,
} from '@vegaprotocol/wallet';
import { addDecimal } from '@vegaprotocol/react-helpers';
import { fireEvent, render, screen, act } from '@testing-library/react';
@ -64,7 +64,7 @@ it('Displays ticket defaults', () => {
// Assert defaults are used
expect(
screen.getByTestId(`order-type-${OrderType.Market}-selected`)
screen.getByTestId(`order-type-${VegaWalletOrderType.Market}-selected`)
).toBeInTheDocument();
expect(
screen.queryByTestId('order-side-SIDE_BUY-selected')
@ -75,7 +75,9 @@ it('Displays ticket defaults', () => {
expect(screen.getByTestId('order-size')).toHaveDisplayValue(
String(1 / Math.pow(10, market.positionDecimalPlaces))
);
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.IOC);
expect(screen.getByTestId('order-tif')).toHaveValue(
VegaWalletOrderTimeInForce.IOC
);
// Assert last price is shown
expect(screen.getByTestId('last-price')).toHaveTextContent(
@ -101,9 +103,11 @@ it('Can edit deal ticket', async () => {
expect(screen.getByTestId('order-size')).toHaveDisplayValue('200');
fireEvent.change(screen.getByTestId('order-tif'), {
target: { value: OrderTimeInForce.IOC },
target: { value: VegaWalletOrderTimeInForce.IOC },
});
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.IOC);
expect(screen.getByTestId('order-tif')).toHaveValue(
VegaWalletOrderTimeInForce.IOC
);
// Switch to limit order
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
@ -113,7 +117,7 @@ it('Can edit deal ticket', async () => {
// Check all TIF options shown
expect(screen.getByTestId('order-tif').children).toHaveLength(
Object.keys(OrderTimeInForce).length
Object.keys(VegaWalletOrderTimeInForce).length
);
});
@ -130,26 +134,34 @@ it('Handles TIF select box dependent on order type', () => {
// Switch to limit order and check all TIF options shown
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
expect(screen.getByTestId('order-tif').children).toHaveLength(
Object.keys(OrderTimeInForce).length
Object.keys(VegaWalletOrderTimeInForce).length
);
// Change to GTC
fireEvent.change(screen.getByTestId('order-tif'), {
target: { value: OrderTimeInForce.GTC },
target: { value: VegaWalletOrderTimeInForce.GTC },
});
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.GTC);
expect(screen.getByTestId('order-tif')).toHaveValue(
VegaWalletOrderTimeInForce.GTC
);
// Switch back to market order and TIF should now be IOC
fireEvent.click(screen.getByTestId('order-type-TYPE_MARKET'));
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.IOC);
expect(screen.getByTestId('order-tif')).toHaveValue(
VegaWalletOrderTimeInForce.IOC
);
// Switch tif to FOK
fireEvent.change(screen.getByTestId('order-tif'), {
target: { value: OrderTimeInForce.FOK },
target: { value: VegaWalletOrderTimeInForce.FOK },
});
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.FOK);
expect(screen.getByTestId('order-tif')).toHaveValue(
VegaWalletOrderTimeInForce.FOK
);
// Change back to limit and check we are still on FOK
fireEvent.click(screen.getByTestId('order-type-TYPE_LIMIT'));
expect(screen.getByTestId('order-tif')).toHaveValue(OrderTimeInForce.FOK);
expect(screen.getByTestId('order-tif')).toHaveValue(
VegaWalletOrderTimeInForce.FOK
);
});

View File

@ -1,17 +1,19 @@
import { useCallback } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { OrderType, OrderTimeInForce } from '@vegaprotocol/wallet';
import {
VegaWalletOrderType,
VegaWalletOrderTimeInForce,
} from '@vegaprotocol/wallet';
import { t, addDecimal, toDecimal } from '@vegaprotocol/react-helpers';
import { Button, InputError } from '@vegaprotocol/ui-toolkit';
import { TypeSelector } from './type-selector';
import { SideSelector } from './side-selector';
import { DealTicketAmount } from './deal-ticket-amount';
import { TimeInForceSelector } from './time-in-force-selector';
import { useOrderValidation } from '../hooks/use-order-validation';
import type { DealTicketQuery_market } from './__generated__/DealTicketQuery';
import type { Order } from '../utils/get-default-order';
import { getDefaultOrder } from '../utils/get-default-order';
import { ExpirySelector } from './expiry-selector';
import type { Order } from '@vegaprotocol/orders';
import { getDefaultOrder, useOrderValidation } from '@vegaprotocol/orders';
export type TransactionStatus = 'default' | 'pending';
@ -97,8 +99,8 @@ export const DealTicket = ({
/>
)}
/>
{orderType === OrderType.Limit &&
orderTimeInForce === OrderTimeInForce.GTT && (
{orderType === VegaWalletOrderType.Limit &&
orderTimeInForce === VegaWalletOrderTimeInForce.GTT && (
<Controller
name="expiration"
control={control}

View File

@ -10,3 +10,4 @@ export * from './info-market';
export * from './side-selector';
export * from './time-in-force-selector';
export * from './type-selector';
export * from './market-selector';

View File

@ -0,0 +1,292 @@
import React, {
useCallback,
useState,
useEffect,
useRef,
useMemo,
} from 'react';
import { gql, useQuery } from '@apollo/client';
import classNames from 'classnames';
import type { DealTicketQuery_market } from './__generated__';
import {
Button,
Dialog,
Icon,
Input,
Loader,
Splash,
} from '@vegaprotocol/ui-toolkit';
import {
t,
useScreenDimensions,
useOutsideClick,
} from '@vegaprotocol/react-helpers';
import type {
MarketNames,
MarketNames_markets,
} from './__generated__/MarketNames';
import { IconNames } from '@blueprintjs/icons';
export const MARKET_NAMES_QUERY = gql`
query MarketNames {
markets {
id
name
tradableInstrument {
instrument {
metadata {
tags
}
product {
... on Future {
quoteName
}
}
}
}
}
}
`;
interface Props {
market: DealTicketQuery_market;
setMarket: (marketId: string) => void;
ItemRenderer?: React.FC<{ market: MarketNames_markets }>;
}
function escapeRegExp(str: string) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
export const MarketSelector = ({ market, setMarket, ItemRenderer }: Props) => {
const { isMobile } = useScreenDimensions();
const contRef = useRef<HTMLDivElement | null>(null);
const inputRef = useRef<HTMLInputElement | null>(null);
const arrowButtonRef = useRef<HTMLButtonElement | null>(null);
const [skip, setSkip] = useState(true);
const [results, setResults] = useState<MarketNames_markets[]>([]);
const [showPane, setShowPane] = useState(false);
const [lookup, setLookup] = useState(market.name || '');
const [dialogContent, setDialogContent] = useState<React.ReactNode | null>(
null
);
const { data, loading, error } = useQuery<MarketNames>(MARKET_NAMES_QUERY, {
skip,
});
const outsideClickCb = useCallback(() => {
if (!isMobile) {
setShowPane(false);
}
}, [setShowPane, isMobile]);
useOutsideClick({ refs: [contRef, arrowButtonRef], func: outsideClickCb });
const handleOnChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement>) => {
const {
target: { value },
} = event;
setLookup(value);
setShowPane(true);
if (value) {
setSkip(false);
}
},
[setLookup, setShowPane, setSkip]
);
const handleMarketSelect = useCallback(
({ id, name }) => {
setLookup(name);
setShowPane(false);
setMarket(id);
inputRef.current?.focus();
},
[setLookup, setShowPane, setMarket, inputRef]
);
const handleItemKeyDown = useCallback(
(
event: React.KeyboardEvent,
market: MarketNames_markets,
index: number
) => {
switch (event.key) {
case 'ArrowDown':
if (index < results.length - 1) {
(contRef.current?.children[index + 1] as HTMLDivElement).focus();
}
break;
case 'ArrowUp':
if (!index) {
inputRef.current?.focus();
inputRef.current?.setSelectionRange(
inputRef.current?.value.length,
inputRef.current?.value.length
);
return;
}
(contRef.current?.children[index - 1] as HTMLDivElement).focus();
break;
case 'Enter':
handleMarketSelect(market);
break;
}
},
[results, handleMarketSelect]
);
const handleInputKeyDown = useCallback(
(event: React.KeyboardEvent) => {
if (event.key === 'ArrowDown') {
(contRef.current?.children[0] as HTMLDivElement).focus();
}
},
[contRef]
);
const handleOnBlur = useCallback(() => {
console.log('lookup, showPane', lookup, showPane);
if (!lookup && !showPane) {
console.log(
'2 lookup, showPane, market.name',
lookup,
showPane,
market.name
);
setLookup(market.name);
}
}, [market, lookup, showPane, setLookup]);
const openPane = useCallback(() => {
setShowPane(!showPane);
setSkip(false);
inputRef.current?.focus();
}, [showPane, setShowPane, setSkip, inputRef]);
const handleDialogOnchange = useCallback(
(isOpen) => {
setShowPane(isOpen);
if (!isOpen) {
setLookup(lookup || market.name);
inputRef.current?.focus();
}
},
[setShowPane, lookup, setLookup, market.name, inputRef]
);
const selectorContent = useMemo(() => {
return (
<div className="relative flex flex-col">
<div className="relative w-full min-h-[30px] dark:bg-black">
<Input
className="h-[30px] w-[calc(100%-20px)] border-none dark:bg-black"
ref={inputRef}
tabIndex={0}
value={lookup}
placeholder={t('Search')}
onChange={handleOnChange}
onKeyDown={handleInputKeyDown}
onBlur={handleOnBlur}
/>
<Button
className="absolute self-end top-[7px] right-0 z-10"
variant="inline-link"
onClick={openPane}
ref={arrowButtonRef}
data-testid="arrow-button"
>
<Icon
name={IconNames.ARROW_DOWN}
className={classNames('fill-current transition-transform', {
'rotate-180': showPane,
})}
size={16}
/>
</Button>
</div>
<hr className="md:hidden mb-5" />
<div
className={classNames(
'md:absolute flex flex-col top-[30px] z-10 md:drop-shadow-md md:border-1 md:border-black md:dark:border-white bg-white dark:bg-black text-black dark:text-white min-w-full md:max-h-[200px] overflow-y-auto',
showPane ? 'block' : 'hidden'
)}
data-testid="market-pane"
>
{loading && <Loader />}
{error && (
<Splash>{t(`Something went wrong: ${error.message}`)}</Splash>
)}
<div ref={contRef} className="w-full">
{results.map((market, i) => (
<div
role="button"
tabIndex={0}
key={market.id}
className="cursor-pointer focus:bg-white-95 focus:outline-0 dark:focus:bg-black-80 px-20 py-5"
onClick={() => handleMarketSelect(market)}
onKeyDown={(e) => handleItemKeyDown(e, market, i)}
>
{ItemRenderer ? <ItemRenderer market={market} /> : market.name}
</div>
))}
</div>
</div>
</div>
);
}, [
ItemRenderer,
error,
handleInputKeyDown,
handleItemKeyDown,
handleMarketSelect,
handleOnBlur,
handleOnChange,
loading,
lookup,
openPane,
results,
showPane,
]);
useEffect(() => {
setResults(
data?.markets?.filter((item: MarketNames_markets) =>
item.name.match(new RegExp(escapeRegExp(lookup), 'i'))
) || []
);
}, [data, lookup]);
useEffect(() => {
inputRef.current?.focus();
}, [inputRef]);
useEffect(() => {
if (showPane && isMobile) {
setDialogContent(selectorContent);
inputRef.current?.focus();
window.scrollTo(0, 0);
} else {
setDialogContent(null);
}
}, [selectorContent, showPane, isMobile, setDialogContent]);
return (
<>
{!dialogContent && selectorContent}
<Dialog
titleClassNames="uppercase font-alpha"
contentClassNames="left-[0px] top-[99px] h-[calc(100%-99px)] border-0 translate-x-[0] translate-y-[0] border-none overflow-y-auto"
title={t('Select Market')}
open={Boolean(dialogContent)}
onChange={handleDialogOnchange}
>
{dialogContent}
</Dialog>
</>
);
};

View File

@ -1,16 +1,16 @@
import { FormGroup } from '@vegaprotocol/ui-toolkit';
import { OrderSide } from '@vegaprotocol/wallet';
import { VegaWalletOrderSide } from '@vegaprotocol/wallet';
import { Toggle } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
interface SideSelectorProps {
value: OrderSide;
onSelect: (side: OrderSide) => void;
value: VegaWalletOrderSide;
onSelect: (side: VegaWalletOrderSide) => void;
}
export const SideSelector = ({ value, onSelect }: SideSelectorProps) => {
const toggles = Object.entries(OrderSide).map(([label, value]) => ({
label,
const toggles = Object.entries(VegaWalletOrderSide).map(([label, value]) => ({
label: label === 'Buy' ? 'Long' : 'Short',
value,
}));
@ -21,7 +21,7 @@ export const SideSelector = ({ value, onSelect }: SideSelectorProps) => {
name="order-side"
toggles={toggles}
checkedValue={value}
onChange={(e) => onSelect(e.target.value as OrderSide)}
onChange={(e) => onSelect(e.target.value as VegaWalletOrderSide)}
/>
</FormGroup>
);

View File

@ -1,11 +1,14 @@
import { FormGroup, Select } from '@vegaprotocol/ui-toolkit';
import { OrderTimeInForce, OrderType } from '@vegaprotocol/wallet';
import {
VegaWalletOrderTimeInForce,
VegaWalletOrderType,
} from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/react-helpers';
interface TimeInForceSelectorProps {
value: OrderTimeInForce;
orderType: OrderType;
onSelect: (tif: OrderTimeInForce) => void;
value: VegaWalletOrderTimeInForce;
orderType: VegaWalletOrderType;
onSelect: (tif: VegaWalletOrderTimeInForce) => void;
}
export const TimeInForceSelector = ({
@ -14,12 +17,12 @@ export const TimeInForceSelector = ({
onSelect,
}: TimeInForceSelectorProps) => {
const options =
orderType === OrderType.Limit
? Object.entries(OrderTimeInForce)
: Object.entries(OrderTimeInForce).filter(
orderType === VegaWalletOrderType.Limit
? Object.entries(VegaWalletOrderTimeInForce)
: Object.entries(VegaWalletOrderTimeInForce).filter(
([_, timeInForce]) =>
timeInForce === OrderTimeInForce.FOK ||
timeInForce === OrderTimeInForce.IOC
timeInForce === VegaWalletOrderTimeInForce.FOK ||
timeInForce === VegaWalletOrderTimeInForce.IOC
);
return (
@ -27,7 +30,7 @@ export const TimeInForceSelector = ({
<Select
id="select-time-in-force"
value={value}
onChange={(e) => onSelect(e.target.value as OrderTimeInForce)}
onChange={(e) => onSelect(e.target.value as VegaWalletOrderTimeInForce)}
className="w-full"
data-testid="order-tif"
>

View File

@ -1,14 +1,14 @@
import { FormGroup } from '@vegaprotocol/ui-toolkit';
import { t } from '@vegaprotocol/react-helpers';
import { OrderType } from '@vegaprotocol/wallet';
import { VegaWalletOrderType } from '@vegaprotocol/wallet';
import { Toggle } from '@vegaprotocol/ui-toolkit';
interface TypeSelectorProps {
value: OrderType;
onSelect: (type: OrderType) => void;
value: VegaWalletOrderType;
onSelect: (type: VegaWalletOrderType) => void;
}
const toggles = Object.entries(OrderType).map(([label, value]) => ({
const toggles = Object.entries(VegaWalletOrderType).map(([label, value]) => ({
label,
value,
}));
@ -21,7 +21,7 @@ export const TypeSelector = ({ value, onSelect }: TypeSelectorProps) => {
name="order-type"
toggles={toggles}
checkedValue={value}
onChange={(e) => onSelect(e.target.value as OrderType)}
onChange={(e) => onSelect(e.target.value as VegaWalletOrderType)}
/>
</FormGroup>
);

View File

@ -1,114 +0,0 @@
/* tslint:disable */
/* eslint-disable */
// @generated
// This file was automatically generated and should not be edited.
import { BusEventType, OrderType, OrderStatus, OrderRejectionReason } from "@vegaprotocol/types";
// ====================================================
// GraphQL subscription operation: OrderEvent
// ====================================================
export interface OrderEvent_busEvents_event_TimeUpdate {
__typename: "TimeUpdate" | "MarketEvent" | "TransferResponses" | "PositionResolution" | "Trade" | "Account" | "Party" | "MarginLevels" | "Proposal" | "Vote" | "MarketData" | "NodeSignature" | "LossSocialization" | "SettlePosition" | "Market" | "Asset" | "MarketTick" | "SettleDistressed" | "AuctionEvent" | "RiskFactor" | "Deposit" | "Withdrawal" | "OracleSpec" | "LiquidityProvision";
}
export interface OrderEvent_busEvents_event_Order_market {
__typename: "Market";
/**
* Market full name
*/
name: string;
/**
* decimalPlaces indicates the number of decimal places that an integer must be shifted by in order to get a correct
* number denominated in the currency of the Market. (uint64)
*
* Examples:
* Currency Balance decimalPlaces Real Balance
* GBP 100 0 GBP 100
* GBP 100 2 GBP 1.00
* GBP 100 4 GBP 0.01
* GBP 1 4 GBP 0.0001 ( 0.01p )
*
* GBX (pence) 100 0 GBP 1.00 (100p )
* GBX (pence) 100 2 GBP 0.01 ( 1p )
* GBX (pence) 100 4 GBP 0.0001 ( 0.01p )
* GBX (pence) 1 4 GBP 0.000001 ( 0.0001p)
*/
decimalPlaces: number;
/**
* positionDecimalPlaces indicated the number of decimal places that an integer must be shifted in order to get a correct size (uint64).
* i.e. 0 means there are no fractional orders for the market, and order sizes are always whole sizes.
* 2 means sizes given as 10^2 * desired size, e.g. a desired size of 1.23 is represented as 123 in this market.
*/
positionDecimalPlaces: number;
}
export interface OrderEvent_busEvents_event_Order {
__typename: "Order";
/**
* Type the order type (defaults to PARTY)
*/
type: OrderType | null;
/**
* Hash of the order data
*/
id: string;
/**
* The status of an order, for example 'Active'
*/
status: OrderStatus;
/**
* Reason for the order to be rejected
*/
rejectionReason: OrderRejectionReason | null;
/**
* RFC3339Nano formatted date and time for when the order was created (timestamp)
*/
createdAt: string;
/**
* Total number of contracts that may be bought or sold (immutable) (uint64)
*/
size: string;
/**
* The worst price the order will trade at (e.g. buy for price or less, sell for price or more) (uint64)
*/
price: string;
/**
* The market the order is trading on (probably stored internally as a hash of the market details)
*/
market: OrderEvent_busEvents_event_Order_market | null;
}
export type OrderEvent_busEvents_event = OrderEvent_busEvents_event_TimeUpdate | OrderEvent_busEvents_event_Order;
export interface OrderEvent_busEvents {
__typename: "BusEvent";
/**
* the id for this event
*/
eventId: string;
/**
* the block hash
*/
block: string;
/**
* the type of event we're dealing with
*/
type: BusEventType;
/**
* the payload - the wrapped event
*/
event: OrderEvent_busEvents_event;
}
export interface OrderEvent {
/**
* Subscribe to event data from the event bus
*/
busEvents: OrderEvent_busEvents[] | null;
}
export interface OrderEventVariables {
partyId: string;
}

View File

@ -1,176 +0,0 @@
import { MockedProvider } from '@apollo/client/testing';
import { act, renderHook } from '@testing-library/react-hooks';
import type { Order } from '../utils/get-default-order';
import type {
VegaKeyExtended,
VegaWalletContextShape,
} from '@vegaprotocol/wallet';
import { VegaTxStatus, VegaWalletContext } from '@vegaprotocol/wallet';
import { OrderSide, OrderTimeInForce, OrderType } from '@vegaprotocol/wallet';
import { MarketState, MarketTradingMode } from '@vegaprotocol/types';
import type { ReactNode } from 'react';
import { useOrderSubmit } from './use-order-submit';
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
const defaultMarket: DealTicketQuery_market = {
__typename: 'Market',
id: 'market-id',
decimalPlaces: 2,
positionDecimalPlaces: 1,
tradingMode: MarketTradingMode.Continuous,
state: MarketState.Active,
tradableInstrument: {
__typename: 'TradableInstrument',
instrument: {
__typename: 'Instrument',
product: {
__typename: 'Future',
quoteName: 'quote-name',
},
},
},
depth: {
__typename: 'MarketDepth',
lastTrade: {
__typename: 'Trade',
price: '100',
},
},
};
const defaultWalletContext = {
keypair: null,
keypairs: [],
sendTx: jest.fn().mockReturnValue(Promise.resolve(null)),
connect: jest.fn(),
disconnect: jest.fn(),
selectPublicKey: jest.fn(),
connector: null,
};
function setup(
context?: Partial<VegaWalletContextShape>,
market = defaultMarket
) {
const wrapper = ({ children }: { children: ReactNode }) => (
<MockedProvider>
<VegaWalletContext.Provider
value={{ ...defaultWalletContext, ...context }}
>
{children}
</VegaWalletContext.Provider>
</MockedProvider>
);
return renderHook(() => useOrderSubmit(market), { wrapper });
}
it('Has the correct default state', () => {
const { result } = setup();
expect(typeof result.current.submit).toEqual('function');
expect(typeof result.current.reset).toEqual('function');
expect(result.current.transaction.status).toEqual(VegaTxStatus.Default);
expect(result.current.transaction.txHash).toEqual(null);
expect(result.current.transaction.error).toEqual(null);
});
it('Should not sendTx if no keypair', async () => {
const mockSendTx = jest.fn();
const { result } = setup({ sendTx: mockSendTx, keypairs: [], keypair: null });
await act(async () => {
result.current.submit({} as Order);
});
expect(mockSendTx).not.toHaveBeenCalled();
});
it('Should not sendTx side is not specified', async () => {
const mockSendTx = jest.fn();
const keypair = {
pub: '0x123',
} as VegaKeyExtended;
const { result } = setup({
sendTx: mockSendTx,
keypairs: [keypair],
keypair,
});
await act(async () => {
result.current.submit({} as Order);
});
expect(mockSendTx).not.toHaveBeenCalled();
});
it('Create an Id if a signature is returned', async () => {
const signature =
'597a7706491e6523c091bab1e4d655b62c45a224e80f6cd92ac366aa5dd9a070cc7dd3c6919cb07b81334b876c662dd43bdbe5e827c8baa17a089feb654fab0b';
const expectedId =
'2fe09b0e2e6ed35f8883802629c7d609d3cc2fc9ce3cec0b7824a0d581bd3747';
const successObj = {
tx: {
inputData: 'input-data',
signature: {
algo: 'algo',
version: 1,
value: signature,
},
},
txHash: '0x123',
};
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve(successObj));
const keypair = {
pub: '0x123',
} as VegaKeyExtended;
const { result } = setup({
sendTx: mockSendTx,
keypairs: [keypair],
keypair,
});
await act(async () => {
result.current.submit({
type: OrderType.Market,
side: OrderSide.Buy,
size: '1',
timeInForce: OrderTimeInForce.FOK,
});
});
expect(result.current.id).toEqual(expectedId);
});
it('Should submit a correctly formatted order', async () => {
const mockSendTx = jest.fn().mockReturnValue(Promise.resolve({}));
const keypair = {
pub: '0x123',
} as VegaKeyExtended;
const { result } = setup(
{
sendTx: mockSendTx,
keypairs: [keypair],
keypair,
},
defaultMarket
);
const order: Order = {
type: OrderType.Limit,
size: '10',
timeInForce: OrderTimeInForce.GTT,
side: OrderSide.Buy,
price: '1234567.89',
expiration: new Date('2022-01-01'),
};
await act(async () => {
result.current.submit(order);
});
expect(mockSendTx).toHaveBeenCalledWith({
pubKey: keypair.pub,
propagate: true,
orderSubmission: {
type: OrderType.Limit,
marketId: defaultMarket.id, // Market provided from hook arugment
size: '100', // size adjusted based on positionDecimalPlaces
side: OrderSide.Buy,
timeInForce: OrderTimeInForce.GTT,
price: '123456789', // Decimal removed
expiresAt: order.expiration?.getTime() + '000000', // Nanoseconds appened
},
});
});

View File

@ -1,102 +0,0 @@
import { useCallback, useState } from 'react';
import { useSubscription } from '@apollo/client';
import type { Order } from '../utils/get-default-order';
import type {
OrderEvent,
OrderEventVariables,
OrderEvent_busEvents_event_Order,
} from '@vegaprotocol/wallet';
import {
OrderType,
useVegaWallet,
ORDER_EVENT_SUB,
} from '@vegaprotocol/wallet';
import { determineId, removeDecimal } from '@vegaprotocol/react-helpers';
import { useVegaTransaction } from '@vegaprotocol/wallet';
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
export const useOrderSubmit = (market: DealTicketQuery_market) => {
const { keypair } = useVegaWallet();
const { send, transaction, reset: resetTransaction } = useVegaTransaction();
const [id, setId] = useState('');
const [finalizedOrder, setFinalizedOrder] =
useState<OrderEvent_busEvents_event_Order | null>(null);
// Start a subscription looking for the newly created order
useSubscription<OrderEvent, OrderEventVariables>(ORDER_EVENT_SUB, {
variables: { partyId: keypair?.pub || '' },
skip: !id,
onSubscriptionData: ({ subscriptionData }) => {
if (!subscriptionData.data?.busEvents?.length) {
return;
}
// No types available for the subscription result
const matchingOrderEvent = subscriptionData.data.busEvents.find((e) => {
if (e.event.__typename !== 'Order') {
return false;
}
return e.event.id === id;
});
if (
matchingOrderEvent &&
matchingOrderEvent.event.__typename === 'Order'
) {
setFinalizedOrder(matchingOrderEvent.event);
resetTransaction();
}
},
});
const submit = useCallback(
async (order: Order) => {
if (!keypair || !order.side) {
return;
}
setFinalizedOrder(null);
const res = await send({
pubKey: keypair.pub,
propagate: true,
orderSubmission: {
marketId: market.id,
price:
order.type === OrderType.Limit && order.price
? removeDecimal(order.price, market.decimalPlaces)
: undefined,
size: removeDecimal(order.size, market.positionDecimalPlaces),
type: order.type,
side: order.side,
timeInForce: order.timeInForce,
expiresAt: order.expiration
? // Wallet expects timestamp in nanoseconds, we don't have that level of accuracy so
// just append 6 zeroes
order.expiration.getTime().toString() + '000000'
: undefined,
},
});
if (res?.signature) {
setId(determineId(res.signature));
}
},
[market, keypair, send]
);
const reset = useCallback(() => {
resetTransaction();
setFinalizedOrder(null);
setId('');
}, [resetTransaction]);
return {
transaction,
finalizedOrder,
id,
submit,
reset,
};
};

View File

@ -1,4 +1 @@
export * from './components';
export * from './utils/get-default-order';
export * from './hooks/use-order-submit';
export * from './hooks/use-order-validation';

View File

@ -1,28 +0,0 @@
import { OrderTimeInForce, OrderType, OrderSide } from '@vegaprotocol/wallet';
import { toDecimal } from '@vegaprotocol/react-helpers';
import type { DealTicketQuery_market } from '../components/__generated__/DealTicketQuery';
export type Order =
| {
size: string;
type: OrderType.Market;
timeInForce: OrderTimeInForce;
side: OrderSide;
price?: never;
expiration?: never;
}
| {
size: string;
type: OrderType.Limit;
timeInForce: OrderTimeInForce;
side: OrderSide;
price?: string;
expiration?: Date;
};
export const getDefaultOrder = (market: DealTicketQuery_market): Order => ({
type: OrderType.Market,
side: OrderSide.Buy,
timeInForce: OrderTimeInForce.IOC,
size: String(toDecimal(market.positionDecimalPlaces)),
});

Some files were not shown because too many files have changed in this diff Show More