Merge pull request #55 from vegaprotocol/test/Cypress-BDD-integration

Test/cypress bdd integration
This commit is contained in:
Joe Tsang 2022-03-08 10:39:05 +00:00 committed by GitHub
commit c1c12d8c70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 320 additions and 32 deletions

View File

@ -0,0 +1,6 @@
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

View File

@ -0,0 +1,6 @@
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

View File

@ -1,4 +1,4 @@
Feature: Home page
Scenario: Visit Home page
Given I go to the homepage
Given I am on the homepage

View File

@ -0,0 +1,6 @@
Feature: Markets Page
Scenario: Navigate to markets page
Given I am on the homepage
When I navigate to the markets page
Then markets page is correctly displayed

View File

@ -0,0 +1,6 @@
Feature: Network parameters Page
Scenario: Navigate to network parameters page
Given I am on the homepage
When I navigate to the transactions page
Then transactions page is correctly displayed

View File

@ -0,0 +1,6 @@
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

View File

@ -0,0 +1,6 @@
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

View File

@ -13,6 +13,7 @@ declare namespace Cypress {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
interface Chainable<Subject> {
login(email: string, password: string): void;
getByTestId(selector: string): Chainable<JQuery<HTMLElement>>;
}
}
//
@ -31,3 +32,6 @@ Cypress.Commands.add('login', (email, password) => {
//
// -- This will overwrite an existing command --
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
Cypress.Commands.add('getByTestId', (selector, ...args) => {
return cy.get(`[data-testid=${selector}]`, ...args);
});

View File

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

View File

@ -0,0 +1,79 @@
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-input';
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) {
cy.getByTestId(this.searchField).type(searchText);
}
validateSearchDisplayed() {
cy.getByTestId(this.blockExplorerHeader).should(
'have.text',
'Vega Block Explorer'
);
cy.getByTestId(this.searchField).should('be.visible');
}
validateBlockDataDisplayed(headerTestId) {
cy.getByTestId(headerTestId).then(($assetHeaders) => {
const headersAmount = parseInt($assetHeaders.length);
cy.wrap($assetHeaders).each(($header) => {
expect($header).to.not.be.empty;
});
cy.get('.language-json')
.each(($asset, index, $list) => {
expect($asset).to.not.be.empty;
})
.then(($list) => {
expect($list).to.have.length(headersAmount);
});
});
}
}

View File

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

View File

@ -0,0 +1,3 @@
import BasePage from './base-page';
export default class HomePage extends BasePage {}

View File

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

View File

@ -0,0 +1,14 @@
import BasePage from './base-page';
export default class NetworkParametersPage extends BasePage {
networkParametersHeader = 'network-param-header';
parameters = 'parameters';
verifyNetworkParametersDisplayed() {
cy.getByTestId(this.networkParametersHeader).should(
'have.text',
'NetworkParameters'
);
cy.getByTestId(this.parameters).should('not.be.empty');
}
}

View File

@ -0,0 +1,37 @@
import BasePage from './base-page';
export default class TransactionsPage extends BasePage {
blockRow = 'block-row';
blockHeight = 'block-height';
numberOfTransactions = 'num-txs';
validatorLink = 'validator-link';
blockTime = 'block-time';
refreshBtn = 'refresh';
validateTransactionsPagedisplayed() {
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');
}
validateRefreshBtn() {
cy.getByTestId(this.blockHeight)
.first()
.invoke('text')
.then((blockHeightTxt) => {
cy.wait(1000); // eslint-disable-line cypress/no-unnecessary-waiting
//Wait needed to allow blocks to change
cy.getByTestId(this.refreshBtn).click();
cy.getByTestId(this.blockHeight)
.first()
.invoke('text')
.should((newBlockHeightText) => {
expect(blockHeightTxt).not.to.eq(newBlockHeightText);
});
});
cy.getByTestId(this.blockTime).first();
}
}

View File

@ -0,0 +1,18 @@
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

@ -0,0 +1,12 @@
import { Given, 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

@ -0,0 +1,5 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
Given('I am on the homepage', () => {
cy.visit('/');
});

View File

@ -1,5 +1 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
Given('I go to the homepage', () => {
cy.visit('/');
});

View File

@ -0,0 +1,12 @@
import { Given, 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

@ -0,0 +1,22 @@
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();
});
Then('transactions page is correctly displayed', () => {
transactionsPage.validateTransactionsPagedisplayed();
transactionsPage.validateRefreshBtn();
});
Then('blocks page is correctly displayed', () => {
transactionsPage.validateTransactionsPagedisplayed();
transactionsPage.validateRefreshBtn();
});

View File

@ -0,0 +1,12 @@
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

