Test/deal ticket tests (#161)

* scaffold dealticket package, remove trading views from react-helpers

* add deal ticket component, add intent utils, expand dialog and form group styles

* add splash component, show market not found message if market doesnt exist

* tidy up error handling

* add handleError method for vega tx hook

* add better testname for provider test, flesh out tests a bit more for deal ticket

* Add unit tests for useVegaTransaction and useOrderSubmit hooks

* add wrapper component for order dialog styles

* add vega styled loader to ui toolkit and use in order dialog

* add title prop to order dialog

* split limit and market tickets into own files

* add button radio component

* revert dialog styles

* move splash component to ui-toolkit, add story

* convert intent to enum

* Make button always type=button unless type prop is passed

* inline filter logic for tif selector

* add date-fns, add datetime to helpers

* add order types to wallet package, make price undefined if order type is market

* use enums in deal ticket logic

* tidy up order state by moving submit and transaction hooks out of deal ticket

* add comment for dialog styles

* remove decimal from price input

* add types package, delete old generated types from trading project

* rename types package to graphql

* update generate command to point to correct locations

* fix use order submit test

* BDD and navigation tests passing

* Remove commented steps

* Steps up to placing order

* Date picker and date-fns update

* Vega connector wallet tests

* Passing up to request sent, updated date picker

* Tests for sell orders and errors

* Update market feature

* Fix failing tests

* Update wallet login

* Readded tx hash assertion and remaining tests

* Add CI wallet import

* Update .github/workflows/cypress.yml

Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com>

* Resolved PR comments

* Fix yaml error

* Attempt to fix failing tests in CI

* Run Cypress in Chrome

* Add reload if public key error displayed

* Fix wallet name

* Add force click and waits

* Increase timeout for deal ticket page

* Removed network list from yaml and using input error id

* Increase timeout to 8 seconds

* Re add deleted test id

Co-authored-by: Matthew Russell <mattrussell36@gmail.com>
Co-authored-by: Dexter Edwards <dexter.edwards93@gmail.com>
This commit is contained in:
Joe Tsang 2022-04-04 16:11:27 +01:00 committed by GitHub
parent 8fd1d00474
commit 205f4124f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 626 additions and 26 deletions

View File

@ -11,7 +11,13 @@ jobs:
master:
name: Run end-to-end tests - main
runs-on: ubuntu-latest
strategy:
matrix:
vegawallet-version:
- '0.13.2'
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v2
@ -28,11 +34,34 @@ jobs:
node-version: 16.14.0
- name: Install root dependencies
run: yarn install
- name: Download and unzip wallet
run: curl -L https://github.com/vegaprotocol/vegawallet/releases/download/v${{ matrix.vegawallet-version }}/vegawallet-linux-amd64.zip -O
- name: Unzip wallet
run: unzip ./vegawallet-linux-amd64.zip
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: ./vegawallet init -f
- name: Import wallet
run: ./vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase
- name: Import config
run: ./vegawallet network import --from-url https://raw.githubusercontent.com/vegaprotocol/networks/master/fairground/fairground.toml
- name: Start service
run: ./vegawallet service run --network fairground &
- name: Run Cypress tests
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.tradingWalletPassphrase=${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }} --browser chrome
pr:
name: Run end-to-end tests - PR
runs-on: ubuntu-latest
strategy:
matrix:
vegawallet-version:
- '0.13.2'
if: ${{ github.event_name == 'pull_request' }}
steps:
- name: Checkout
@ -51,5 +80,23 @@ jobs:
node-version: 16.14.0
- name: Install root dependencies
run: yarn install
- name: Download and unzip wallet
run: curl -L https://github.com/vegaprotocol/vegawallet/releases/download/v${{ matrix.vegawallet-version }}/vegawallet-linux-amd64.zip -O
- name: Unzip wallet
run: unzip ./vegawallet-linux-amd64.zip
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: ./vegawallet init -f
- name: Import wallet
run: ./vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase
- name: Import config
run: ./vegawallet network import --from-url https://raw.githubusercontent.com/vegaprotocol/networks/master/fairground/fairground.toml
- name: Start service
run: ./vegawallet service run --network fairground &
- name: Run Cypress tests
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }}
run: npx nx affected:e2e --parallel=5 --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --env.tradingWalletPassphrase=${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }} --browser chrome

