diff --git a/apps/explorer-e2e/src/integration/asset.cy.js b/apps/explorer-e2e/src/integration/asset.cy.js index 4838c1699..90e9da744 100644 --- a/apps/explorer-e2e/src/integration/asset.cy.js +++ b/apps/explorer-e2e/src/integration/asset.cy.js @@ -1,20 +1,148 @@ -import '../support/common.functions'; +context('Asset page', { tags: '@regression' }, function () { + before('gather system asset information', function () { + cy.get_asset_information().as('assetsInfo'); + }); -context('Asset page', function () { describe('Verify elements on page', function () { const assetsNavigation = 'a[href="/assets"]'; const assetHeader = '[data-testid="asset-header"]'; + const jsonSection = '.language-json'; - it('Assets page is displayed', function () { + before('Navigate to assets page', function () { cy.visit('/'); cy.get(assetsNavigation).click(); - cy.common_validate_blocks_data_displayed(assetHeader); + + // Check we have enough enough assets + const assetNames = Object.keys(this.assetsInfo); + assert.isAtLeast( + assetNames.length, + 5, + 'Ensuring we have at least 5 assets to test' + ); }); - it('Assets page displayed in mobile', function () { + it('should be able to see assets page sections', function () { + const assetNames = Object.keys(this.assetsInfo); + assetNames.forEach((assetName) => { + cy.get(assetHeader) + .contains(assetName) + .should('be.visible') + .next() + .within(() => { + cy.get(jsonSection).should('not.be.empty'); + }); + }); + }); + + it('should be able to see all asset details displayed in JSON', function () { + const assetNames = Object.keys(this.assetsInfo); + assetNames.forEach((assetName) => { + cy.get(assetHeader) + .contains(assetName) + .next() + .within(() => { + cy.get(jsonSection) + .invoke('text') + .convert_string_json_to_js_object() + .then((assetsListedInJson) => { + const assetInfo = this.assetsInfo[assetName]; + + assert.equal(assetsListedInJson.name, assetInfo.name); + assert.equal(assetsListedInJson.id, assetInfo.id); + assert.equal(assetsListedInJson.decimals, assetInfo.decimals); + assert.equal(assetsListedInJson.symbol, assetInfo.symbol); + assert.equal( + assetsListedInJson.source.__typename, + assetInfo.source.__typename + ); + + if (assetInfo.source.__typename == 'ERC20') { + assert.equal( + assetsListedInJson.source.contractAddress, + assetInfo.source.contractAddress + ); + } + + if (assetInfo.source.__typename == 'BuiltinAsset') { + assert.equal( + assetsListedInJson.source.maxFaucetAmountMint, + assetInfo.source.maxFaucetAmountMint + ); + } + + let knownAssetTypes = ['BuiltinAsset', 'ERC20']; + assert.include( + knownAssetTypes, + assetInfo.source.__typename, + `Checking that current asset type of ${assetInfo.source.__typename} / + is one of: ${knownAssetTypes}: / + If fail then we need to add extra tests for un-encountered asset types` + ); + }); + }); + }); + }); + + it('should be able to switch assets between light and dark mode', function () { + const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)'; + const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)'; + const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)'; + const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)'; + const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)'; + const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)'; + const themeSwitcher = '[data-testid="theme-switcher"]'; + const jsonFields = '.hljs'; + const sideMenuBackground = '.absolute'; + + // Engage dark mode if not allready set + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .then((background_color) => { + if (background_color.includes(whiteThemeSideMenuBackgroundColor)) + cy.get(themeSwitcher).click(); + }); + + // Engage white mode + cy.get(themeSwitcher).click(); + + // White Mode + cy.get(assetsNavigation) + .should('have.css', 'background-color') + .and('include', whiteThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', whiteThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', whiteThemeSideMenuBackgroundColor); + + // Dark Mode + cy.get(themeSwitcher).click(); + cy.get(assetsNavigation) + .should('have.css', 'background-color') + .and('include', darkThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', darkThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', darkThemeSideMenuBackgroundColor); + }); + + it('should be able to see 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); + + const assetNames = Object.keys(this.assetsInfo); + assetNames.forEach((assetName) => { + cy.get(assetHeader) + .contains(assetName) + .should('be.visible') + .next() + .within(() => { + cy.get(jsonSection).should('not.be.empty'); + }); + }); }); }); }); diff --git a/apps/explorer-e2e/src/integration/network.cy.js b/apps/explorer-e2e/src/integration/network.cy.js index 4898bc6bb..0e8f06af3 100644 --- a/apps/explorer-e2e/src/integration/network.cy.js +++ b/apps/explorer-e2e/src/integration/network.cy.js @@ -1,35 +1,267 @@ -import '../support/common.functions'; - -context('Network parameters page', function () { - before('visit token home page', function () { - cy.visit('/'); +context('Network parameters page', { tags: '@smoke' }, function () { + before('navigate to network parameter page', function () { + cy.fixture('net_parameter_format_lookup').as('networkParameterFormat'); }); describe('Verify elements on page', function () { const networkParametersNavigation = 'a[href="/network-parameters"]'; + const networkParametersHeader = '[data-testid="network-param-header"]'; + const tableRows = '[data-testid="key-value-table-row"]'; - beforeEach('Navigate to network parameter page', function () { + before('navigate to network parameter page', function () { + cy.visit('/'); cy.get(networkParametersNavigation).click(); }); - it('Network paremeter page is displayed', function () { - verifyNetworkParametersPageDisplayed(); + it('should show network parameter heading at top of page', function () { + cy.get(networkParametersHeader) + .should('have.text', 'Network Parameters') + .and('be.visible'); }); - it('Network parameter page displayed on mobile', function () { + it('should list each of the network parameters available', function () { + cy.get_network_parameters().then((network_parameters) => { + const numberOfNetworkParametersInSystem = + Object.keys(network_parameters).length; + cy.get(tableRows).should( + 'have.length', + numberOfNetworkParametersInSystem + ); + }); + }); + + it('should list each network parameter displayed with json - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if (this.networkParameterFormat.json.includes(parameterName)) { + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .invoke('text') + .convert_string_json_to_js_object() + .then((jsonOnPage) => { + cy.wrap(parameterValue, { log: false }) + .convert_string_json_to_js_object() + .then((jsonInSystem) => + assert.deepEqual( + jsonOnPage, + jsonInSystem, + `Checking ${parameterName} has the correct value of ${jsonInSystem}` + ) + ); + }); + } + }); + }); + }); + + it('should list each network parameter displayed as a percentage - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if (this.networkParameterFormat.percentage.includes(parameterName)) { + const formattedPercentageParameter = + (parseFloat(parameterValue) * 100).toFixed(0) + '%'; + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .invoke('text') + .then((parameterValueOnPage) => { + assert.equal( + parameterValueOnPage, + formattedPercentageParameter, + `Checking ${parameterName} has the correct value of ${formattedPercentageParameter}` + ); + cy.contains(parameterValueOnPage).should('be.visible'); + }); + } + }); + }); + }); + + it('should list each network parameter displayed as an id - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if (this.networkParameterFormat.id.includes(parameterName)) { + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .should('contain', parameterValue) + .and('be.visible') + .invoke('text'); + } + }); + }); + }); + + it('should list each network parameter displayed as a date - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if (this.networkParameterFormat.date.includes(parameterName)) { + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .should('contain', parameterValue) + .and('be.visible'); + } + }); + }); + }); + + it('should list each network parameter displayed as a duration - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if (this.networkParameterFormat.duration.includes(parameterName)) { + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .should('contain', parameterValue) + .and('be.visible'); + } + }); + }); + }); + + it('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if (this.networkParameterFormat.fiveDecimal.includes(parameterName)) { + cy.convert_number_to_four_decimal(parameterValue) + .add_commas_to_number_if_large_enough() + .then((parameterValueFormatted) => { + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .invoke('text') + .then((parameterValueOnPage) => { + assert.equal( + parameterValueOnPage, + parameterValueFormatted, + `Checking ${parameterName} has the correct value of ${parameterValueFormatted}` + ); + cy.contains(parameterValueOnPage).should('be.visible'); + }); + }); + } + }); + }); + }); + + it('should list each network parameter displayed as a currency value with eighteen decimals - in the correct format', function () { + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + const parameterValue = network_parameter[1]; + if ( + this.networkParameterFormat.eighteenDecimal.includes(parameterName) + ) { + cy.convert_number_to_eighteen_decimal(parameterValue) + .add_commas_to_number_if_large_enough() + .then((parameterValueFormatted) => { + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .invoke('text') + .then((parameterValueOnPage) => { + assert.equal( + parameterValueOnPage, + parameterValueFormatted, + `Checking ${parameterName} has the correct value of ${parameterValueFormatted}` + ); + cy.contains(parameterValueOnPage).should('be.visible'); + }); + }); + } + }); + }); + }); + + it('should be able to switch network parameter page - between light and dark mode', function () { + const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)'; + const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)'; + const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)'; + const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)'; + const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)'; + const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)'; + const themeSwitcher = '[data-testid="theme-switcher"]'; + const jsonFields = '.hljs'; + const sideMenuBackground = '.absolute'; + + // Engage dark mode if not allready set + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .then((background_color) => { + if (background_color.includes(whiteThemeSideMenuBackgroundColor)) + cy.get(themeSwitcher).click(); + }); + + // Engage white mode + cy.get(themeSwitcher).click(); + + // White Mode + cy.get(networkParametersNavigation) + .should('have.css', 'background-color') + .and('include', whiteThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', whiteThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', whiteThemeSideMenuBackgroundColor); + + // Dark Mode + cy.get(themeSwitcher).click(); + cy.get(networkParametersNavigation) + .should('have.css', 'background-color') + .and('include', darkThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', darkThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', darkThemeSideMenuBackgroundColor); + }); + + it('should be able to see network parameters - on mobile', function () { cy.common_switch_to_mobile_and_click_toggle(); cy.get(networkParametersNavigation).click(); - verifyNetworkParametersPageDisplayed(); + cy.get_network_parameters().then((network_parameters) => { + network_parameters = Object.entries(network_parameters); + network_parameters.forEach((network_parameter) => { + const parameterName = network_parameter[0]; + cy.get(tableRows) + .contains(parameterName) + .should('be.visible') + .next() + .should('not.be.empty') + .and('be.visible'); + }); + }); }); }); - - 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); - } }); diff --git a/apps/explorer-e2e/src/integration/parties.cy.js b/apps/explorer-e2e/src/integration/parties.cy.js new file mode 100644 index 000000000..21a85e2b6 --- /dev/null +++ b/apps/explorer-e2e/src/integration/parties.cy.js @@ -0,0 +1,264 @@ +const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey'); +const partiesMenuHeader = 'a[href="/parties"]'; +const partiesSearchBox = '[data-testid="party-input"]'; +const partiesSearchAction = '[data-testid="go-submit"]'; +const partiesJsonSection = '[data-testid="parties-json"]'; +const txTimeout = Cypress.env('txTimeout'); + +let assetData = { + fUSDC: { id: 'fUSDC', name: 'USDC (fake)', amount: '10' }, + fBTC: { id: 'fBTC', name: 'BTC (fake)', amount: '6' }, + fEURO: { id: 'fEURO', name: 'EURO (fake)', amount: '8' }, + fDAI: { id: 'fDAI', name: 'DAI (fake)', amount: '2' }, +}; + +const assetsInTest = Object.keys(assetData); + +context('Parties page', { tags: '@regression' }, function () { + before('send-faucet assets to connected vega wallet', function () { + cy.vega_wallet_import(); + assetsInTest.forEach((asset) => { + cy.vega_wallet_receive_fauceted_asset( + assetData[asset].name, + assetData[asset].amount, + vegaWalletPublicKey + ); + }); + cy.visit('/'); + }); + + describe('Verify parties page content', function () { + before('navigate to parties page and search for party', function () { + cy.get(partiesMenuHeader).click(); + // Deliberate slow entry of party id/key - enabling transactions to sync + cy.get(partiesSearchBox).type(vegaWalletPublicKey, { delay: 120 }); + cy.get(partiesSearchAction).click(); + cy.get_connected_parties_accounts().as('party_accounts'); + + // Ensure balance of each party asset is correct + cy.get('@party_accounts').then((accounts) => { + assetsInTest.forEach((asset) => { + cy.get_asset_decimals(assetData[asset].id).then((assetDecimals) => { + assert.equal( + accounts[assetData[asset].id].balance, + assetData[asset].amount + assetDecimals, + `Checking ${assetData[asset].id} faucet was successfull` + ); + }); + }); + }); + }); + + it('should see party address id - having searched', function () { + cy.getByTestId('parties-header') + .siblings() + .contains(vegaWalletPublicKey.substring(0, 14)) + .should('be.visible'); + }); + + it('should see each asset and balance - within asset data section', function () { + assetsInTest.forEach((asset) => { + cy.contains(assetData[asset].name, txTimeout).should('be.visible'); + cy.contains(assetData[asset].name) + .siblings() + .contains(assetData[asset].id) + .should('be.visible'); + cy.contains(assetData[asset].name, txTimeout) + .parent() + .siblings() + .within(() => { + cy.get_asset_decimals(asset).then((assetDecimals) => { + cy.contains_exactly( + assetData[asset].amount + '.' + assetDecimals + ).should('be.visible'); + }); + }); + }); + }); + + it( + 'should be able to copy the party address id', + { browser: 'chrome' }, + function () { + cy.monitor_clipboard().as('clipboard'); + cy.getByTestId('parties-header') + .next() + .within(() => { + cy.get('button').click(); + }); + cy.get('@clipboard') + .get_copied_text_from_clipboard() + .should('equal', vegaWalletPublicKey); + } + ); + + it( + 'should be able to copy an asset id', + { browser: 'chrome' }, + function () { + cy.monitor_clipboard().as('clipboard'); + + cy.contains(assetData.fDAI.name, txTimeout).should('be.visible'); + cy.contains(assetData.fDAI.name) + .parent() + .parent() + .siblings() + .within(() => { + cy.get('[data-state="closed"]').last().click({ force: true }); + }); + + cy.get('@clipboard') + .get_copied_text_from_clipboard() + .should('equal', assetData.fDAI.id); + } + ); + + it('should be able to see JSON of each asset containing correct balance and decimals', function () { + cy.get(partiesJsonSection).should('be.visible'); + + assetsInTest.forEach((assetInTest) => { + cy.get(partiesJsonSection) + .invoke('text') + .convert_string_json_to_js_object() + .get_party_accounts_data_from_js_object() + .then((accountsListedInJson) => { + cy.get_asset_information().then((assetsInfo) => { + const assetInfo = + assetsInfo[accountsListedInJson[assetInTest].asset.name]; + + assert.equal( + accountsListedInJson[assetInTest].asset.name, + assetInfo.name + ); + assert.equal( + accountsListedInJson[assetInTest].asset.id, + assetInfo.id + ); + assert.equal( + accountsListedInJson[assetInTest].asset.decimals, + assetInfo.decimals + ); + assert.equal( + accountsListedInJson[assetInTest].asset.symbol, + assetInfo.symbol + ); + assert.equal( + accountsListedInJson[assetInTest].asset.source.__typename, + assetInfo.source.__typename + ); + cy.get_asset_decimals(assetInTest).then((assetDecimals) => { + assert.equal( + accountsListedInJson[assetInTest].balance, + assetData[assetInTest].amount + assetDecimals + ); + }); + }); + }); + }); + }); + + it('should be able to switch parties page between light and dark mode', function () { + const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)'; + const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)'; + const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)'; + const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)'; + const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)'; + const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)'; + const themeSwitcher = '[data-testid="theme-switcher"]'; + const jsonFields = '.hljs'; + const sideMenuBackground = '.absolute'; + + // Engage dark mode if not allready set + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .then((background_color) => { + if (background_color.includes(whiteThemeSideMenuBackgroundColor)) + cy.get(themeSwitcher).click(); + }); + + // Engage white mode + cy.get(themeSwitcher).click(); + + // White Mode + cy.get(partiesMenuHeader) + .should('have.css', 'background-color') + .and('include', whiteThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', whiteThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', whiteThemeSideMenuBackgroundColor); + + // Dark Mode + cy.get(themeSwitcher).click(); + cy.get(partiesMenuHeader) + .should('have.css', 'background-color') + .and('include', darkThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', darkThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', darkThemeSideMenuBackgroundColor); + }); + + after( + 'teardown environment to prevent test data bleeding into other tests', + function () { + if (Cypress.env('TEARDOWN_NETWORK_AFTER_FLOWS')) { + cy.restart_vegacapsule_network(); + } + } + ); + + Cypress.Commands.add('get_asset_decimals', (assetID) => { + cy.get_asset_information().then((assetsInfo) => { + const assetDecimals = assetsInfo[assetData[assetID].name].decimals; + let decimals = ''; + for (let i = 0; i < assetDecimals; i++) decimals += '0'; + return decimals; + }); + }); + + Cypress.Commands.add('get_connected_parties_accounts', () => { + const mutation = + '{partiesConnection {edges{node{accountsConnection{edges{node{\ + balance type asset {id symbol decimals}}}}}}}}'; + cy.request({ + method: 'POST', + url: `http://localhost:3028/query`, + body: { + query: mutation, + }, + headers: { 'content-type': 'application/json' }, + }) + .its( + `body.data.partiesConnection.edges.1.node.accountsConnection.edges` + ) + .then(function (response) { + let accounts = {}; + response.forEach((account) => { + accounts[account.node.asset.id] = account.node; + }); + return accounts; + }); + }); + + Cypress.Commands.add( + 'get_party_accounts_data_from_js_object', + { prevSubject: true }, + (jsObject) => { + const accounts = jsObject.party.accounts.reduce(function ( + account, + entry + ) { + account[entry.asset.id] = entry; + return account; + }, + {}); + return accounts; + } + ); + }); +}); diff --git a/apps/explorer-e2e/src/integration/validator.cy.js b/apps/explorer-e2e/src/integration/validator.cy.js index 1529670b4..f4f4ca3ac 100644 --- a/apps/explorer-e2e/src/integration/validator.cy.js +++ b/apps/explorer-e2e/src/integration/validator.cy.js @@ -1,27 +1,300 @@ -import '../support/common.functions'; +context('Validator page', { tags: '@smoke' }, function () { + const validatorMenuHeading = 'a[href="/validators"]'; + const tendermintDataHeader = '[data-testid="tendermint-header"]'; + const vegaDataHeader = '[data-testid="vega-header"]'; + const jsonSection = '.language-json'; + + before('Visit validators page and obtain data', function () { + cy.visit('/'); + cy.get(validatorMenuHeading).click(); + cy.get_validators().as('validators'); + cy.get_nodes().as('nodes'); + }); -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' + before('Ensure at least two validators are present', function () { + assert.isAtLeast( + this.validators.length, + 2, + 'Ensuring at least two validators exist' ); - cy.get('[data-testid="tendermint-data"]').should('not.be.empty'); - } + }); + + it('should be able to see validator page sections', function () { + cy.get(vegaDataHeader) + .contains('Vega data') + .and('is.visible') + .next() + .within(() => { + cy.get(jsonSection).should('not.be.empty'); + }); + + cy.get(tendermintDataHeader) + .contains('Tendermint data') + .and('is.visible') + .next() + .within(() => { + cy.get(jsonSection).should('not.be.empty'); + }); + }); + + it('should be able to see relevant validator information in tendermint section', function () { + cy.get(tendermintDataHeader) + .contains('Tendermint data') + .next() + .within(() => { + cy.get(jsonSection) + .invoke('text') + .convert_string_json_to_js_object() + .then((validatorsInJson) => { + this.validators.forEach((validator, index) => { + const validatorInJson = + validatorsInJson.result.validators[index]; + + assert.equal( + validatorInJson.address, + validator.address, + `Checking that validator address shown in json matches system data` + ); + cy.contains(validator.address).should('be.visible'); + + assert.equal( + validatorInJson.pub_key.type, + validator.pub_key.type, + `Checking that validator public key type shown in json matches system data` + ); + cy.contains(validator.pub_key.type).should('be.visible'); + + assert.equal( + validatorInJson.pub_key.value, + validator.pub_key.value, + `Checking that validator public key value shown in json matches system data` + ); + cy.contains(validator.pub_key.value).should('be.visible'); + + assert.equal( + validatorInJson.voting_power, + validator.voting_power, + `Checking that validator voting power in json matches system data` + ); + cy.contains(validator.voting_power).should('be.visible'); + + // Proposer priority can change frequently mid test + // Therefore only checking the field name is present. + cy.contains('proposer_priority').should('be.visible'); + }); + }); + }); + }); + + it('should be able to see relevant node information in vega data section', function () { + cy.get(vegaDataHeader) + .contains('Vega data') + .next() + .within(() => { + cy.get(jsonSection) + .invoke('text') + .convert_string_json_to_js_object() + .then((nodesInJson) => { + this.nodes.forEach((node, index) => { + const nodeInJson = nodesInJson.nodes[index]; + + // Vegacapsule shows no info or null for following fields: + // name, infoURL, avatarUrl, location, epoch data + // Therefore, these values remain unchecked. + + assert.equal( + nodeInJson.__typename, + node.__typename, + `Checking that node __typename shown in json matches system data` + ); + cy.contains(node.__typename).should('be.visible'); + + assert.equal( + nodeInJson.id, + node.id, + `Checking that node id shown in json matches system data` + ); + cy.contains(node.id).should('be.visible'); + + assert.equal( + nodeInJson.pubkey, + node.pubkey, + `Checking that node pubkey shown in json matches system data` + ); + cy.contains(node.pubkey).should('be.visible'); + + assert.equal( + nodeInJson.tmPubkey, + node.tmPubkey, + `Checking that node tmPubkey shown in json matches system data` + ); + cy.contains(node.tmPubkey).should('be.visible'); + + assert.equal( + nodeInJson.ethereumAddress, + node.ethereumAddress, + `Checking that node ethereumAddress shown in json matches system data` + ); + cy.contains(node.ethereumAddress).should('be.visible'); + + assert.equal( + nodeInJson.stakedByOperator, + node.stakedByOperator, + `Checking that node stakedByOperator value shown in json matches system data` + ); + cy.contains(node.stakedByOperator).should('be.visible'); + + assert.equal( + nodeInJson.stakedByDelegates, + node.stakedByDelegates, + `Checking that node stakedByDelegates value shown in json matches system data` + ); + cy.contains(node.stakedByDelegates).should('be.visible'); + + assert.equal( + nodeInJson.stakedTotal, + node.stakedTotal, + `Checking that node stakedTotal shown in json matches system data` + ); + cy.contains(node.stakedTotal).should('be.visible'); + + assert.equal( + nodeInJson.pendingStake, + node.pendingStake, + `Checking that node pendingStake shown in json matches system data` + ); + cy.contains(node.pendingStake).should('be.visible'); + + assert.equal( + nodeInJson.status, + node.status, + `Checking that node status shown in json matches system data` + ); + cy.contains(node.status).should('be.visible'); + }); + }); + }); + }); + + it('should be able to see validator page displayed on mobile', function () { + cy.common_switch_to_mobile_and_click_toggle(); + cy.get(validatorMenuHeading).click(); + cy.get(vegaDataHeader) + .contains('Vega data') + .and('is.visible') + .next() + .within(() => { + cy.get(jsonSection).should('not.be.empty'); + }); + + cy.get(tendermintDataHeader) + .contains('Tendermint data') + .and('is.visible') + .next() + .within(() => { + cy.get(jsonSection).should('not.be.empty'); + }); + + cy.get(tendermintDataHeader) + .contains('Tendermint data') + .next() + .within(() => { + this.validators.forEach((validator) => { + cy.contains(validator.address).should('be.visible'); + cy.contains(validator.pub_key.type).should('be.visible'); + cy.contains(validator.pub_key.value).should('be.visible'); + cy.contains(validator.voting_power).should('be.visible'); + // Proposer priority can change frequently mid test + // Therefore only checking the field name is present. + cy.contains('proposer_priority').should('be.visible'); + }); + }); + }); + + it('should be able to switch validator page between light and dark mode', function () { + const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)'; + const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)'; + const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)'; + const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)'; + const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)'; + const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)'; + const themeSwitcher = '[data-testid="theme-switcher"]'; + const jsonFields = '.hljs'; + const sideMenuBackground = '.absolute'; + + // Engage dark mode if not allready set + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .then((background_color) => { + if (background_color.includes(whiteThemeSideMenuBackgroundColor)) + cy.get(themeSwitcher).click(); + }); + + // Engage white mode + cy.get(themeSwitcher).click(); + + // White Mode + cy.get(validatorMenuHeading) + .should('have.css', 'background-color') + .and('include', whiteThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', whiteThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', whiteThemeSideMenuBackgroundColor); + + // Dark Mode + cy.get(themeSwitcher).click(); + cy.get(validatorMenuHeading) + .should('have.css', 'background-color') + .and('include', darkThemeSelectedMenuOptionColor); + cy.get(jsonFields) + .should('have.css', 'background-color') + .and('include', darkThemeJsonFieldBackColor); + cy.get(sideMenuBackground) + .should('have.css', 'background-color') + .and('include', darkThemeSideMenuBackgroundColor); + }); + + Cypress.Commands.add('get_validators', () => { + cy.request({ + method: 'GET', + url: `http://localhost:26617/validators`, + headers: { 'content-type': 'application/json' }, + }) + .its(`body.result.validators`) + .then(function (response) { + let validators = []; + response.forEach((account, index) => { + validators[index] = account; + }); + return validators; + }); + }); + + Cypress.Commands.add('get_nodes', () => { + const mutation = + '{nodes { id name infoUrl avatarUrl pubkey tmPubkey ethereumAddress \ + location stakedByOperator stakedByDelegates stakedTotal pendingStake \ + epochData { total offline online __typename } status name __typename}}'; + cy.request({ + method: 'POST', + url: `http://localhost:3028/query`, + body: { + query: mutation, + }, + headers: { 'content-type': 'application/json' }, + }) + .its(`body.data.nodes`) + .then(function (response) { + let nodes = []; + response.forEach((node) => { + nodes.push(node); + }); + return nodes; + }); + }); }); }); diff --git a/apps/explorer/.env b/apps/explorer/.env index a38d60492..3533b45b9 100644 --- a/apps/explorer/.env +++ b/apps/explorer/.env @@ -2,9 +2,11 @@ NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-exp NX_TENDERMINT_URL=https://n01.stagnet3.vega.xyz/tm NX_TENDERMINT_WEBSOCKET_URL=wss://n01.stagnet3.vega.xyz/tm/websocket NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json -NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} NX_VEGA_ENV=STAGNET3 NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions +NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz +NX_BLOCK_EXPLORER= # App flags NX_EXPLORER_ASSETS=1 @@ -15,4 +17,4 @@ NX_EXPLORER_PARTIES=1 NX_EXPLORER_VALIDATORS=1 NX_EXPLORER_MARKETS=1 NX_EXPLORER_ORACLES=1 -NX_EXPLORER_TXS_LIST=1 +NX_EXPLORER_TXS_LIST=0 diff --git a/apps/explorer/.env.capsule b/apps/explorer/.env.capsule index f360272cc..b062a7419 100644 --- a/apps/explorer/.env.capsule +++ b/apps/explorer/.env.capsule @@ -3,7 +3,7 @@ NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-exp NX_TENDERMINT_URL=http://localhost:26617 NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket NX_VEGA_URL=http://localhost:3028/query -NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} NX_VEGA_ENV=CUSTOM NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions diff --git a/apps/explorer/.env.devnet b/apps/explorer/.env.devnet index 0953fb115..76c07de0c 100644 --- a/apps/explorer/.env.devnet +++ b/apps/explorer/.env.devnet @@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql -NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} NX_VEGA_ENV=DEVNET NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions +NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest diff --git a/apps/explorer/.env.mainnet b/apps/explorer/.env.mainnet index 13281de02..a5eac8b75 100644 --- a/apps/explorer/.env.mainnet +++ b/apps/explorer/.env.mainnet @@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/ NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json NX_VEGA_URL=https://api.vega.xyz/query -NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} NX_VEGA_ENV=MAINNET NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions +NX_BLOCK_EXPLORER= diff --git a/apps/explorer/.env.sandbox b/apps/explorer/.env.sandbox new file mode 100644 index 000000000..01a51fdf2 --- /dev/null +++ b/apps/explorer/.env.sandbox @@ -0,0 +1,11 @@ +# App configuration variables +NX_VEGA_URL=https://api.sandbox.vega.xyz/graphql +NX_VEGA_ENV=SANDBOX +NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/sandbox-network.json +NX_VEGA_EXPLORER_URL=https://sandbox.explorer.vega.xyz +NX_VEGA_DOCS_URL=https://docs.vega.xyz/docs/testnet +NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 +NX_ETHERSCAN_URL=https://sepolia.etherscan.io +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} +NX_TENDERMINT_URL=https://tm.n01.sandbox.vega.xyz +NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.sandbox.vega.xyz/websocket diff --git a/apps/explorer/.env.stagnet1 b/apps/explorer/.env.stagnet1 new file mode 100644 index 000000000..9051f3a38 --- /dev/null +++ b/apps/explorer/.env.stagnet1 @@ -0,0 +1,13 @@ +NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8 +NX_ETHERSCAN_URL=https://sepolia.etherscan.io +NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz +NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json +NX_VEGA_ENV=STAGNET1 +NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} +NX_VEGA_TOKEN_URL=https://stagnet1.token.vega.xyz +NX_VEGA_URL=https://api.n00.stagnet1.vega.xyz/graphql +NX_VEGA_WALLET_URL=http://localhost:1789 +NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz +NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket +NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest diff --git a/apps/explorer/.env.stagnet3 b/apps/explorer/.env.stagnet3 index 3cb62f060..b8d197cdd 100644 --- a/apps/explorer/.env.stagnet3 +++ b/apps/explorer/.env.stagnet3 @@ -4,6 +4,7 @@ NX_TENDERMINT_URL=https://n01.stagnet3.vega.xyz/tm NX_TENDERMINT_WEBSOCKET_URL=wss://n01.stagnet3.vega.xyz/tm/websocket NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json NX_VEGA_URL=https://api.n01.stagnet3.vega.xyz/graphql -NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} NX_VEGA_ENV=STAGNET3 NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions +NX_BLOCK_EXPLORER=https://blockexplorer.n01.stagnet3.vega.xyz/rest diff --git a/apps/explorer/.env.testnet b/apps/explorer/.env.testnet index e90d0804c..1f96564b7 100644 --- a/apps/explorer/.env.testnet +++ b/apps/explorer/.env.testnet @@ -1,8 +1,10 @@ # App configuration variables NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz +NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n07.testnet.vega.xyz/websocket NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json -NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}' +NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"} NX_VEGA_ENV=TESTNET NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions +NX_VEGA_URL=https://api.n09.testnet.vega.xyz/graphql diff --git a/apps/explorer/.env.vegacapsule b/apps/explorer/.env.vegacapsule index cc49be5cb..a0b854c9a 100644 --- a/apps/explorer/.env.vegacapsule +++ b/apps/explorer/.env.vegacapsule @@ -5,3 +5,4 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket NX_VEGA_URL=http://localhost:3003/query NX_VEGA_ENV=CUSTOM NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions +NX_BLOCK_EXPLORER= diff --git a/apps/explorer/.eslintrc.json b/apps/explorer/.eslintrc.json index de2a9b877..c236a78b5 100644 --- a/apps/explorer/.eslintrc.json +++ b/apps/explorer/.eslintrc.json @@ -14,5 +14,8 @@ "files": ["*.js", "*.jsx"], "rules": {} } - ] + ], + "env": { + "jest": true + } } diff --git a/apps/explorer/src/app/app.tsx b/apps/explorer/src/app/app.tsx index 2b21f5f55..fd5493640 100644 --- a/apps/explorer/src/app/app.tsx +++ b/apps/explorer/src/app/app.tsx @@ -45,7 +45,7 @@ function App() { menuOpen && 'h-[100vh] overflow-hidden' } antialiased m-0 bg-white dark:bg-black text-black dark:text-white`} > -
+ {subtitle}
+ {tooltipInfo ? (
+
+ {id} +
+ )} + + {type && ( +
+ {JSON.stringify(value, null, ' ')}
+
+ )}
+