@ -20,23 +20,23 @@ export const BlocksTable = ({ data, showTransactions }: BlocksProps) => {
{data.result?.block_metas?.map((block, index) => {
return (
<React.Fragment key={index}>
<tr>
<td>
<tr data-testid="block-row">
<td data-testid="block-height">
<Link to={`/blocks/${block.header?.height}`}>
{block.header?.height}
</Link>
</td>
<td>
<td data-testid="num-txs">
{block.num_txs === '1'
? '1 transaction'
: `${block.num_txs} transactions`}
</td>
<td>
<td data-testid="validator-link">
<Link to={`/validators/${block.header?.proposer_address}`}>
{block.header.proposer_address}
</Link>
</td>
<td>
<td data-testid="block-time">
<SecondsAgo date={block.header?.time} />
</td>
</tr>

View File

@ -91,10 +91,15 @@ const Search = () => {
const { search, onChange } = useGuess();
return (
<section>
<h1>Vega Block Explorer</h1>
<h1 data-testid="explorer-header">Vega Block Explorer</h1>
<fieldset>
<label htmlFor="search">Search: </label>
<input name="search" value={search} onChange={(e) => onChange(e)} />
<input
data-testid="search-field"
name="search"
value={search}
onChange={(e) => onChange(e)}
/>
</fieldset>
</section>
);

View File

@ -17,12 +17,12 @@ export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
<Table>
<tr>
<td>Hash</td>
<td>{txData.hash}</td>
<td data-testid="hash">{txData.hash}</td>
</tr>
{pubKey ? (
<tr>
<td>Submitted by</td>
<td>
<td data-testid="submitted-by">
<Link to={`/${Routes.PARTIES}/${pubKey}`}>{pubKey}</Link>
</td>
</tr>
@ -35,14 +35,14 @@ export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
{txData.height ? (
<tr>
<td>Block</td>
<td>
<td data-testid="block">
<Link to={`/blocks/${txData.height}`}>{txData.height}</Link>
</td>
</tr>
) : null}
<tr>
<td>Encoded tnx</td>
<td>{txData.tx}</td>
<td data-testid="encoded-tnx">{txData.tx}</td>
</tr>
</Table>
);

View File

@ -1,5 +1,5 @@
import React from "react";
import { WebSocketHook } from "react-use-websocket/dist/lib/types";
import React from 'react';
import { WebSocketHook } from 'react-use-websocket/dist/lib/types';
export type WebsocketContextShape = WebSocketHook;
@ -9,7 +9,7 @@ export const TendermintWebsocketContext =
export function useTendermintWebsocketContext() {
const context = React.useContext(TendermintWebsocketContext);
if (context === null) {
throw new Error("useWebsocket must be used within WebsocketContext");
throw new Error('useWebsocket must be used within WebsocketContext');
}
return context;
}

View File

@ -39,7 +39,7 @@ const Assets = () => {
<h1>Assets</h1>
{data?.assets.map((a) => (
<React.Fragment key={a.id}>
<h2>
<h2 data-testid="asset-header">
{a.name} ({a.symbol})
</h2>
<SyntaxHighlighter data={a} />

View File

@ -16,7 +16,9 @@ const Blocks = () => {
<>
<section>
<h1>Blocks</h1>
<button onClick={() => refetch()}>Refresh to see latest blocks</button>
<button data-testid="refresh" onClick={() => refetch()}>
Refresh to see latest blocks
</button>
<BlocksTable data={data} />
</section>

View File

@ -12,7 +12,7 @@ const Genesis = () => {
if (!genesis?.result.genesis) return null;
return (
<section>
<h1>Genesis</h1>
<h1 data-testid="genesis-header">Genesis</h1>
<SyntaxHighlighter data={genesis?.result.genesis} />
</section>
);

View File

@ -153,7 +153,7 @@ const Markets = () => {
<h1>Markets</h1>
{data.markets.map((m) => (
<React.Fragment key={m.id}>
<h2>{m.name}</h2>
<h2 data-testid="markets-header">{m.name}</h2>
<SyntaxHighlighter data={m} />
</React.Fragment>
))}

View File

@ -1,5 +1,5 @@
import { gql, useQuery } from "@apollo/client";
import { NetworkParametersQuery } from "./__generated__/NetworkParametersQuery";
import { gql, useQuery } from '@apollo/client';
import { NetworkParametersQuery } from './__generated__/NetworkParametersQuery';
export const NETWORK_PARAMETERS_QUERY = gql`
query NetworkParametersQuery {
@ -14,8 +14,8 @@ const NetworkParameters = () => {
const { data } = useQuery<NetworkParametersQuery>(NETWORK_PARAMETERS_QUERY);
return (
<section>
<h1>NetworkParameters</h1>
<pre>{JSON.stringify(data, null, " ")}</pre>
<h1 data-testid="network-param-header">NetworkParameters</h1>
<pre data-testid="parameters">{JSON.stringify(data, null, ' ')}</pre>
</section>
);
};

View File

@ -16,7 +16,9 @@ const Txs = () => {
<>
<section>
<h1>Transactions</h1>
<button onClick={() => refetch()}>Refresh to see latest blocks</button>
<button data-testid="refresh" onClick={() => refetch()}>
Refresh to see latest blocks
</button>
<BlocksTable data={data} showTransactions={true} />
</section>

View File

@ -44,10 +44,12 @@ const Validators = () => {
return (
<section>
<h1>Validators</h1>
<h2>Tendermint data</h2>
<pre>{JSON.stringify(validators, null, ' ')}</pre>
<h2>Vega data</h2>
<pre>{JSON.stringify(data, null, ' ')}</pre>
<h2 data-testid="tendermint-header">Tendermint data</h2>
<pre data-testid="tendermint-data">
{JSON.stringify(validators, null, ' ')}
</pre>
<h2 data-testid="vega-header">Vega data</h2>
<pre data-testid="vega-data">{JSON.stringify(data, null, ' ')}</pre>
</section>
);
};