View File

@ -14,6 +14,7 @@
"screenshotsFolder": "../../dist/cypress/apps/explorer-e2e/screenshots",
"chromeWebSecurity": false,
"env": {
"tsConfig": "tsconfig.json"
"tsConfig": "tsconfig.json",
"TAGS": "not @todo and not @ignore and not @manual"
}
}

View File

@ -0,0 +1,3 @@
{
"stepDefinitions": "src/support/step_definitions"
}

View File

@ -1,13 +1,20 @@
{
"baseUrl": "http://localhost:4200",
"fileServerFolder": ".",
"fixturesFolder": "./src/fixtures",
"fixturesFolder": false,
"pluginsFile": "./src/plugins/index.js",
"testFiles": "*.{ts,feature,features}",
"ignoreTestFiles": "**/*.js",
"integrationFolder": "./src/integration",
"modifyObstructiveCode": false,
"supportFile": "./src/support/index.ts",
"pluginsFile": false,
"video": true,
"videosFolder": "../../dist/cypress/apps/trading-e2e/videos",
"screenshotsFolder": "../../dist/cypress/apps/trading-e2e/screenshots",
"chromeWebSecurity": false,
"projectId": "et4snf"
"projectId": "et4snf",
"env": {
"tsConfig": "tsconfig.json",
"TAGS": "not @todo and not @ignore and not @manual"
}
}

View File

@ -1,13 +0,0 @@
import { getGreeting } from '../support/app.po';
describe('trading', () => {
beforeEach(() => cy.visit('/'));
it('should display welcome message', () => {
// Custom command example, see `../support/commands.ts` file
cy.login('my-email@something.com', 'myPassword');
// Function helper example, see `../support/app.po.ts` file
getGreeting().contains('Welcome to Vega Trading App');
});
});

View File

@ -0,0 +1,18 @@
Feature: Home page
Scenario: Visit Home page
Given I am on the homepage
Scenario: Visit Portfolio page
Given I am on the homepage
And I navigate to portfolio page
Scenario: Unable to connect Vega wallet with incorrect credentials
Given I am on the homepage
When I try to connect Vega wallet with incorrect details
Then wallet not running error message is displayed
Scenario: Unable to connect Vega wallet with blank fields
Given I am on the homepage
When I try to connect Vega wallet with blank fields
Then wallet field validation errors are shown

View File

@ -0,0 +1,98 @@
Feature: Market orders
Scenario Outline: Successfull market buy orders
Given I am on the homepage
And I navigate to markets page
When I click on active market
And I connect to Vega Wallet
And place a buy '<marketOrderType>' market order
Then order request is sent
Examples:
| marketOrderType |
| FOK |
| IOC |
Scenario Outline: Successfull Limit buy orders
Given I am on the homepage
And I navigate to markets page
When I click on active market
And I connect to Vega Wallet
And place a buy '<limitOrderType>' limit order
Then order request is sent
Examples:
| limitOrderType |
| IOC |
| FOK |
| GTT |
# | GFA | Requires market to be in auction
| GFN |
Scenario Outline: Successfull market sell order
Given I am on the homepage
And I navigate to markets page
When I click on active market
And I connect to Vega Wallet
And place a sell '<marketOrderType>' market order
Then order request is sent
Examples:
| marketOrderType |
| FOK |
| IOC |
Scenario Outline: Successfull limit sell order
Given I am on the homepage
And I navigate to markets page
When I click on active market
And I connect to Vega Wallet
And place a sell '<limitOrderType>' limit order
Examples:
| limitOrderType |
| IOC |
| FOK |
| GTT |
# | GFA | Requires market to be in auction
| GFN |
Scenario: Unsuccessfull order because lack of funds
Given I am on the homepage
And I navigate to markets page
When I click on active market
And I connect to Vega Wallet
And place a buy 'FOK' market order
Then error message for insufficient funds is displayed
Scenario: Unable to order because market is suspended
Given I am on the homepage
And I navigate to markets page
When I click on suspended market
And I connect to Vega Wallet
Then place order button is disabled
And "Market is currently suspended" error is shown
Scenario: Unable to order because wallet is not connected
Given I am on the homepage
And I navigate to markets page
When I click on active market
Then place order button is disabled
And "No public key selected" error is shown
Scenario: Unsuccessfull because quantity is 0
Given I am on the homepage
And I navigate to markets page
When I click on active market
And I connect to Vega Wallet
And place a buy 'FOK' market order with amount of 0
Then Order rejected by wallet error shown containing text "must be positive"
@manual
Scenario: GTT order failed because invalid date
@manual
Scenario: GTT order failed because date in the past
@manual
Scenario: GTT order failed because date over allowed period

View File

@ -0,0 +1,75 @@
/// <reference types="cypress" />
const webpackPreprocessor = require('@cypress/webpack-preprocessor');
const webpack = require('webpack');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
on(
'file:preprocessor',
webpackPreprocessor({
webpackOptions: {
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
plugins: [
new TsconfigPathsPlugin({
configFile: config.env.tsConfig,
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
}),
],
fallback: {
path: require.resolve('path-browserify'),
},
},
module: {
rules: [
{
test: /\.([jt])sx?$/,
loader: 'ts-loader',
exclude: [/node_modules/],
options: {
configFile: config.env.tsConfig,
// https://github.com/TypeStrong/ts-loader/pull/685
experimentalWatchApi: true,
transpileOnly: true,
},
},
{
test: /\.feature$/,
use: [
{
loader: 'cypress-cucumber-preprocessor/loader',
},
],
},
{
test: /\.features$/,
use: [
{
loader: 'cypress-cucumber-preprocessor/lib/featuresLoader',
},
],
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin({
typescript: {
enabled: true,
configFile: config.env.tsConfig,
},
}),
new webpack.ProvidePlugin({
process: 'process/browser',
}),
],
externals: [nodeExternals()],
},
})
);
};

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,56 @@
export default class BasePage {
porfolioUrl = '/portfolio';
marketsUrl = '/markets';
connectVegaBtn = 'connect-vega-wallet';
walletConnectors = 'connectors-list';
walletForm = 'rest-connector-form';
walletInputError = 'input-wallet-error';
walletFormError = 'form-error';
inputError = 'input-error-text';
navigateToPortfolio() {
cy.get(`a[href='${this.porfolioUrl}']`).click();
}
navigateToMarkets() {
cy.get(`a[href='${this.marketsUrl}']`)
.should('be.visible')
.click({ force: true });
cy.url().should('include', '/markets');
}
navigateToConnectVegaWallet() {
cy.getByTestId(this.connectVegaBtn).click();
cy.contains('Connects using REST to a running Vega wallet service');
cy.getByTestId(this.walletConnectors).find('button').click();
}
fillInWalletForm(walletName, walletPassphrase) {
cy.getByTestId(this.walletForm)
.find('#wallet')
.click({ force: true })
.type(walletName);
cy.getByTestId(this.walletForm)
.find('#passphrase')
.click({ force: true })
.type(walletPassphrase);
}
clickConnectVegaWallet() {
cy.getByTestId(this.walletForm)
.find('button[type=submit]')
.click({ force: true });
}
validateWalletNotRunningError() {
cy.getByTestId(this.walletFormError).should(
'have.text',
'Authentication failed'
);
}
validateWalletErrorFieldsDisplayed() {
cy.getByTestId(this.walletInputError).should('have.text', 'Required');
cy.getByTestId(this.inputError).should('have.text', 'Required');
}
}

View File

@ -0,0 +1,114 @@
import BasePage from './base-page';
export default class DealTicketPage extends BasePage {
marketOrderType = 'order-type-TYPE_MARKET';
limitOrderType = 'order-type-TYPE_LIMIT';
buyOrder = 'order-side-SIDE_BUY';
sellOrder = 'order-side-SIDE_SELL';
orderSizeField = 'order-size';
orderPriceField = 'order-price';
orderTypeDropDown = 'order-tif';
datePickerField = 'date-picker-field';
placeOrderBtn = 'place-order';
orderDialog = 'order-wrapper';
orderStatusHeader = 'order-status-header';
orderTransactionHash = 'tx-hash';
orderErrorTxt = 'error-reason';
placeMarketOrder(isBuy, orderSize, orderType) {
cy.get(`[data-testid=${this.placeOrderBtn}]`, { timeout: 8000 }).should(
'be.visible'
);
if (isBuy == false) {
cy.getByTestId(this.sellOrder).click();
}
cy.getByTestId(this.orderSizeField).clear().type(orderSize);
cy.getByTestId(this.orderTypeDropDown).select(orderType);
}
placeLimitOrder(isBuy, orderSize, orderPrice, orderType) {
cy.getByTestId(this.limitOrderType).click();
if (isBuy == false) {
cy.getByTestId(this.sellOrder).click();
}
cy.getByTestId(this.orderSizeField).clear().type(orderSize);
cy.getByTestId(this.orderPriceField).clear().type(orderPrice);
cy.getByTestId(this.orderTypeDropDown).select(orderType);
if (orderType == 'GTT') {
const today = new Date(new Date().setSeconds(0));
const futureDate = new Date(today.setMonth(today.getMonth() + 1)); // set expiry to one month from now
const formattedDate = this.formatDate(futureDate);
cy.getByTestId(this.datePickerField).click().type(formattedDate);
}
}
verifyOrderRequestSent() {
cy.getByTestId(this.orderStatusHeader).should(
'have.text',
'Awaiting network confirmation'
);
cy.getByTestId(this.orderTransactionHash)
.invoke('text')
.should('contain', 'Tx hash: ')
.and('have.length.above', 64);
}
verifyOrderFailedInsufficientFunds() {
cy.get(`[data-testid=${this.orderErrorTxt}]`, { timeout: 8000 }).should(
'have.text',
'Reason: InsufficientAssetBalance'
);
}
clickPlaceOrder() {
cy.getByTestId(this.placeOrderBtn).click();
}
verifyPlaceOrderBtnDisabled() {
cy.getByTestId(this.placeOrderBtn).should('be.disabled');
}
verifySubmitBtnErrorText(expectedText) {
cy.getByTestId(this.inputError).should('have.text', expectedText);
}
verifyOrderRejected(errorMsg) {
cy.getByTestId(this.orderStatusHeader).should(
'have.text',
'Order rejected by wallet'
);
cy.getByTestId(this.orderDialog)
.find('pre')
.should('contain.text', errorMsg);
}
reloadPageIfPublicKeyErrorDisplayed() {
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.inputError}]`).length) {
cy.getByTestId(this.inputError)
.invoke('text')
.then(($errorText) => {
if ($errorText == 'No public key selected') {
cy.reload;
}
});
}
});
}
formatDate(date) {
const padZero = (num) => num.toString().padStart(2, '0');
const year = date.getFullYear();
const month = padZero(date.getMonth() + 1);
const day = padZero(date.getDate());
const hours = padZero(date.getHours());
const minutes = padZero(date.getMinutes());
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
}

View File

@ -0,0 +1,54 @@
import BasePage from './base-page';
export default class MarketPage extends BasePage {
marketRow = 'market-row';
chartTab = 'chart';
ticketTab = 'ticket';
orderbookTab = 'orderbook';
ordersTab = 'orders';
positionsTab = 'positions';
collateralTab = 'collateral';
tradesTab = 'trades';
completedTrades = 'market-trades';
orderBookTab = 'orderbook';
validateMarketsAreDisplayed() {
cy.getByTestId(this.marketRow).should('have.length.above', 0);
}
validateCompletedTradesDisplayed() {
cy.getByTestId(this.completedTrades).should('not.be.empty');
}
clickOnMarket(marketText) {
cy.contains(marketText).click();
}
clickOnActiveMarket() {
cy.contains('Active', { timeout: 8000 }).click({ force: true });
}
clickOnTopMarketRow() {
cy.getByTestId(this.marketRow).first().click();
}
clickOnOrdersTab() {
cy.getByTestId(this.ordersTab).click();
}
clickOnTicketTab() {
cy.getByTestId(this.ticketTab).click();
}
clickOnCollateralTab() {
cy.getByTestId(this.collateralTab).click();
}
clickOnTradesTab() {
cy.getByTestId(this.tradesTab).click();
}
clickOrderBookTab() {
cy.getByTestId(this.orderBookTab).click();
}
}

View File

@ -0,0 +1,27 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
import MarketsPage from '../pages/markets-page';
import DealTicketPage from '../pages/deal-ticket-page';
const marketsPage = new MarketsPage();
const dealTicketPage = new DealTicketPage();
Given('I am on the homepage', () => {
cy.visit('/');
});
Given('I navigate to markets page', () => {
marketsPage.navigateToMarkets();
});
Given('I navigate to portfolio page', () => {
marketsPage.navigateToPortfolio();
});
When('I connect to Vega Wallet', () => {
marketsPage.navigateToConnectVegaWallet();
marketsPage.fillInWalletForm(
'UI_Trading_Test',
Cypress.env('tradingWalletPassphrase')
);
marketsPage.clickConnectVegaWallet();
dealTicketPage.reloadPageIfPublicKeyErrorDisplayed();
});

View File

@ -0,0 +1,22 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
import MarketsPage from '../pages/markets-page';
const marketsPage = new MarketsPage();
When('I try to connect Vega wallet with incorrect details', () => {
marketsPage.navigateToConnectVegaWallet();
marketsPage.fillInWalletForm('name', 'wrong passphrase');
marketsPage.clickConnectVegaWallet();
});
When('I try to connect Vega wallet with blank fields', () => {
marketsPage.navigateToConnectVegaWallet();
marketsPage.clickConnectVegaWallet();
});
Then('wallet not running error message is displayed', () => {
marketsPage.validateWalletNotRunningError();
});
Then('wallet field validation errors are shown', () => {
marketsPage.validateWalletErrorFieldsDisplayed();
});

View File

@ -0,0 +1,65 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
import MarketsPage from '../pages/markets-page';
import DealTicketPage from '../pages/deal-ticket-page';
const marketsPage = new MarketsPage();
const dealTicketPage = new DealTicketPage();
When('I click on market for {string}', (marketText) => {
marketsPage.clickOnMarket(marketText);
});
When('I click on active market', () => {
marketsPage.clickOnMarket('Active');
});
When('place a buy {string} market order', (orderType) => {
dealTicketPage.placeMarketOrder(true, 100, orderType);
dealTicketPage.clickPlaceOrder();
});
When('place a sell {string} market order', (orderType) => {
dealTicketPage.placeMarketOrder(false, 100, orderType);
dealTicketPage.clickPlaceOrder();
});
When('place a buy {string} limit order', (limitOrderType) => {
dealTicketPage.placeLimitOrder(true, 100, 2000, limitOrderType);
dealTicketPage.clickPlaceOrder();
});
When('place a sell {string} limit order', (limitOrderType) => {
dealTicketPage.placeLimitOrder(false, 100, 2000, limitOrderType);
dealTicketPage.clickPlaceOrder();
});
When('place a buy {string} market order with amount of 0', (orderType) => {
dealTicketPage.placeMarketOrder(true, 0, orderType);
dealTicketPage.clickPlaceOrder();
});
When('I click on suspended market', () => {
marketsPage.clickOnMarket('Suspended');
});
Then('order request is sent', () => {
dealTicketPage.verifyOrderRequestSent();
});
Then('error message for insufficient funds is displayed', () => {
dealTicketPage.verifyOrderFailedInsufficientFunds();
});
Then('place order button is disabled', () => {
dealTicketPage.verifyPlaceOrderBtnDisabled();
});
Then('{string} error is shown', (errorMsg) => {
dealTicketPage.verifySubmitBtnErrorText(errorMsg);
});
Then(
'Order rejected by wallet error shown containing text {string}',
(expectedError) => {
dealTicketPage.verifyOrderRejected(expectedError);
}
);

View File

@ -27,6 +27,7 @@ export const VegaWalletConnectButton = ({
<span className="text-ui-small font-mono mr-2">Vega key:</span>
)}
<button
data-testid="connect-vega-wallet"
onClick={handleClick}
className="ml-auto inline-block text-ui-small font-mono hover:underline"
>

View File

@ -53,7 +53,11 @@ export const GridTabs = ({ children, group }: GridTabsProps) => {
'bg-black-10 dark:bg-white-10': !isActive,
});
return (
<Tabs.Trigger value={child.props.id} className={triggerClass}>
<Tabs.Trigger
data-testid={child.props.name}
value={child.props.id}
className={triggerClass}
>
{child.props.name}
</Tabs.Trigger>
);

View File

@ -156,6 +156,7 @@ export const TradePanels = ({ market }: TradePanelsProps) => {
});
return (
<button
data-testid={key}
onClick={() => setView(key as TradingView)}
className={className}
key={key}

View File

@ -14,6 +14,7 @@ export const ExpirySelector = ({ order, onSelect }: ExpirySelectorProps) => {
return (
<FormGroup label="Expiry time/date" labelFor="expiration">
<Input
data-testid="date-picker-field"
id="expiration"
name="expiration"
type="datetime-local"

View File

@ -40,7 +40,9 @@ export const OrderDialog = ({
icon={<Loader />}
>
{transaction.hash && (
<p className="break-all">{t(`Tx hash: ${transaction.hash}`)}</p>
<p data-testid="tx-hash" className="break-all">
{t(`Tx hash: ${transaction.hash}`)}
</p>
)}
</OrderDialogWrapper>
);
@ -53,7 +55,9 @@ export const OrderDialog = ({
title="Order failed"
icon={<Icon name="warning-sign" size={20} />}
>
<p>{t(`Reason: ${finalizedOrder.rejectionReason}`)}</p>
<p data-testid="error-reason">
{t(`Reason: ${finalizedOrder.rejectionReason}`)}
</p>
</OrderDialogWrapper>
);
}
@ -97,8 +101,10 @@ const OrderDialogWrapper = ({
return (
<div className="flex gap-12 max-w-full">
<div className="pt-8 fill-current">{icon}</div>
<div className="flex-1">
<h1 className="text-h4">{title}</h1>
<div data-testid="order-wrapper" className="flex-1">
<h1 data-testid="order-status-header" className="text-h4">
{title}
</h1>
{children}
</div>
</div>

View File

@ -75,6 +75,7 @@ export const SubmitButton = ({
variant="primary"
type="submit"
disabled={disabled}
data-testid="place-order"
>
{transactionStatus === 'pending' ? t('Pending...') : t('Place order')}
</Button>

View File

@ -35,7 +35,11 @@ export const InputError = ({
'fill-intent-warning': intent === 'warning',
});
return (
<div className={effectiveClassName} {...props}>
<div
data-testid="input-error-text"
className={effectiveClassName}
{...props}
>
<Icon name="warning-sign" className={iconClassName} />
{children}
</div>

View File

@ -60,7 +60,11 @@ export function RestConnectorForm({
autoFocus={true}
/>
{errors.wallet?.message && (
<InputError intent="danger" className="mt-4">
<InputError
data-testid="input-wallet-error"
intent="danger"
className="mt-4"
>
{errors.wallet.message}
</InputError>
)}