Compare commits

..
381 changed files with 10488 additions and 10612 deletions
-1
View File
@@ -1 +0,0 @@
node_modules
+6 -11
View File
@@ -1,7 +1,7 @@
{
"root": true,
"ignorePatterns": ["**/*"],
"plugins": ["@nx", "eslint-plugin-unicorn", "jsx-a11y", "jest"],
"plugins": ["@nrwl/nx", "eslint-plugin-unicorn", "jsx-a11y", "jest"],
"settings": {
"jsx-a11y": {
"components": {
@@ -18,7 +18,7 @@
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
"extends": ["plugin:jsx-a11y/strict"],
"rules": {
"@nx/enforce-module-boundaries": [
"@nrwl/nx/enforce-module-boundaries": [
"error",
{
"enforceBuildableLibDependency": true,
@@ -56,7 +56,7 @@
},
{
"files": ["*.ts", "*.tsx"],
"extends": ["plugin:@nx/typescript"],
"extends": ["plugin:@nrwl/nx/typescript"],
"rules": {
"@typescript-eslint/ban-ts-comment": [
"error",
@@ -80,19 +80,14 @@
},
{
"files": ["*.spec.ts", "*.spec.tsx"],
"extends": ["plugin:@nx/typescript", "plugin:jest/recommended"],
"extends": ["plugin:@nrwl/nx/typescript", "plugin:jest/recommended"],
"rules": {
"jest/consistent-test-it": [
"error",
{
"fn": "it"
}
]
"jest/consistent-test-it": ["error", { "fn": "it" }]
}
},
{
"files": ["*.js", "*.jsx"],
"extends": ["plugin:@nx/javascript"],
"extends": ["plugin:@nrwl/nx/javascript"],
"rules": {}
}
]
+4 -4
View File
@@ -49,7 +49,7 @@ jobs:
lint-pr-title:
needs: node-modules
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
if: ${{ github.event_name == 'pull_request' }}
name: Verify PR title
uses: ./.github/workflows/lint-pr.yml
secrets: inherit
@@ -204,7 +204,7 @@ jobs:
cypress:
needs: lint-test-build
name: '(CI) cypress'
# if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
if: ${{ needs.lint-test-build.outputs.projects-e2e != '[]' }}
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
@@ -214,7 +214,7 @@ jobs:
publish-dist:
needs: lint-test-build
name: '(CD) publish dist'
# if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
if: ${{ needs.lint-test-build.outputs.projects != '[]' }}
uses: ./.github/workflows/publish-dist.yml
secrets: inherit
with:
@@ -225,7 +225,7 @@ jobs:
needs:
- publish-dist
- lint-test-build
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
if: ${{ github.event_name == 'pull_request' }}
timeout-minutes: 60
name: '(CD) comment preview links'
steps:
+8 -24
View File
@@ -33,7 +33,7 @@ jobs:
uses: docker/setup-buildx-action@v2
- name: Log in to the Container registry (ghcr)
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
if: ${{ github.event_name == 'pull_request' }}
uses: docker/login-action@v2
with:
registry: ghcr.io
@@ -113,19 +113,19 @@ jobs:
- name: Build local dist
run: |
envCmd=""
flags=""
if [[ ! -z "${{ env.ENV_NAME }}" ]]; then
envCmd="yarn env-cmd -f ./apps/${{ matrix.app }}/.env.${{ env.ENV_NAME }}"
flags="--env=${{ env.ENV_NAME }}"
fi
if [ "${{ matrix.app }}" = "trading" ]; then
$envCmd yarn nx export trading || (yarn install && $envCmd yarn nx export trading)
yarn nx export trading $flags || (yarn install && yarn nx export trading $flags)
DIST_LOCATION=dist/apps/trading/exported
elif [ "${{ matrix.app }}" = "ui-toolkit" ]; then
NODE_ENV=production yarn nx run ui-toolkit:build-storybook
DIST_LOCATION=dist/storybook/ui-toolkit
else
$envCmd yarn nx build ${{ matrix.app }} || (yarn install && $envCmd yarn nx build ${{ matrix.app }})
yarn nx build ${{ matrix.app }} $flags || (yarn install && yarn nx build ${{ matrix.app }} $flags)
DIST_LOCATION=dist/apps/${{ matrix.app }}
fi
mv $DIST_LOCATION dist-result
@@ -145,7 +145,7 @@ jobs:
ghcr.io/vegaprotocol/frontend/${{ matrix.app }}:local
- name: Image digest
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
if: ${{ github.event_name == 'pull_request' }}
run: echo ${{ steps.docker_build.outputs.digest }}
- name: Sanity check docker image
@@ -160,7 +160,7 @@ jobs:
uses: docker/build-push-action@v3
continue-on-error: true
id: ghcr-push
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
if: ${{ github.event_name == 'pull_request' }}
with:
context: .
file: docker/node-outside-docker.Dockerfile
@@ -228,25 +228,9 @@ jobs:
AWS_REGION: 'eu-west-1'
SOURCE_DIR: 'dist-result'
- name: Install aws CLI
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
uses: unfor19/install-aws-cli-action@master
- name: Perform cache invalidation
if: ${{ github.event_name == 'push' && ( matrix.app != 'trading' || (matrix.app == 'trading' && !( endsWith(github.ref, 'main') || endsWith(github.ref, 'release/testnet') ) ) ) }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: 'eu-west-1'
run: |
echo "Looking for distribution for bucket: ${{ env.BUCKET_NAME }}"
id=$(aws cloudfront list-distributions | jq -Mrc '.DistributionList.Items | .[] | select(.DefaultCacheBehavior.TargetOriginId == "${{ env.BUCKET_NAME }}") | .Id')
echo "Found id is: ${id}"
aws cloudfront create-invalidation --distribution-id $id --paths "/*"
- name: Add preview label
uses: actions-ecosystem/action-add-labels@v1
if: ${{ github.event_name == 'pull_request' || github.event_name == 'pull_request_target' }}
if: ${{ github.event_name == 'pull_request' }}
with:
labels: ${{ matrix.app }}-preview
number: ${{ github.event.number }}
+29
View File
@@ -0,0 +1,29 @@
module.exports = {
stories: [],
addons: [
'@storybook/addon-actions',
'@storybook/addon-viewport',
{
name: '@storybook/addon-docs',
options: {
configureJSX: true,
babelOptions: {},
sourceLoaderOptions: null,
transcludeMarkdown: true,
},
},
'@storybook/addon-controls',
'@storybook/addon-backgrounds',
'@storybook/addon-toolbars',
'@storybook/addon-measure',
'@storybook/addon-outline',
'@storybook/addon-a11y',
],
// uncomment the property below if you want to apply some webpack config globally
// webpackFinal: async (config, { configType }) => {
// // Make whatever fine-grained changes you need that should apply to all storybook configs
// // Return the altered config
// return config;
// },
};
+14
View File
@@ -0,0 +1,14 @@
{
"extends": "../tsconfig.base.json",
"exclude": [
"../**/*.spec.js",
"../**/*.test.js",
"../**/*.spec.ts",
"../**/*.test.ts",
"../**/*.spec.tsx",
"../**/*.test.tsx",
"../**/*.spec.jsx",
"../**/*.test.jsx"
],
"include": ["../**/*"]
}
+3 -4
View File
@@ -1,11 +1,10 @@
{
"name": "explorer-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/explorer-e2e/src",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nx/cypress:cypress",
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/explorer-e2e/cypress.config.js",
"devServerTarget": "explorer:serve"
@@ -17,14 +16,14 @@
}
},
"lint": {
"executor": "@nx/linter:eslint",
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"]
}
},
"build": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/explorer-e2e/"
@@ -27,11 +27,6 @@ context('Home Page', function () {
16: 'Chain ID',
};
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('[data-testid="stats-title"]')
.each(($list, index) => {
cy.wrap($list).should('contain.text', statTitles[index]);
@@ -34,11 +34,6 @@ context('Network parameters page', { tags: '@smoke' }, function () {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.json.includes(parameterName)) {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
@@ -70,11 +65,6 @@ context('Network parameters page', { tags: '@smoke' }, function () {
if (this.networkParameterFormat.percentage.includes(parameterName)) {
const formattedPercentageParameter =
(parseFloat(parameterValue) * 100).toFixed(0) + '%';
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
@@ -158,11 +148,6 @@ context('Network parameters page', { tags: '@smoke' }, function () {
cy.convert_number_to_max_four_decimal(parameterValue)
.add_commas_to_number_if_large_enough()
.then((parameterValueFormatted) => {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
@@ -194,11 +179,6 @@ context('Network parameters page', { tags: '@smoke' }, function () {
cy.convert_number_to_max_eighteen_decimal(parameterValue)
.add_commas_to_number_if_large_enough()
.then((parameterValueFormatted) => {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
@@ -170,11 +170,6 @@ context.skip('Parties page', { tags: '@regression' }, function () {
const sideMenuBackground = '.absolute';
// Engage dark mode if not allready set
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.then((background_color) => {
@@ -60,31 +60,16 @@ context.skip('Transactions page', function () {
});
cy.get('block').should('not.be.empty');
cy.get('encoded-tnx').should('not.be.empty');
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('tx-type')
.should('not.be.empty')
.invoke('text')
.then((txTypeTxt) => {
if (txTypeTxt == 'Order Submission') {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('.hljs-attr')
.should('have.length.at.least', 8)
.each(($propertyName) => {
cy.wrap($propertyName).should('not.be.empty');
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('.hljs-string')
.should('have.length.at.least', 8)
.each(($propertyValue) => {
+1 -1
View File
@@ -1,7 +1,7 @@
{
"presets": [
[
"@nx/react/babel",
"@nrwl/react/babel",
{
"runtime": "automatic"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__"],
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+1 -1
View File
@@ -39,7 +39,7 @@ Example configurations are provided here:
For convenience, you can boot the app injecting one of the configurations above by running:
```bash
yarn env-cmd -f .\apps\explorer\.env.{env} yarn nx run explorer:serve # e.g. stagnet1
yarn nx run explorer:serve --env={env} # e.g. stagnet1
```
There are a few different configuration options offered for this app:
+2 -2
View File
@@ -4,8 +4,8 @@ export default {
displayName: 'explorer',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/explorer',
+8 -15
View File
@@ -1,11 +1,10 @@
{
"name": "explorer",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/explorer/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"executor": "./tools/executors/webpack:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
@@ -39,7 +38,7 @@
}
},
"serve": {
"executor": "@nx/webpack:dev-server",
"executor": "./tools/executors/webpack:serve",
"options": {
"port": 3000,
"buildTarget": "explorer:build:development",
@@ -53,28 +52,22 @@
}
},
"lint": {
"executor": "@nx/linter:eslint",
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/explorer/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/apps/explorer"],
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/apps/explorer"],
"options": {
"jestConfig": "apps/explorer/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"generate-types": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.71.4/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
@@ -82,7 +75,7 @@
}
},
"build-netlify": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"cp apps/explorer/netlify.toml netlify.toml",
@@ -91,7 +84,7 @@
}
},
"build-spec": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/explorer/tsconfig.spec.json"
@@ -1,15 +1,15 @@
import { useMemo } from 'react';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
import { AssetTypeMapping, AssetStatusMapping } from '@vegaprotocol/assets';
import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridColumn } from 'ag-grid-react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import type { VegaICellRendererParams } from '@vegaprotocol/datagrid';
import { useRef, useLayoutEffect } from 'react';
import { BREAKPOINT_MD } from '../../config/breakpoints';
import { useNavigate } from 'react-router-dom';
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community';
type AssetsTableProps = {
data: AssetFieldsFragment[] | null;
@@ -31,58 +31,6 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
};
}, []);
const columnDefs = useMemo<ColDef[]>(
() => [
{ headerName: t('Symbol'), field: 'symbol' },
{ headerName: t('Name'), field: 'name' },
{
flex: 2,
headerName: t('ID'),
field: 'id',
hide: window.innerWidth < BREAKPOINT_MD,
},
{
colId: 'type',
headerName: t('Type'),
field: 'source.__typename',
hide: window.innerWidth < BREAKPOINT_MD,
valueFormatter: ({ value }: { value?: string }) =>
value ? AssetTypeMapping[value].value : '',
},
{
headerName: t('Status'),
field: 'status',
hide: window.innerWidth < BREAKPOINT_MD,
valueFormatter: ({ value }: { value?: string }) =>
value ? AssetStatusMapping[value].value : '',
},
{
colId: 'actions',
headerName: '',
sortable: false,
filter: false,
resizable: false,
wrapText: true,
field: 'id',
cellRenderer: ({
value,
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<ButtonLink
onClick={(e) => {
navigate(value);
}}
>
{t('View details')}
</ButtonLink>
) : (
''
),
},
],
[navigate]
);
return (
<AgGrid
ref={ref}
@@ -98,11 +46,60 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
filterParams: { buttons: ['reset'] },
autoHeight: true,
}}
columnDefs={columnDefs}
suppressCellFocus={true}
onRowClicked={({ data }: RowClickedEvent) => {
navigate(data.id);
}}
/>
>
<AgGridColumn headerName={t('Symbol')} field="symbol" />
<AgGridColumn headerName={t('Name')} field="name" />
<AgGridColumn
flex="2"
headerName={t('ID')}
field="id"
hide={window.innerWidth < BREAKPOINT_MD}
/>
<AgGridColumn
colId="type"
headerName={t('Type')}
field="source.__typename"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetTypeMapping[value].value
}
/>
<AgGridColumn
headerName={t('Status')}
field="status"
hide={window.innerWidth < BREAKPOINT_MD}
valueFormatter={({ value }: { value?: string }) =>
value && AssetStatusMapping[value].value
}
/>
<AgGridColumn
colId="actions"
headerName=""
sortable={false}
filter={false}
resizable={false}
wrapText={true}
field="id"
cellRenderer={({
value,
}: VegaICellRendererParams<AssetFieldsFragment, 'id'>) =>
value ? (
<ButtonLink
onClick={(e) => {
navigate(value);
}}
>
{t('View details')}
</ButtonLink>
) : (
''
)
}
/>
</AgGrid>
);
};
@@ -1,9 +1,8 @@
import { useMemo } from 'react';
import type { MarketFieldsFragment } from '@vegaprotocol/markets';
import { t } from '@vegaprotocol/i18n';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
import type { ColDef } from 'ag-grid-community';
import { AgGridColumn } from 'ag-grid-react';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import type {
VegaICellRendererParams,
@@ -40,34 +39,54 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
};
}, []);
const columnDefs = useMemo<ColDef[]>(
() => [
{
colId: 'code',
headerName: t('Code'),
field: 'tradableInstrument.instrument.code',
},
{
colId: 'name',
headerName: t('Name'),
field: 'tradableInstrument.instrument.name',
},
{
headerName: t('Status'),
field: 'state',
hide: window.innerWidth <= BREAKPOINT_MD,
valueGetter: ({
return (
<AgGrid
ref={gridRef}
rowData={data}
getRowId={({ data }: { data: MarketFieldsFragment }) => data.id}
overlayNoRowsTemplate={t('This chain has no markets')}
domLayout="autoHeight"
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
autoHeight: true,
}}
suppressCellFocus={true}
onRowClicked={({ data, event }: RowClickedEvent) => {
if ((event?.target as HTMLElement).tagName.toUpperCase() !== 'BUTTON') {
navigate(data.id);
}
}}
>
<AgGridColumn
colId="code"
headerName={t('Code')}
field="tradableInstrument.instrument.code"
/>
<AgGridColumn
colId="name"
headerName={t('Name')}
field="tradableInstrument.instrument.name"
/>
<AgGridColumn
headerName={t('Status')}
field="state"
hide={window.innerWidth <= BREAKPOINT_MD}
valueGetter={({
data,
}: VegaValueGetterParams<MarketFieldsFragment>) => {
return data?.state ? MarketStateMapping[data?.state] : '-';
},
},
{
colId: 'asset',
headerName: t('Settlement asset'),
field: 'tradableInstrument.instrument.product.settlementAsset.symbol',
hide: window.innerWidth <= BREAKPOINT_MD,
cellRenderer: ({
}}
/>
<AgGridColumn
colId="asset"
headerName={t('Settlement asset')}
field="tradableInstrument.instrument.product.settlementAsset.symbol"
hide={window.innerWidth <= BREAKPOINT_MD}
cellRenderer={({
data,
}: VegaICellRendererParams<
MarketFieldsFragment,
@@ -86,19 +105,19 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
) : (
''
);
},
},
{
flex: 2,
headerName: t('Market ID'),
field: 'id',
hide: window.innerWidth <= BREAKPOINT_MD,
},
{
colId: 'actions',
headerName: '',
field: 'id',
cellRenderer: ({
}}
/>
<AgGridColumn
flex={2}
headerName={t('Market ID')}
field="id"
hide={window.innerWidth <= BREAKPOINT_MD}
/>
<AgGridColumn
colId="actions"
headerName=""
field="id"
cellRenderer={({
value,
}: VegaICellRendererParams<MarketFieldsFragment, 'id'>) =>
value ? (
@@ -107,34 +126,9 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
</Link>
) : (
''
),
},
],
[openAssetDetailsDialog]
);
return (
<AgGrid
ref={gridRef}
rowData={data}
getRowId={({ data }: { data: MarketFieldsFragment }) => data.id}
overlayNoRowsTemplate={t('This chain has no markets')}
domLayout="autoHeight"
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
filterParams: { buttons: ['reset'] },
autoHeight: true,
}}
columnDefs={columnDefs}
suppressCellFocus={true}
onRowClicked={({ data, event }: RowClickedEvent) => {
if ((event?.target as HTMLElement).tagName.toUpperCase() !== 'BUTTON') {
navigate(data.id);
)
}
}}
/>
/>
</AgGrid>
);
};
@@ -12,7 +12,7 @@ type Amend = components['schemas']['v1OrderAmendment'];
function renderAmendOrderDetails(
id: string,
version: number | undefined,
version: number,
amend: Amend,
mocks: MockedResponse[]
) {
@@ -25,11 +25,7 @@ function renderAmendOrderDetails(
);
}
function renderExistingAmend(
id: string,
version: number | undefined,
amend: Amend
) {
function renderExistingAmend(id: string, version: number, amend: Amend) {
const mocks = [
{
request: {
@@ -81,55 +77,6 @@ function renderExistingAmend(
},
},
},
{
request: {
query: ExplorerDeterministicOrderDocument,
variables: {
orderId: '123',
},
},
result: {
data: {
orderByID: {
__typename: 'Order',
id: '123',
type: 'GTT',
status: Schema.OrderStatus.STATUS_ACTIVE,
version: 100,
createdAt: '123',
updatedAt: '456',
expiresAt: '789',
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_GTC,
price: '200',
side: 'BUY',
remaining: '99',
rejectionReason: 'rejection',
reference: '123',
size: '200',
party: {
__typename: 'Party',
id: '234',
},
market: {
__typename: 'Market',
id: 'amend-to-order-latest-version',
state: 'STATUS_ACTIVE',
positionDecimalPlaces: 2,
decimalPlaces: '5',
tradableInstrument: {
instrument: {
name: 'amend-to-order-latest-version-test',
product: {
__typename: 'Future',
quoteName: '123',
},
},
},
},
},
},
},
},
{
request: {
query: ExplorerMarketDocument,
@@ -210,15 +157,4 @@ describe('Amend order details', () => {
expect(await res.findByText('New price')).toBeInTheDocument();
expect(await res.findByText('-7879')).toBeInTheDocument();
});
it('Fetches latest version when version is not specified', async () => {
const amend: Amend = {
price: '-7879',
};
const res = renderExistingAmend('123', undefined, amend);
expect(
await res.findByText('amend-to-order-latest-version')
).toBeInTheDocument();
});
});
@@ -12,7 +12,7 @@ import { wrapperClasses } from './deterministic-order-details';
export interface AmendOrderDetailsProps {
id: string;
amend: components['schemas']['v1OrderAmendment'];
// Version to fetch. Latest is provided by default
// Version to fetch, with 0 being 'latest' and 1 being 'first'. Defaults to 0
version?: number;
}
@@ -34,11 +34,13 @@ export function getSideDeltaColour(delta: string): string {
* @param param0
* @returns
*/
const AmendOrderDetails = ({ id, version, amend }: AmendOrderDetailsProps) => {
const variables = version ? { orderId: id, version } : { orderId: id };
const AmendOrderDetails = ({
id,
version = 0,
amend,
}: AmendOrderDetailsProps) => {
const { data, error } = useExplorerDeterministicOrderQuery({
variables,
variables: { orderId: id, version },
});
if (error || (data && !data.orderByID)) {
@@ -1,6 +1,7 @@
import type { ProposalListFieldsFragment } from '@vegaprotocol/proposals';
import { VoteProgress } from '@vegaprotocol/proposals';
import type { AgGridReact } from 'ag-grid-react';
import { AgGridColumn } from 'ag-grid-react';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import { AgGridLazy as AgGrid } from '@vegaprotocol/datagrid';
import type {
@@ -8,7 +9,7 @@ import type {
VegaValueFormatterParams,
} from '@vegaprotocol/datagrid';
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
import type { RowClickedEvent, ColDef } from 'ag-grid-community';
import type { RowClickedEvent } from 'ag-grid-community';
import { getDateTimeFormat } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import {
@@ -63,128 +64,7 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
title: '',
content: null,
});
const columnDefs = useMemo<ColDef[]>(
() => [
{
colId: 'title',
headerName: t('Title'),
field: 'rationale.title',
flex: 2,
wrapText: true,
},
{
colId: 'type',
maxWidth: 180,
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Type'),
field: 'terms.change.__typename',
},
{
maxWidth: 100,
headerName: t('State'),
field: 'state',
valueFormatter: ({
value,
}: VegaValueFormatterParams<ProposalListFieldsFragment, 'state'>) => {
return value ? ProposalStateMapping[value] : '-';
},
},
{
colId: 'voting',
maxWidth: 100,
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Voting'),
cellRenderer: ({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
if (data) {
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
const noTokens = new BigNumber(data.votes.no.totalTokens);
const totalTokensVoted = yesTokens.plus(noTokens);
const yesPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<div className="uppercase flex h-full items-center justify-center pt-2">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
/>
</div>
);
}
return '-';
},
},
{
colId: 'cDate',
maxWidth: 150,
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Closing date'),
field: 'terms.closingDatetime',
valueFormatter: ({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
'terms.closingDatetime'
>) => {
return value ? getDateTimeFormat().format(new Date(value)) : '-';
},
},
{
colId: 'eDate',
maxWidth: 150,
hide: window.innerWidth <= BREAKPOINT_MD,
headerName: t('Enactment date'),
field: 'terms.enactmentDatetime',
valueFormatte: ({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
'terms.enactmentDatetime'
>) => {
return value ? getDateTimeFormat().format(new Date(value)) : '-';
},
},
{
colId: 'actions',
minWidth: window.innerWidth > BREAKPOINT_MD ? 221 : 80,
maxWidth: 221,
sortable: false,
filter: false,
resizable: false,
cellRenderer: ({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
const proposalPage = tokenLink(
TOKEN_PROPOSAL.replace(':id', data?.id || '')
);
const openDialog = () => {
if (!data) return;
setDialog({
open: true,
title: data.rationale.title,
content: data.terms,
});
};
return (
<div className="pb-1">
<button className="underline max-md:hidden" onClick={openDialog}>
{t('View terms')}
</button>{' '}
<ExternalLink className="max-md:hidden" href={proposalPage}>
{t('Open in Governance')}
</ExternalLink>
<ExternalLink className="md:hidden" href={proposalPage}>
{t('Open')}
</ExternalLink>
</div>
);
},
},
],
[requiredMajorityPercentage, tokenLink]
);
return (
<>
<AgGrid
@@ -203,7 +83,6 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
filterParams: { buttons: ['reset'] },
autoHeight: true,
}}
columnDefs={columnDefs}
suppressCellFocus={true}
onRowClicked={({ data, event }: RowClickedEvent) => {
if (
@@ -215,7 +94,128 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
window.open(proposalPage, '_blank');
}
}}
/>
>
<AgGridColumn
colId="title"
headerName={t('Title')}
field="rationale.title"
flex={2}
wrapText={true}
/>
<AgGridColumn
colId="type"
maxWidth={180}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Type')}
field="terms.change.__typename"
/>
<AgGridColumn
maxWidth={100}
headerName={t('State')}
field="state"
valueFormatter={({
value,
}: VegaValueFormatterParams<ProposalListFieldsFragment, 'state'>) => {
return value ? ProposalStateMapping[value] : '-';
}}
/>
<AgGridColumn
colId="voting"
maxWidth={100}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Voting')}
cellRenderer={({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
if (data) {
const yesTokens = new BigNumber(data.votes.yes.totalTokens);
const noTokens = new BigNumber(data.votes.no.totalTokens);
const totalTokensVoted = yesTokens.plus(noTokens);
const yesPercentage = totalTokensVoted.isZero()
? new BigNumber(0)
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
return (
<div className="uppercase flex h-full items-center justify-center pt-2">
<VoteProgress
threshold={requiredMajorityPercentage}
progress={yesPercentage}
/>
</div>
);
}
return '-';
}}
/>
<AgGridColumn
colId="cDate"
maxWidth={150}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Closing date')}
field="terms.closingDatetime"
valueFormatter={({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
'terms.closingDatetime'
>) => {
return value ? getDateTimeFormat().format(new Date(value)) : '-';
}}
/>
<AgGridColumn
colId="eDate"
maxWidth={150}
hide={window.innerWidth <= BREAKPOINT_MD}
headerName={t('Enactment date')}
field="terms.enactmentDatetime"
valueFormatter={({
value,
}: VegaValueFormatterParams<
ProposalListFieldsFragment,
'terms.enactmentDatetime'
>) => {
return value ? getDateTimeFormat().format(new Date(value)) : '-';
}}
/>
<AgGridColumn
colId="actions"
minWidth={window.innerWidth > BREAKPOINT_MD ? 221 : 80}
maxWidth={221}
sortable={false}
filter={false}
resizable={false}
cellRenderer={({
data,
}: VegaICellRendererParams<ProposalListFieldsFragment>) => {
const proposalPage = tokenLink(
TOKEN_PROPOSAL.replace(':id', data?.id || '')
);
const openDialog = () => {
if (!data) return;
setDialog({
open: true,
title: data.rationale.title,
content: data.terms,
});
};
return (
<div className="pb-1">
<button
className="underline max-md:hidden"
onClick={openDialog}
>
{t('View terms')}
</button>{' '}
<ExternalLink className="max-md:hidden" href={proposalPage}>
{t('Open in Governance')}
</ExternalLink>
<ExternalLink className="md:hidden" href={proposalPage}>
{t('Open')}
</ExternalLink>
</div>
);
}}
/>
</AgGrid>
<JsonViewerDialog
open={dialog.open}
onChange={(isOpen) => setDialog({ ...dialog, open: isOpen })}
@@ -1,86 +0,0 @@
import { render } from '@testing-library/react';
import { TxDetailsLiquidityAmendment } from './tx-liquidity-amend';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter } from 'react-router-dom';
describe('TxDetailsLiquidityAmendment', () => {
const mockTxData = {
hash: 'test',
command: {
liquidityProvisionAmendment: {
marketId: 'BTC-USD',
commitmentAmount: 100,
fee: '0.01',
},
},
};
const mockPubKey = '123';
const mockBlockData = {
result: {
block: {
header: {
height: '123',
},
},
},
};
it('should render the component with correct data', () => {
const { getByText } = render(
<MockedProvider>
<MemoryRouter>
<TxDetailsLiquidityAmendment
txData={mockTxData as BlockExplorerTransactionResult}
pubKey={mockPubKey}
blockData={mockBlockData as TendermintBlocksResponse}
/>
</MemoryRouter>
</MockedProvider>
);
expect(getByText('Market')).toBeInTheDocument();
expect(getByText('BTC-USD')).toBeInTheDocument();
expect(getByText('Commitment amount')).toBeInTheDocument();
expect(getByText('100')).toBeInTheDocument();
expect(getByText('Fee')).toBeInTheDocument();
expect(getByText('1%')).toBeInTheDocument();
});
it('should display awaiting message when tx data is undefined', () => {
const { getByText } = render(
<MockedProvider>
<MemoryRouter>
<TxDetailsLiquidityAmendment
txData={undefined}
pubKey={mockPubKey}
blockData={mockBlockData as TendermintBlocksResponse}
/>
</MemoryRouter>
</MockedProvider>
);
expect(
getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
it('should display awaiting message when liquidityProvisionAmendment is undefined', () => {
const { getByText } = render(
<MockedProvider>
<MemoryRouter>
<TxDetailsLiquidityAmendment
txData={{ command: {} } as BlockExplorerTransactionResult}
pubKey={mockPubKey}
blockData={mockBlockData as TendermintBlocksResponse}
/>
</MemoryRouter>
</MockedProvider>
);
expect(
getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
});
@@ -7,7 +7,6 @@ import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
import PriceInMarket from '../../price-in-market/price-in-market';
import BigNumber from 'bignumber.js';
export type LiquidityAmendment =
components['schemas']['v1LiquidityProvisionAmendment'];
@@ -35,10 +34,6 @@ export const TxDetailsLiquidityAmendment = ({
txData.command.liquidityProvisionAmendment;
const marketId: string = amendment.marketId || '-';
const fee = amendment.fee
? new BigNumber(amendment.fee).times(100).toString()
: '-';
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
@@ -68,7 +63,7 @@ export const TxDetailsLiquidityAmendment = ({
{amendment.fee ? (
<TableRow modifier="bordered">
<TableCell>{t('Fee')}</TableCell>
<TableCell>{fee}%</TableCell>
<TableCell>{amendment.fee}%</TableCell>
</TableRow>
) : null}
</TableWithTbody>
@@ -1,86 +0,0 @@
import { render } from '@testing-library/react';
import { TxDetailsLiquiditySubmission } from './tx-liquidity-submission';
import type { TendermintBlocksResponse } from '../../../routes/blocks/tendermint-blocks-response';
import type { BlockExplorerTransactionResult } from '../../../routes/types/block-explorer-response';
import { MockedProvider } from '@apollo/client/testing';
import { MemoryRouter } from 'react-router-dom';
describe('TxDetailsLiquiditySubmission', () => {
const mockTxData = {
hash: 'test',
command: {
liquidityProvisionSubmission: {
marketId: 'BTC-USD',
commitmentAmount: 100,
fee: '0.01',
},
},
};
const mockPubKey = '123';
const mockBlockData = {
result: {
block: {
header: {
height: '123',
},
},
},
};
it('should render the component with correct data', () => {
const { getByText } = render(
<MockedProvider>
<MemoryRouter>
<TxDetailsLiquiditySubmission
txData={mockTxData as BlockExplorerTransactionResult}
pubKey={mockPubKey}
blockData={mockBlockData as TendermintBlocksResponse}
/>
</MemoryRouter>
</MockedProvider>
);
expect(getByText('Market')).toBeInTheDocument();
expect(getByText('BTC-USD')).toBeInTheDocument();
expect(getByText('Commitment amount')).toBeInTheDocument();
expect(getByText('100')).toBeInTheDocument();
expect(getByText('Fee')).toBeInTheDocument();
expect(getByText('1%')).toBeInTheDocument();
});
it('should display awaiting message when tx data is undefined', () => {
const { getByText } = render(
<MockedProvider>
<MemoryRouter>
<TxDetailsLiquiditySubmission
txData={undefined}
pubKey={mockPubKey}
blockData={mockBlockData as TendermintBlocksResponse}
/>
</MemoryRouter>
</MockedProvider>
);
expect(
getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
it('should display awaiting message when liquidityProvisionSubmission is undefined', () => {
const { getByText } = render(
<MockedProvider>
<MemoryRouter>
<TxDetailsLiquiditySubmission
txData={{ command: {} } as BlockExplorerTransactionResult}
pubKey={mockPubKey}
blockData={mockBlockData as TendermintBlocksResponse}
/>
</MemoryRouter>
</MockedProvider>
);
expect(
getByText('Awaiting Block Explorer transaction details')
).toBeInTheDocument();
});
});
@@ -7,7 +7,6 @@ import { TableCell, TableRow, TableWithTbody } from '../../table';
import type { components } from '../../../../types/explorer';
import { LiquidityProvisionDetails } from './liquidity-provision/liquidity-provision-details';
import PriceInMarket from '../../price-in-market/price-in-market';
import BigNumber from 'bignumber.js';
export type LiquiditySubmission =
components['schemas']['v1LiquidityProvisionSubmission'];
@@ -34,10 +33,6 @@ export const TxDetailsLiquiditySubmission = ({
txData.command.liquidityProvisionSubmission;
const marketId: string = submission.marketId || '-';
const fee = submission.fee
? new BigNumber(submission.fee).times(100).toString()
: '-';
return (
<>
<TableWithTbody className="mb-8" allowWrap={true}>
@@ -67,7 +62,7 @@ export const TxDetailsLiquiditySubmission = ({
{submission.fee ? (
<TableRow modifier="bordered">
<TableCell>{t('Fee')}</TableCell>
<TableCell>{fee}%</TableCell>
<TableCell>{submission.fee}%</TableCell>
</TableRow>
) : null}
</TableWithTbody>
+3 -2
View File
@@ -1,5 +1,6 @@
@import 'ag-grid-community/styles/ag-grid.css';
@import 'ag-grid-community/styles/ag-theme-balham.css';
@import 'ag-grid-community/dist/styles/ag-grid.css';
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
/* You can add global styles to this file, and also import other style files */
@tailwind base;
+1 -1
View File
@@ -1,5 +1,5 @@
const { join } = require('path');
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
const theme = require('../../libs/tailwindcss-config/src/theme');
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
+2 -2
View File
@@ -5,8 +5,8 @@
"types": ["node"]
},
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"**/*.spec.ts",
+2 -2
View File
@@ -18,7 +18,7 @@
"jest.config.ts"
],
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
]
}
+2 -4
View File
@@ -1,8 +1,6 @@
const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const SentryPlugin = require('@sentry/webpack-plugin');
module.exports = composePlugins(withNx(), withReact(), (config) => {
module.exports = (config, context) => {
const additionalPlugins = process.env.SENTRY_AUTH_TOKEN
? [
new SentryPlugin({
@@ -16,4 +14,4 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
...config,
plugins: [...additionalPlugins, ...config.plugins],
};
});
};
+3 -4
View File
@@ -1,11 +1,10 @@
{
"name": "governance-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/governance-e2e/src",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@nx/cypress:cypress",
"executor": "@nrwl/cypress:cypress",
"options": {
"cypressConfig": "apps/governance-e2e/cypress.config.js",
"devServerTarget": "governance:serve"
@@ -17,14 +16,14 @@
}
},
"lint": {
"executor": "@nx/linter:eslint",
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/governance-e2e/**/*.{js,ts}"]
}
},
"build": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/governance-e2e/"
@@ -28,17 +28,18 @@ import type { testFreeformProposal } from '../../support/common-interfaces';
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
'[data-testid="vote-progress-indicator-percentage-for"]';
const proposalVoteProgressAgainstPercentage =
'vote-progress-indicator-percentage-against';
const proposalVoteProgressForTokens = 'vote-progress-indicator-tokens-for';
'[data-testid="vote-progress-indicator-percentage-against"]';
const proposalVoteProgressForTokens =
'[data-testid="vote-progress-indicator-tokens-for"]';
const proposalVoteProgressAgainstTokens =
'vote-progress-indicator-tokens-against';
const changeVoteButton = 'change-vote-button';
const proposalDetailsTitle = 'proposal-title';
const proposalDetailsDescription = 'proposal-description';
const openProposals = 'open-proposals';
const viewProposalButton = 'view-proposal-btn';
'[data-testid="vote-progress-indicator-tokens-against"]';
const changeVoteButton = '[data-testid="change-vote-button"]';
const proposalDetailsTitle = '[data-testid="proposal-title"]';
const proposalDetailsDescription = '[data-testid="proposal-description"]';
const openProposals = '[data-testid="open-proposals"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const proposalDescriptionToggle = 'proposal-description-toggle';
const voteBreakdownToggle = 'vote-breakdown-toggle';
const proposalTermsToggle = 'proposal-json-toggle';
@@ -71,18 +72,18 @@ describe(
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
cy.getByTestId(openProposals).within(() => {
cy.get(openProposals).within(() => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.getByTestId(viewProposalButton).should('be.visible').click();
cy.get(viewProposalButton).should('be.visible').click();
});
});
cy.getByTestId(proposalDetailsTitle).should(
cy.get(proposalDetailsTitle).should(
'contain.text',
rawProposal.rationale.title
);
cy.getByTestId(proposalDescriptionToggle).click();
cy.getByTestId('proposal-description-toggle');
cy.getByTestId(proposalDetailsDescription)
cy.get(proposalDetailsDescription)
.find('p')
.should('have.text', proposalDescription);
});
@@ -98,11 +99,6 @@ describe(
.and('have.length', 64);
cy.getByTestId(proposalTermsToggle).click();
// 3001-VOTE-052 3001-VOTE-010
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('code.language-json')
.should('exist')
.within(() => {
@@ -121,7 +117,7 @@ describe(
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
cy.wrap(
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
@@ -143,7 +139,7 @@ describe(
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
});
cy.contains('Participation: Not Met 0.00 0.00%(0.00% Required)').should(
@@ -170,7 +166,7 @@ describe(
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
});
// 3001-VOTE-080
@@ -187,16 +183,14 @@ describe(
.contains(votedDate)
.should('be.visible');
});
cy.getByTestId(proposalVoteProgressForPercentage) // 3001-VOTE-072
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
.contains('100.00%')
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
cy.get(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
cy.getByTestId(proposalVoteProgressForTokens)
.contains('1.00')
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstTokens)
cy.get(proposalVoteProgressForTokens).contains('1.00').and('be.visible');
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
@@ -217,15 +211,15 @@ describe(
getProposalInformationFromTable('Number of voting parties')
.should('have.text', '1')
.and('be.visible');
cy.getByTestId(changeVoteButton).should('be.visible').click();
cy.get(changeVoteButton).should('be.visible').click();
voteForProposal('for');
// 3001-VOTE-064
getProposalInformationFromTable('Tokens for proposal')
.should('have.text', (1).toFixed(2))
.and('be.visible');
cy.getByTestId(changeVoteButton).should('be.visible').click();
cy.get(changeVoteButton).should('be.visible').click();
voteForProposal('against');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
cy.get(proposalVoteProgressAgainstPercentage)
.contains('100.00%')
.and('be.visible');
getProposalInformationFromTable('Tokens against proposal')
@@ -242,15 +236,13 @@ describe(
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
});
voteForProposal('for');
// 3001-VOTE-079
cy.contains('You voted: For').should('be.visible');
cy.getByTestId(proposalVoteProgressForTokens)
.contains('1')
.and('be.visible');
cy.get(proposalVoteProgressForTokens).contains('1').and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
getProposalInformationFromTable('Total Supply')
.invoke('text')
@@ -266,22 +258,22 @@ describe(
navigateTo(navigation.proposals);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
});
cy.getByTestId(proposalVoteProgressForPercentage)
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstPercentage)
cy.get(proposalVoteProgressAgainstPercentage)
.contains('0.00%')
.and('be.visible');
// 3001-VOTE-065
cy.getByTestId(changeVoteButton).should('be.visible').click();
cy.get(changeVoteButton).should('be.visible').click();
voteForProposal('for');
cy.getByTestId(proposalVoteProgressForTokens)
cy.get(proposalVoteProgressForTokens)
.contains(tokensRequiredToAchieveResult)
.and('be.visible');
cy.getByTestId(proposalVoteProgressAgainstTokens)
cy.get(proposalVoteProgressAgainstTokens)
.contains('0.00')
.and('be.visible');
cy.getByTestId(voteBreakdownToggle).click();
@@ -318,7 +310,7 @@ describe(
createRawProposal();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
voteForProposal('for');
cy.contains('You voted: For').should('be.visible');
@@ -327,16 +319,13 @@ describe(
stakingPageAssociateTokens('2');
navigateTo(navigation.proposals);
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
cy.getByTestId('you-voted').should('not.exist');
voteForProposal('against');
cy.contains('You voted: Against').should('be.visible');
switchVegaWalletPubKey();
cy.getByTestId(proposalVoteProgressForTokens).should(
'contain.text',
'1.00'
);
cy.get(proposalVoteProgressForTokens).should('contain.text', '1.00');
// Checking vote status for different public keys is displayed correctly
cy.contains('You voted: For').should('be.visible');
});
@@ -19,13 +19,13 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const closedProposals = 'closed-proposals';
const proposalStatus = 'proposal-status';
const viewProposalButton = 'view-proposal-btn';
const votesTable = 'votes-table';
const openProposals = 'open-proposals';
const closedProposals = '[data-testid="closed-proposals"]';
const proposalStatus = '[data-testid="proposal-status"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const votesTable = '[data-testid="votes-table"]';
const openProposals = '[data-testid="open-proposals"]';
const proposalVoteProgressForPercentage =
'vote-progress-indicator-percentage-for';
'[data-testid="vote-progress-indicator-percentage-for"]';
const proposalTimeout = { timeout: 8000 };
context(
@@ -55,18 +55,18 @@ context(
cy.createMarket();
cy.reload();
waitForSpinner();
cy.getByTestId(closedProposals).within(() => {
cy.get(closedProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
cy.getByTestId(viewProposalButton).click();
cy.get(proposalStatus).should('have.text', 'Enacted');
cy.get(viewProposalButton).click();
});
});
cy.getByTestId('proposal-type').should('have.text', 'New market');
cy.getByTestId(proposalStatus).should('have.text', 'Enacted');
cy.getByTestId(votesTable).within(() => {
cy.get(proposalStatus).should('have.text', 'Enacted');
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
});
@@ -81,27 +81,27 @@ context(
navigateTo(navigation.proposals);
cy.reload();
waitForSpinner();
cy.getByTestId(openProposals).within(() => {
cy.get(openProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => cy.getByTestId(viewProposalButton).click());
.within(() => cy.get(viewProposalButton).click());
});
cy.getByTestId(proposalStatus).should('have.text', 'Open');
cy.get(proposalStatus).should('have.text', 'Open');
voteForProposal('for');
cy.getByTestId(proposalStatus, proposalTimeout)
cy.get(proposalStatus, proposalTimeout)
.should('have.text', 'Passed')
.then(() => {
cy.getByTestId(proposalStatus, proposalTimeout).should(
cy.get(proposalStatus, proposalTimeout).should(
'have.text',
'Enacted'
);
});
cy.getByTestId(votesTable).within(() => {
cy.get(votesTable).within(() => {
cy.contains('Vote passed.').should('be.visible');
cy.contains('Voting has ended.').should('be.visible');
});
cy.getByTestId(proposalVoteProgressForPercentage)
cy.get(proposalVoteProgressForPercentage)
.contains('100.00%')
.and('be.visible');
});
@@ -115,18 +115,15 @@ context(
navigateTo(navigation.proposals);
cy.reload();
waitForSpinner();
cy.getByTestId(openProposals, { timeout: 6000 }).within(() => {
cy.get(openProposals, { timeout: 6000 }).within(() => {
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => cy.getByTestId(viewProposalButton).click());
.within(() => cy.get(viewProposalButton).click());
});
cy.getByTestId(proposalStatus).should('have.text', 'Open');
cy.get(proposalStatus).should('have.text', 'Open');
voteForProposal('for');
cy.getByTestId(proposalStatus, proposalTimeout).should(
'have.text',
'Enacted'
);
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Enacted');
});
// 3001-VOTE-048 3001-VOTE-049 3001-VOTE-050
@@ -137,17 +134,14 @@ context(
navigateTo(navigation.proposals);
cy.reload();
waitForSpinner();
cy.getByTestId(openProposals).within(() => {
cy.get(openProposals).within(() => {
cy.contains(proposalTitle)
.parentsUntil(proposalListItem)
.last()
.within(() => cy.getByTestId(viewProposalButton).click());
.within(() => cy.get(viewProposalButton).click());
});
cy.getByTestId(proposalStatus).should('have.text', 'Open');
cy.getByTestId(proposalStatus, proposalTimeout).should(
'have.text',
'Declined'
);
cy.get(proposalStatus).should('have.text', 'Open');
cy.get(proposalStatus, proposalTimeout).should('have.text', 'Declined');
getProposalInformationFromTable('Rejection reason')
.contains('PROPOSAL_ERROR_PARTICIPATION_THRESHOLD_NOT_REACHED')
.and('be.visible');
@@ -36,19 +36,20 @@ import {
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
const vegaWalletStakedBalances = 'vega-wallet-balance-staked-validators';
const vegaWalletAssociatedBalance = 'associated-amount';
const vegaWalletNameElement = 'wallet-name';
const vegaWallet = 'vega-wallet';
const connectToVegaWalletButton = 'connect-to-vega-wallet-btn';
const newProposalSubmitButton = 'proposal-submit';
const viewProposalButton = 'view-proposal-btn';
const rawProposalData = 'proposal-data';
const voteButtons = 'vote-buttons';
const vegaWalletStakedBalances =
'[data-testid="vega-wallet-balance-staked-validators"]';
const vegaWalletAssociatedBalance = '[data-testid="associated-amount"]';
const vegaWalletNameElement = '[data-testid="wallet-name"]';
const vegaWallet = '[data-testid="vega-wallet"]';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const rawProposalData = '[data-testid="proposal-data"]';
const voteButtons = '[data-testid="vote-buttons"]';
const rejectProposalsLink = '[href="/proposals/rejected"]';
const feedbackError = 'Error';
const noOpenProposals = 'no-open-proposals';
const noClosedProposals = 'no-closed-proposals';
const feedbackError = '[data-testid="Error"]';
const noOpenProposals = '[data-testid="no-open-proposals"]';
const noClosedProposals = '[data-testid="no-closed-proposals"]';
const txTimeout = Cypress.env('txTimeout');
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
@@ -92,10 +93,10 @@ context(
// Test can only pass if run before other proposal tests.
it.skip('Should be able to see that no proposals exist', function () {
// 3001-VOTE-003
cy.getByTestId(noOpenProposals)
cy.get(noOpenProposals)
.should('be.visible')
.and('have.text', 'There are no open or yet to enact proposals');
cy.getByTestId(noClosedProposals)
cy.get(noClosedProposals)
.should('be.visible')
.and('have.text', 'There are no enacted or rejected proposals');
});
@@ -125,10 +126,7 @@ context(
stakingValidatorPageAddStake('2');
closeStakingDialog();
cy.getByTestId(vegaWalletStakedBalances, txTimeout).should(
'contain',
'2'
);
cy.get(vegaWalletStakedBalances, txTimeout).should('contain', '2');
createRawProposal();
});
@@ -170,7 +168,7 @@ context(
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.contains('Rejected').should('be.visible');
cy.contains('Close time too late').should('be.visible');
cy.getByTestId(viewProposalButton).click();
cy.get(viewProposalButton).click();
});
});
cy.getByTestId('proposal-status').should('have.text', 'Rejected');
@@ -187,14 +185,14 @@ context(
const errorMsg =
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)';
vegaWalletTeardown();
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).contains(
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'0.00',
txTimeout
);
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.getByTestId(feedbackError).should('have.text', errorMsg);
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
});
@@ -207,7 +205,7 @@ context(
goToMakeNewProposal(governanceProposalType.RAW);
enterRawProposalBody(createTenDigitUnixTimeStampForSpecifiedDays(8));
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.getByTestId(feedbackError).should('have.text', errorMsg);
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
});
@@ -223,17 +221,17 @@ context(
createTenDigitUnixTimeStampForSpecifiedDays(8);
freeformProposal.unexpected = `i shouldn't be here`;
const proposalPayload = JSON.stringify(freeformProposal);
cy.getByTestId(rawProposalData).type(proposalPayload, {
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.getByTestId(feedbackError).should('have.text', errorMsg);
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
cy.getByTestId(rawProposalData)
cy.get(rawProposalData)
.invoke('val')
.should('contain', "i shouldn't be here");
});
@@ -251,15 +249,15 @@ context(
rawProposal.terms.unexpectedField = `i shouldn't be here`;
const proposalPayload = JSON.stringify(rawProposal);
cy.getByTestId(rawProposalData).type(proposalPayload, {
cy.get(rawProposalData).type(proposalPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
cy.getByTestId(feedbackError).should('have.text', errorMsg);
cy.get(feedbackError).should('have.text', errorMsg);
closeDialog();
});
@@ -267,10 +265,10 @@ context(
// 3006-PASC-006 3006-PASC-007 3008-PFRO-018 3008-PFRO-019 3003-PMAN-006 3003-PMAN-007
it('Unable to submit proposal without valid json', function () {
goToMakeNewProposal(governanceProposalType.RAW);
cy.getByTestId(newProposalSubmitButton).click();
cy.get(newProposalSubmitButton).click();
cy.getByTestId('input-error-text').should('have.text', 'Required');
cy.getByTestId(rawProposalData).type('Not a valid json string');
cy.getByTestId(newProposalSubmitButton).click();
cy.get(rawProposalData).type('Not a valid json string');
cy.get(newProposalSubmitButton).click();
cy.getByTestId('input-error-text').should(
'have.text',
'Must be valid JSON'
@@ -285,22 +283,22 @@ context(
submitUniqueRawProposal({ proposalTitle: proposalTitle });
ethereumWalletConnect();
stakingPageDisassociateTokens('0.0001');
cy.getByTestId(vegaWallet)
cy.get(vegaWallet)
.first()
.within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'0.9999'
);
});
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
cy.contains('Vote breakdown').should('be.visible', {
timeout: 10000,
});
cy.getByTestId(voteButtons).should('not.exist');
cy.get(voteButtons).should('not.exist');
cy.getByTestId('min-proposal-requirements').should(
'have.text',
`You must have at least ${this.minVoterBalance} VEGA associated to vote on this proposal`
@@ -313,20 +311,20 @@ context(
cy.get('[data-testid="disconnect"]').click();
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() =>
cy.getByTestId(viewProposalButton).click()
cy.get(viewProposalButton).click()
);
});
// 3001-VOTE-075
// 3001-VOTE-076
cy.getByTestId(connectToVegaWalletButton)
cy.get(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet')
.click();
cy.getByTestId('connector-jsonRpc').click();
cy.getByTestId(vegaWalletNameElement).should('be.visible');
cy.getByTestId(connectToVegaWalletButton).should('not.exist');
cy.get(vegaWalletNameElement).should('be.visible');
cy.get(connectToVegaWalletButton).should('not.exist');
// 3001-VOTE-100
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).contains(
cy.get(vegaWalletAssociatedBalance, txTimeout).contains(
'1.00',
txTimeout
);
@@ -31,25 +31,27 @@ import {
} from '../../support/wallet-functions';
const proposalListItem = '[data-testid="proposals-list-item"]';
const openProposals = 'open-proposals';
const proposalType = 'proposal-type';
const proposalDetails = 'proposal-details';
const newProposalSubmitButton = 'proposal-submit';
const proposalVoteDeadline = 'proposal-vote-deadline';
const proposalParameterSelect = 'proposal-parameter-select';
const proposalMarketSelect = 'proposal-market-select';
const newProposalTitle = 'proposal-title';
const newProposalDescription = 'proposal-description';
const newProposalTerms = 'proposal-terms';
const newProposedParameterValue = 'selected-proposal-param-new-value';
const minVoteDeadline = 'min-vote';
const maxVoteDeadline = 'max-vote';
const minValidationDeadline = 'min-validation';
const minEnactDeadline = 'min-enactment';
const maxEnactDeadline = 'max-enactment';
const inputError = 'input-error-text';
const enactmentDeadlineError = 'enactment-before-voting-deadline';
const proposalDownloadBtn = 'proposal-download-json';
const openProposals = '[data-testid="open-proposals"]';
const proposalType = '[data-testid="proposal-type"]';
const proposalDetails = '[data-testid="proposal-details"]';
const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const proposalVoteDeadline = '[data-testid="proposal-vote-deadline"]';
const proposalParameterSelect = '[data-testid="proposal-parameter-select"]';
const proposalMarketSelect = '[data-testid="proposal-market-select"]';
const newProposalTitle = '[data-testid="proposal-title"]';
const newProposalDescription = '[data-testid="proposal-description"]';
const newProposalTerms = '[data-testid="proposal-terms"]';
const newProposedParameterValue =
'[data-testid="selected-proposal-param-new-value"]';
const minVoteDeadline = '[data-testid="min-vote"]';
const maxVoteDeadline = '[data-testid="max-vote"]';
const minValidationDeadline = '[data-testid="min-validation"]';
const minEnactDeadline = '[data-testid="min-enactment"]';
const maxEnactDeadline = '[data-testid="max-enactment"]';
const inputError = '[data-testid="input-error-text"]';
const enactmentDeadlineError =
'[data-testid="enactment-before-voting-deadline"]';
const proposalDownloadBtn = '[data-testid="proposal-download-json"]';
const feedbackError = '[data-testid="Error"]';
const viewProposalBtn = 'view-proposal-btn';
const liquidityVoteStatus = 'liquidity-votes-status';
@@ -86,20 +88,18 @@ context(
it('Unable to submit network parameter with missing/invalid fields', function () {
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.getByTestId(proposalDownloadBtn).click();
cy.getByTestId(inputError).should('have.length', 3);
cy.getByTestId(newProposalTitle).type(
cy.get(proposalDownloadBtn).click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type(
'Invalid update network parameter proposal'
);
cy.getByTestId(newProposalDescription).type(
'E2E invalid test for proposals'
);
cy.getByTestId(proposalParameterSelect).select(
cy.get(newProposalDescription).type('E2E invalid test for proposals');
cy.get(proposalParameterSelect).select(
'spam_protection_proposal_min_tokens'
);
cy.getByTestId(newProposedParameterValue).type('0');
cy.getByTestId(proposalVoteDeadline).clear().type('0');
cy.getByTestId(proposalDownloadBtn)
cy.get(newProposedParameterValue).type('0');
cy.get(proposalVoteDeadline).clear().type('0');
cy.get(proposalDownloadBtn)
.click()
.then(() => {
cy.wrap(
@@ -109,7 +109,7 @@ context(
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.getByTestId(newProposalSubmitButton).click();
cy.get(newProposalSubmitButton).click();
validateDialogContentMsg('PROPOSAL_ERROR_CLOSE_TIME_TOO_SOON');
});
@@ -117,33 +117,29 @@ context(
it('Able to download and submit network param proposal', function () {
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
// 3007-PNEC-006
cy.getByTestId(newProposalTitle)
cy.get(newProposalTitle)
.siblings()
.should('contain.text', '(100 characters or less)');
// 3007-PNEC-004 3007-PNEC-005
cy.getByTestId(newProposalTitle).type(
'Test update network parameter proposal'
);
cy.get(newProposalTitle).type('Test update network parameter proposal');
// 3007-PNEC-009
cy.getByTestId(newProposalDescription)
cy.get(newProposalDescription)
.siblings()
.should('contain.text', '(20,000 characters or less)');
// 3007-PNEC-007 3007-PNEC-008
cy.getByTestId(newProposalDescription).type(
'E2E test for downloading proposals'
);
cy.get(newProposalDescription).type('E2E test for downloading proposals');
// 3007-PNEC-010
cy.getByTestId(proposalParameterSelect).select(
cy.get(proposalParameterSelect).select(
'governance_proposal_asset_minClose'
);
// 3007-PNEC-011
cy.getByTestId(newProposedParameterValue).type('10s');
cy.get(newProposedParameterValue).type('10s');
// 3007-PNEC-012
cy.getByTestId(proposalVoteDeadline).clear().type('2');
cy.get(proposalVoteDeadline).clear().type('2');
// 3007-PNEC-013 3007-PNEC-014
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
// 3007-PNEC-015
cy.getByTestId(maxEnactDeadline).click();
cy.get(maxEnactDeadline).click();
// 3007-PNEC-016
cy.getByTestId('enactment-date').invoke('text').should('not.be.empty');
// 3007-PNEC-017
@@ -152,7 +148,7 @@ context(
).should('be.visible');
// 3007-PNE-018
cy.log('Download updated proposal file');
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -182,21 +178,19 @@ context(
it('Unable to submit network parameter proposal with vote deadline above enactment deadline', function () {
navigateTo(navigation.proposals);
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.getByTestId(newProposalTitle).type(
'Test update network parameter proposal'
);
cy.getByTestId(newProposalDescription).type('invalid deadlines');
cy.getByTestId(proposalParameterSelect).select(
cy.get(newProposalTitle).type('Test update network parameter proposal');
cy.get(newProposalDescription).type('invalid deadlines');
cy.get(proposalParameterSelect).select(
'spam_protection_proposal_min_tokens'
);
cy.getByTestId(newProposedParameterValue).type('0');
cy.getByTestId(proposalVoteDeadline).clear().type('0');
cy.getByTestId(maxVoteDeadline).click();
cy.getByTestId(enactmentDeadlineError).should(
cy.get(newProposedParameterValue).type('0');
cy.get(proposalVoteDeadline).clear().type('0');
cy.get(maxVoteDeadline).click();
cy.get(enactmentDeadlineError).should(
'have.text',
'The proposal will fail if enactment is earlier than the voting deadline'
);
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -207,7 +201,7 @@ context(
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.getByTestId(newProposalSubmitButton).click();
cy.get(newProposalSubmitButton).click();
validateFeedBackMsg(
'Invalid params: proposal_submission.terms.closing_timestamp (cannot be after enactment time)'
);
@@ -220,16 +214,16 @@ context(
function () {
const proposalTitle = 'Test new market proposal';
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.getByTestId(newProposalTitle).type('Test new market proposal');
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
const newMarketPayload = JSON.stringify(newMarketProposal);
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
cy.get(newProposalTerms).type(newMarketPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -265,19 +259,19 @@ context(
'Invalid params: the transaction does not use a valid Vega command: unknown field "invalid" in vega.NewMarket';
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.getByTestId(proposalDownloadBtn).should('be.visible').click();
cy.getByTestId(inputError).should('have.length', 3);
cy.getByTestId(newProposalTitle).type('Test new market proposal');
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
cy.get(newProposalTitle).type('Test new market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-market').then((newMarketProposal) => {
newMarketProposal.invalid = 'I am an invalid field';
const newMarketPayload = JSON.stringify(newMarketProposal);
cy.getByTestId(newProposalTerms).type(newMarketPayload, {
cy.get(newProposalTerms).type(newMarketPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -288,7 +282,7 @@ context(
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
validateFeedBackMsg(errorMsg);
});
@@ -299,19 +293,17 @@ context(
switchVegaWalletPubKey();
stakingPageAssociateTokens('1');
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.getByTestId(newProposalTitle).type(
'Test update market proposal - rejected'
);
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.getByTestId(proposalMarketSelect).select('Test market 1');
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.getByTestId(newProposalTerms).type(newUpdateMarketProposal, {
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -322,7 +314,7 @@ context(
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
validateDialogContentMsg('PROPOSAL_ERROR_INSUFFICIENT_EQUITY_LIKE_SHARE');
closeDialog();
@@ -340,19 +332,17 @@ context(
vegaWalletPublicKey
);
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.getByTestId(newProposalTitle).type(
'Test update market proposal - rejected'
);
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.getByTestId(proposalMarketSelect).select('Test market 1');
cy.get(newProposalTitle).type('Test update market proposal - rejected');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.getByTestId(newProposalTerms).type(newUpdateMarketProposal, {
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -363,7 +353,7 @@ context(
submitUniqueRawProposal({ proposalBody: filePath, submit: false });
});
});
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
cy.contains('Transaction failed', proposalTimeout).should('be.visible');
validateFeedBackMsg(
'Network error: the network blocked the transaction through the spam protection: party has insufficient associated governance tokens in their staking account to submit proposal request (ABCI code 89)'
@@ -378,9 +368,9 @@ context(
vegaWalletPublicKey
);
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.getByTestId(newProposalTitle).type('Test update market proposal');
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.getByTestId(proposalMarketSelect).select('Test market 1');
cy.get(newProposalTitle).type('Test update market proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.get(proposalMarketSelect).select('Test market 1');
cy.get('[data-testid="update-market-details"]').within(() => {
cy.get('dd').eq(0).should('have.text', 'Test market 1');
cy.get('dd').eq(1).should('have.text', 'TEST.24h');
@@ -395,12 +385,12 @@ context(
});
cy.fixture('/proposals/update-market').then((updateMarketProposal) => {
const newUpdateMarketProposal = JSON.stringify(updateMarketProposal);
cy.getByTestId(newProposalTerms).type(newUpdateMarketProposal, {
cy.get(newProposalTerms).type(newUpdateMarketProposal, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -447,19 +437,19 @@ context(
it('Able to submit new asset proposal using min deadlines', function () {
const proposalTitle = 'Test new asset proposal';
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.getByTestId(newProposalTitle).type(proposalTitle);
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.get(newProposalTitle).type(proposalTitle);
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/new-asset').then((newAssetProposal) => {
const newAssetPayload = JSON.stringify(newAssetProposal);
cy.getByTestId(newProposalTerms).type(newAssetPayload, {
cy.get(newProposalTerms).type(newAssetPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
});
cy.getByTestId(minVoteDeadline).click();
cy.getByTestId(minValidationDeadline).click();
cy.getByTestId(minEnactDeadline).click();
cy.getByTestId(proposalDownloadBtn)
cy.get(minVoteDeadline).click();
cy.get(minValidationDeadline).click();
cy.get(minEnactDeadline).click();
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -470,9 +460,9 @@ context(
submitUniqueRawProposal({ proposalBody: filePath, submit: false }); // 3005-PASN-003
});
});
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
closeDialog();
cy.getByTestId(newProposalSubmitButton).should('be.visible').click();
cy.get(newProposalSubmitButton).should('be.visible').click();
// cannot submit a proposal with ERC20 address already in use
cy.contains('Proposal rejected', proposalTimeout).should('be.visible');
validateDialogContentMsg('PROPOSAL_ERROR_ERC20_ADDRESS_ALREADY_IN_USE');
@@ -493,8 +483,8 @@ context(
it('Unable to submit new asset proposal with missing/invalid fields', function () {
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.getByTestId(proposalDownloadBtn).should('be.visible').click();
cy.getByTestId(inputError).should('have.length', 3);
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
});
it('Able to submit update asset proposal using min deadline', function () {
@@ -503,9 +493,9 @@ context(
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails();
cy.getByTestId(minVoteDeadline).click();
cy.getByTestId(minEnactDeadline).click();
cy.getByTestId(proposalDownloadBtn)
cy.get(minVoteDeadline).click();
cy.get(minEnactDeadline).click();
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -517,16 +507,13 @@ context(
});
});
navigateTo(navigation.proposals);
cy.getByTestId(openProposals).within(() => {
cy.getByTestId(proposalType)
cy.get(openProposals).within(() => {
cy.get(proposalType)
.contains('Update asset')
.parentsUntil(proposalListItem)
.last()
.within(() => {
cy.getByTestId(proposalDetails).should(
'contain.text',
assetId.slice(0, 6)
); // 3001-VOTE-029
cy.get(proposalDetails).should('contain.text', assetId.slice(0, 6)); // 3001-VOTE-029
cy.getByTestId(viewProposalBtn).click();
});
});
@@ -546,9 +533,9 @@ context(
it('Able to submit update asset proposal using max deadline', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
enterUpdateAssetProposalDetails();
cy.getByTestId(maxVoteDeadline).click();
cy.getByTestId(maxEnactDeadline).click();
cy.getByTestId(proposalDownloadBtn)
cy.get(maxVoteDeadline).click();
cy.get(maxEnactDeadline).click();
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -563,36 +550,36 @@ context(
it('Unable to submit edit asset proposal with missing/invalid fields', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
cy.getByTestId(proposalDownloadBtn).should('be.visible').click();
cy.getByTestId(inputError).should('have.length', 3);
cy.get(proposalDownloadBtn).should('be.visible').click();
cy.get(inputError).should('have.length', 3);
});
it('Able to download and submit freeform proposal', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
// 3008-PFRO-006
cy.getByTestId(newProposalTitle)
cy.get(newProposalTitle)
.siblings()
.should('contain.text', '(100 characters or less)'); // 3008-PFRO-007
// 3008-PFRO-005
cy.getByTestId(newProposalTitle).type('Test freeform proposal form');
cy.get(newProposalTitle).type('Test freeform proposal form');
// 3008-PFRO-009
cy.getByTestId(newProposalDescription)
cy.get(newProposalDescription)
.siblings()
.should('contain.text', '(20,000 characters or less)'); // 3008-PFRO-010
// 3008-PFRO-008 3002-PROP-012 3002-PROP-016
cy.getByTestId(newProposalDescription).type(
cy.get(newProposalDescription).type(
'E2E test for downloading freeform proposal'
);
// 3008-PFRO-012
cy.getByTestId(minVoteDeadline).should('exist'); // 3002-PROP-008
cy.getByTestId(maxVoteDeadline).should('exist');
cy.get(minVoteDeadline).should('exist'); // 3002-PROP-008
cy.get(maxVoteDeadline).should('exist');
// 3008-PFRO-011
cy.getByTestId(proposalVoteDeadline).clear().type('2');
cy.get(proposalVoteDeadline).clear().type('2');
// 3008-PFRO-013 3008-PFRO-014
cy.getByTestId('voting-date').invoke('text').should('not.be.empty');
// 3008-PFRO-015
cy.log('Download updated proposal file');
cy.getByTestId(proposalDownloadBtn)
cy.get(proposalDownloadBtn)
.should('be.visible')
.click()
.then(() => {
@@ -626,11 +613,11 @@ context(
}
function enterUpdateAssetProposalDetails() {
cy.getByTestId(newProposalTitle).type('Test update asset proposal');
cy.getByTestId(newProposalDescription).type('E2E test for proposals');
cy.get(newProposalTitle).type('Test update asset proposal');
cy.get(newProposalDescription).type('E2E test for proposals');
cy.fixture('/proposals/update-asset').then((newAssetProposal) => {
const newAssetPayload = JSON.stringify(newAssetProposal);
cy.getByTestId(newProposalTerms).type(newAssetPayload, {
cy.get(newProposalTerms).type(newAssetPayload, {
parseSpecialCharSequences: false,
delay: 2,
});
@@ -24,12 +24,12 @@ import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../support/wallet-functions';
const proposalListItem = 'proposals-list-item';
const openProposals = 'open-proposals';
const openProposals = '[data-testid="open-proposals"]';
const voteStatus = 'vote-status';
const proposalType = 'proposal-type';
const proposalStatus = 'proposal-status';
const proposalClosingDate = 'vote-details';
const viewProposalButton = 'view-proposal-btn';
const proposalClosingDate = '[data-testid="vote-details"]';
const viewProposalButton = '[data-testid="view-proposal-btn"]';
const voteBreakDownToggle = 'vote-breakdown-toggle';
describe('Governance flow for proposal list', { tags: '@slow' }, function () {
@@ -62,13 +62,13 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
}
navigateTo(navigation.proposals);
cy.getByTestId(openProposals).within(() => {
cy.getByTestId(proposalClosingDate)
cy.get(openProposals).within(() => {
cy.get(proposalClosingDate)
.first()
.invoke('text')
.should('match', /days|minutes/);
cy.getByTestId(proposalClosingDate).should('contain.text', 'months');
cy.getByTestId(proposalClosingDate).last().should('contain.text', 'year');
cy.get(proposalClosingDate).should('contain.text', 'months');
cy.get(proposalClosingDate).last().should('contain.text', 'year');
});
});
@@ -77,7 +77,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
const proposalTitle = generateFreeFormProposalTitle();
submitUniqueRawProposal({ proposalTitle: proposalTitle });
cy.get('[data-testid="proposal-filter-toggle"]').click();
cy.get('[data-testid="set-proposals-filter-visible"]').click();
cy.get('[data-testid="filter-input"]').type(proposerId);
// cy.get(`#${proposalId}`).should('contain', proposalId);
cy.contains(proposalTitle).should('be.visible');
@@ -106,7 +106,7 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
createRawProposal(this.minProposerBalance);
cy.get<testFreeformProposal>('@rawProposal').then((rawProposal) => {
getProposalFromTitle(rawProposal.rationale.title).within(() => {
cy.getByTestId(viewProposalButton).should('be.visible');
cy.get(viewProposalButton).should('be.visible');
cy.getByTestId(proposalType).should('have.text', 'Freeform');
cy.getByTestId(proposalStatus).should('have.text', 'Open');
});
@@ -124,13 +124,13 @@ describe('Governance flow for proposal list', { tags: '@slow' }, function () {
'have.text',
'Participation not reached'
);
cy.getByTestId(viewProposalButton).click();
cy.get(viewProposalButton).click();
});
voteForProposal('for');
navigateTo(navigation.proposals);
getProposalFromTitle(proposalTitle).within(() => {
cy.getByTestId(voteStatus).should('have.text', 'Set to pass');
cy.getByTestId(viewProposalButton).click();
cy.get(viewProposalButton).click();
});
cy.getByTestId(voteBreakDownToggle).click();
getProposalInformationFromTable('Token participation met')
@@ -17,7 +17,8 @@ import {
} from '../../support/wallet-functions';
const vegaAssetAddress = '0x67175Da1D5e966e40D11c4B2519392B2058373de';
const vegaWalletUnstakedBalance = 'vega-wallet-balance-unstaked';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const rewardsTable = 'epoch-total-rewards-table';
const txTimeout = Cypress.env('txTimeout');
const rewardsTimeOut = { timeout: 60000 };
@@ -39,7 +40,7 @@ context('rewards - flow', { tags: '@slow' }, function () {
cy.associateTokensToVegaWallet('6000');
navigateTo(navigation.validators);
cy.VegaWalletTopUpRewardsPool();
cy.getByTestId(vegaWalletUnstakedBalance, txTimeout).should(
cy.get(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
'6,000.0',
txTimeout
@@ -4,6 +4,7 @@ import {
verifyStakedBalance,
verifyEthWalletTotalAssociatedBalance,
verifyEthWalletAssociatedBalance,
waitForSpinner,
navigateTo,
navigation,
turnTelemetryOff,
@@ -57,7 +58,6 @@ context(
before('visit staking tab and connect vega wallet', function () {
cy.visit('/');
ethereumWalletConnect();
cy.connectVegaWallet();
vegaWalletSetSpecifiedApprovalAmount('1000');
});
@@ -67,9 +67,10 @@ context(
function () {
cy.clearLocalStorage();
turnTelemetryOff();
// Go to homepage to allow wallet teardown without epoch timer refreshing page
navigateTo(navigation.home);
vegaWalletTeardown();
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
ethereumWalletConnect();
navigateTo(navigation.validators);
}
);
@@ -127,7 +128,6 @@ context(
cy.getByTestId('staked-by-user-tooltip')
.first()
.should('have.text', 'Staked by me: 2.00');
waitForBeginningOfEpoch();
cy.getByTestId('total-pending-stake').first().realHover();
cy.getByTestId('pending-user-stake-tooltip')
.first()
@@ -398,7 +398,6 @@ context(
vegaWalletSetSpecifiedApprovalAmount('1000');
cy.reload();
ethereumWalletConnect();
cy.connectVegaWallet();
stakingPageAssociateTokens('3');
verifyUnstakedBalance(3.0);
cy.get('button').contains('Select a validator to nominate').click();
@@ -504,6 +503,11 @@ context(
);
});
afterEach('Teardown Wallet', function () {
navigateTo(navigation.home);
vegaWalletTeardown();
});
function verifyNextEpochValue(amount: number) {
cy.getByTestId('stake-next-epoch', epochTimeout)
.contains(amount, epochTimeout)
@@ -21,24 +21,25 @@ import {
vegaWalletTeardown,
} from '../../support/wallet-functions';
const ethWalletContainer = 'ethereum-wallet';
const vegaWalletAssociatedBalance = 'currency-value';
const vegaWalletUnstakedBalance = 'vega-wallet-balance-unstaked';
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
const associateWalletRadioButton = 'associate-radio-wallet';
const tokenAmountInputBox = 'token-amount-input';
const tokenSubmitButton = 'token-input-submit-button';
const ethWalletDissociateButton = '[href="/token/disassociate"]:visible';
const vestingContractSection = 'vega-in-vesting-contract';
const vegaInWalletSection = 'vega-in-wallet';
const connectedVegaKey = 'connected-vega-key';
const associatedKey = 'associated-key';
const associatedAmount = 'associated-amount';
const associateCompleteText = 'transaction-complete-body';
const disassociationWarning = 'disassociation-warning';
const vegaWallet = 'aside [data-testid="vega-wallet"]';
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const ethWalletContainer = '[data-testid="ethereum-wallet"]';
const vegaWalletAssociatedBalance = '[data-testid="currency-value"]';
const vegaWalletUnstakedBalance =
'[data-testid="vega-wallet-balance-unstaked"]';
const txTimeout = Cypress.env('txTimeout');
const vegaWalletPublicKeyShort = Cypress.env('vegaWalletPublicKeyShort');
const ethWalletAssociateButton = '[data-testid="associate-btn"]:visible';
const associateWalletRadioButton = '[data-testid="associate-radio-wallet"]';
const tokenAmountInputBox = '[data-testid="token-amount-input"]';
const tokenSubmitButton = '[data-testid="token-input-submit-button"]';
const ethWalletDissociateButton = '[href="/token/disassociate"]:visible';
const vestingContractSection = '[data-testid="vega-in-vesting-contract"]';
const vegaInWalletSection = '[data-testid="vega-in-wallet"]';
const connectedVegaKey = '[data-testid="connected-vega-key"]';
const associatedKey = '[data-testid="associated-key"]';
const associatedAmount = '[data-testid="associated-amount"]';
const associateCompleteText = '[data-testid="transaction-complete-body"]';
const disassociationWarning = '[data-testid="disassociation-warning"]';
const vegaWallet = 'aside [data-testid="vega-wallet"]';
context(
'Token association flow - with eth and vega wallets connected',
@@ -88,15 +89,12 @@ context(
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
});
cy.getByTestId(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
2.0
);
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
}
);
@@ -129,7 +127,7 @@ context(
verifyEthWalletAssociatedBalance('1,001.00');
verifyEthWalletTotalAssociatedBalance('7,001.00');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
cy.get(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
'1,001.00'
);
@@ -139,20 +137,14 @@ context(
it('Able to disassociate a partial amount of tokens currently associated', function () {
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageDisassociateTokens('1');
verifyEthWalletAssociatedBalance('1.0');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
1.0
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 1.0);
});
});
@@ -162,24 +154,21 @@ context(
'Warning: Any tokens that have been nominated to a node will sacrifice rewards they are due for the current epoch. If you do not wish to sacrifice these, you should remove stake from a node at the end of an epoch before disassociation.';
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
cy.get(ethWalletDissociateButton).click();
cy.getByTestId(disassociationWarning).should('contain', warningText);
cy.get(disassociationWarning).should('contain', warningText);
stakingPageDisassociateAllTokens();
cy.getByTestId(ethWalletContainer)
cy.get(ethWalletContainer)
.first()
.within(() => {
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
'not.exist'
);
});
cy.getByTestId(ethWalletContainer)
cy.get(ethWalletContainer)
.first()
.within(() => {
cy.contains(vegaWalletPublicKeyShort, { timeout: 20000 }).should(
@@ -187,10 +176,7 @@ context(
);
});
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
0.0
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 0.0);
});
});
@@ -212,15 +198,9 @@ context(
verifyEthWalletAssociatedBalance('2.0');
verifyEthWalletTotalAssociatedBalance('2.0');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.getByTestId(vegaWalletUnstakedBalance, txTimeout).should(
'contain',
2.0
);
cy.get(vegaWalletUnstakedBalance, txTimeout).should('contain', 2.0);
stakingPageDisassociateTokens('1', {
type: 'contract',
skipConfirmation: true,
@@ -241,54 +221,45 @@ context(
cy.get('button').contains('Select a validator to nominate').click();
cy.getByTestId('epoch-countdown').should('be.visible');
stakingPageAssociateTokens('37', { type: 'contract' });
cy.getByTestId(vestingContractSection)
cy.get(vestingContractSection)
.first()
.within(() => {
cy.getByTestId(associatedKey).should(
cy.get(associatedKey).should(
'contain',
Cypress.env('vegaWalletPublicKeyShort')
);
cy.getByTestId(associatedAmount, txTimeout).should('contain', 37);
cy.get(associatedAmount, txTimeout).should('contain', 37);
});
cy.getByTestId(vegaInWalletSection)
cy.get(vegaInWalletSection)
.first()
.within(() => {
cy.getByTestId(associatedKey).should(
cy.get(associatedKey).should(
'contain',
Cypress.env('vegaWalletPublicKeyShort')
);
cy.getByTestId(associatedAmount, txTimeout).should('contain', 21);
cy.get(associatedAmount, txTimeout).should('contain', 21);
});
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
58
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 58);
});
stakingPageDisassociateTokens('6', { type: 'contract' });
cy.getByTestId(vestingContractSection)
cy.get(vestingContractSection)
.first()
.within(() => {
cy.getByTestId(associatedAmount, txTimeout).should('contain', 31);
cy.get(associatedAmount, txTimeout).should('contain', 31);
});
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
52
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 52);
});
navigateTo(navigation.validators);
stakingPageDisassociateTokens('9', { type: 'wallet' });
cy.getByTestId(vegaInWalletSection)
cy.get(vegaInWalletSection)
.first()
.within(() => {
cy.getByTestId(associatedAmount, txTimeout).should('contain', 12);
cy.get(associatedAmount, txTimeout).should('contain', 12);
});
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
43
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 43);
});
});
@@ -297,10 +268,10 @@ context(
// 1004-ASSO-010
// No warning visible as described in AC, but the button is disabled
cy.get(ethWalletAssociateButton).click();
cy.getByTestId(associateWalletRadioButton, { timeout: 30000 }).click();
cy.getByTestId(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
cy.getByTestId(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
cy.getByTestId(tokenSubmitButton, txTimeout).should('be.disabled');
cy.get(associateWalletRadioButton, { timeout: 30000 }).click();
cy.get(tokenSubmitButton, txTimeout).should('be.disabled'); // button disabled with no input
cy.get(tokenAmountInputBox, { timeout: 10000 }).type('6500000');
cy.get(tokenSubmitButton, txTimeout).should('be.disabled');
});
// 1004-ASSO-004
@@ -325,25 +296,22 @@ context(
it('Able to associate tokens to different public key of connected vega wallet', function () {
cy.get(ethWalletAssociateButton).click();
cy.getByTestId(associateWalletRadioButton).click();
cy.getByTestId(connectedVegaKey).should(
cy.get(associateWalletRadioButton).click();
cy.get(connectedVegaKey).should(
'have.text',
Cypress.env('vegaWalletPublicKey')
);
switchVegaWalletPubKey();
cy.getByTestId(connectedVegaKey).should(
cy.get(connectedVegaKey).should(
'have.text',
Cypress.env('vegaWalletPublicKey2')
);
stakingPageAssociateTokens('2');
cy.get(vegaWallet).within(() => {
cy.getByTestId(vegaWalletAssociatedBalance, txTimeout).should(
'contain',
2.0
);
cy.get(vegaWalletAssociatedBalance, txTimeout).should('contain', 2.0);
});
cy.getByTestId(associateCompleteText).should(
cy.get(associateCompleteText).should(
'have.text',
`Vega key ${Cypress.env(
'vegaWalletPublicKey2Short'
@@ -117,11 +117,6 @@ context(
cy.getByTestId(submitWithdrawalButton).click();
});
// assert withdrawal request
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
@@ -135,11 +130,6 @@ context(
cy.getByTestId(toastClose).click();
});
// withdrawal complete
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'The withdrawal has been approved.')
@@ -149,11 +139,6 @@ context(
'Withdraw 120.00 tUSDC'
);
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Transaction confirmed')
@@ -161,11 +146,6 @@ context(
cy.getByTestId('external-link').should('exist');
});
// withdrawal history for complete withdrawal displayed
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(tableWithdrawnStatus)
.eq(1, txTimeout)
.should('have.text', 'Completed')
@@ -207,11 +187,6 @@ context(
cy.getByTestId(submitWithdrawalButton).click();
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
@@ -223,11 +198,6 @@ context(
);
cy.getByTestId(toastClose).click();
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get(tableTxHash)
.eq(1)
.should('have.text', 'Complete withdrawal')
@@ -243,33 +213,18 @@ context(
});
ethereumWalletConnect();
cy.getByTestId(completeWithdrawalButton).first().click();
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Awaiting confirmation')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'The withdrawal has been approved.')
.within(() => {
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.last(txTimeout)
.should('contain.text', 'Transaction confirmed')
@@ -293,11 +248,6 @@ context(
cy.getByTestId(amountInput).click().type('50');
cy.getByTestId(submitWithdrawalButton).click();
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId(toast)
.first(txTimeout)
.should('contain.text', 'Funds unlocked')
@@ -16,11 +16,6 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
it('should display announcement banner', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('app-announcement')
.should('contain.text', 'TEST ANNOUNCEMENT!')
.within(() => {
@@ -40,11 +35,6 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
waitForSpinner();
}
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('proposals-list-item')
.should('have.length.at.least', 1)
.first()
@@ -104,11 +94,6 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
it('should contain link to specific validators', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('validators')
.should('have.length', '2')
.each(($validator) => {
@@ -135,11 +120,6 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
it('should display network data', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('git-network-data')
.should('contain.text', 'Reading network data from')
.within(() => {
@@ -151,11 +131,6 @@ context('Home Page - verify elements on page', { tags: '@smoke' }, function () {
});
it('should display eth data', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('git-eth-data')
.should('contain.text', 'Reading Ethereum data from')
.within(() => {
@@ -10,10 +10,9 @@ import {
} from '../../support/governance.functions';
import { mockNetworkUpgradeProposal } from '../../support/proposal.functions';
const proposalDocsLink = 'proposal-docs-link';
const proposalDocumentationLink = 'proposal-documentation-link';
const connectToVegaWalletButton = 'connect-to-vega-wallet-btn';
const proposalDocumentationLink = '[data-testid="proposal-documentation-link"]';
const governanceDocsUrl = 'https://vega.xyz/governance';
const connectToVegaWalletButton = '[data-testid="connect-to-vega-wallet-btn"]';
context(
'Governance Page - verify elements on page',
@@ -42,7 +41,7 @@ context(
it('should be able to see a working link for - find out more about Vega governance', function () {
// 3001-VOTE-001
cy.getByTestId(proposalDocumentationLink)
cy.get(proposalDocumentationLink)
.should('be.visible')
.and('have.text', 'Find out more about Vega governance')
.and('have.attr', 'href')
@@ -65,7 +64,7 @@ context(
// 3007-PNE-021
it('should have documentation links for network parameter proposal', function () {
goToMakeNewProposal(governanceProposalType.NETWORK_PARAMETER);
cy.getByTestId(proposalDocsLink)
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/network-parameter-proposal');
@@ -74,7 +73,7 @@ context(
// 3003-PMAN-002 3003-PMAN-005
it('should have documentation links for new market proposal', function () {
goToMakeNewProposal(governanceProposalType.NEW_MARKET);
cy.getByTestId(proposalDocsLink)
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/new-market-proposal');
@@ -83,7 +82,7 @@ context(
// 3004-PMAC-005
it('should have documentation links for update market proposal', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_MARKET);
cy.getByTestId(proposalDocsLink)
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/update-market-proposal');
@@ -92,7 +91,7 @@ context(
// 3005-PASN-002 005-PASN-005
it('should have documentation links for new asset proposal', function () {
goToMakeNewProposal(governanceProposalType.NEW_ASSET);
cy.getByTestId(proposalDocsLink)
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/new-asset-proposal');
@@ -101,7 +100,7 @@ context(
// 3006-PASC-002 3006-PASC-005
it('should have documentation links for update asset proposal', function () {
goToMakeNewProposal(governanceProposalType.UPDATE_ASSET);
cy.getByTestId(proposalDocsLink)
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/update-asset-proposal');
@@ -110,7 +109,7 @@ context(
// 3008-PFRO-003 3008-PFRO-017
it('should have documentation links for freeform proposal', function () {
goToMakeNewProposal(governanceProposalType.FREEFORM);
cy.getByTestId(proposalDocsLink)
cy.getByTestId('proposal-docs-link')
.find('a')
.should('have.attr', 'href')
.and('contain', '/tutorials/proposals/freeform-proposal');
@@ -118,7 +117,7 @@ context(
it('should be able to see a connect wallet button - if vega wallet disconnected and user is submitting new proposal', function () {
goToMakeNewProposal(governanceProposalType.RAW);
cy.getByTestId(connectToVegaWalletButton)
cy.get(connectToVegaWalletButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});
@@ -139,11 +138,6 @@ context(
mockNetworkUpgradeProposal();
navigateTo(navigation.proposals);
cy.getByTestId('open-proposals').within(() => {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('li')
.eq(0)
.should(
@@ -171,7 +165,6 @@ context(
);
});
});
cy.get('[data-testid="closed-proposals-toggle-networkUpgrades"]').click();
cy.getByTestId('closed-proposals').within(() => {
cy.getByTestId('protocol-upgrade-proposals-list-item').should(
'have.length',
@@ -206,11 +199,6 @@ context(
.should('contain.text', '99.98% approval (% validator voting power)')
.and('contain.text', '(67% voting power required)');
cy.get('h2').should('contain.text', 'Approvers (4/4 validators)');
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('validator-name')
.should('have.length', 4)
.each(($validator) => {
@@ -5,8 +5,8 @@ import {
} from '../../support/common.functions';
import { waitForBeginningOfEpoch } from '../../support/staking.functions';
const viewToggle = 'epoch-reward-view-toggle-total';
const warning = 'callout';
const viewToggle = '[data-testid="epoch-reward-view-toggle-total"]';
const warning = '[data-testid="callout"]';
context(
'Rewards Page - verify elements on page',
@@ -27,7 +27,7 @@ context(
});
it('should have epoch warning', function () {
cy.getByTestId(warning)
cy.get(warning)
.should('be.visible')
.and(
'have.text',
@@ -36,17 +36,12 @@ context(
});
it('should have toggle for seeing total vs individual rewards', function () {
cy.getByTestId(viewToggle).should('be.visible');
cy.get(viewToggle).should('be.visible');
});
// Skipping due to bug #3471 causing flaky failuress
it.skip('should have option to view go to next and previous page', function () {
waitForBeginningOfEpoch();
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('page-info')
.should('contain.text', 'Page ')
.invoke('text')
@@ -1,17 +1,18 @@
import { navigateTo, navigation } from '../../support/common.functions';
const tokenDetailsTable = '.token-details';
const address = 'token-address';
const contract = 'token-contract';
const totalSupply = 'total-supply';
const circulatingSupply = 'circulating-supply';
const staked = 'staked';
const tranchesLink = 'tranches-link';
const redeemBtn = 'check-vesting-page-btn';
const getVegaWalletLink = 'get-vega-wallet-link';
const associateVegaLink = 'associate-vega-tokens-link-on-homepage';
const stakingBtn = 'staking-button-on-homepage';
const governanceBtn = 'governance-button-on-homepage';
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');
@@ -24,7 +25,7 @@ context('Verify elements on Token page', { tags: '@smoke' }, function () {
describe('THE $VEGA TOKEN table', function () {
it('should have TOKEN ADDRESS', function () {
cy.get(tokenDetailsTable).within(() => {
cy.getByTestId(address)
cy.get(address)
.should('be.visible')
.invoke('text')
.should('be.equal', vegaTokenAddress);
@@ -33,7 +34,7 @@ context('Verify elements on Token page', { tags: '@smoke' }, function () {
it('should have VESTING CONTRACT', function () {
// 1004-ASSO-001
cy.get(tokenDetailsTable).within(() => {
cy.getByTestId(contract)
cy.get(contract)
.should('be.visible')
.invoke('text')
.should('be.equal', vegaTokenContractAddress);
@@ -41,56 +42,56 @@ context('Verify elements on Token page', { tags: '@smoke' }, function () {
});
it('should have TOTAL SUPPLY', function () {
cy.get(tokenDetailsTable).within(() => {
cy.getByTestId(totalSupply).should('be.visible');
cy.get(totalSupply).should('be.visible');
});
});
it('should have CIRCULATING SUPPLY', function () {
cy.get(tokenDetailsTable).within(() => {
cy.getByTestId(circulatingSupply).should('be.visible');
cy.get(circulatingSupply).should('be.visible');
});
});
it('should have STAKED $VEGA', function () {
cy.get(tokenDetailsTable).within(() => {
cy.getByTestId(staked).should('be.visible');
cy.get(staked).should('be.visible');
});
});
});
describe('links and buttons', function () {
it('should have TRANCHES link', function () {
cy.getByTestId(tranchesLink)
cy.get(tranchesLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/token/tranches');
});
it('should have REDEEM button', function () {
cy.getByTestId(redeemBtn)
cy.get(redeemBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/token/redeem');
});
it('should have GET VEGA WALLET link', function () {
cy.getByTestId(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.getByTestId(associateVegaLink)
cy.get(associateVegaLink)
.should('be.visible')
.and('have.attr', 'href')
.and('equal', '/token/associate');
});
it('should have STAKING button', function () {
cy.getByTestId(stakingBtn)
cy.get(stakingBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
.and('equal', '/validators');
});
it('should have GOVERNANCE button', function () {
cy.getByTestId(governanceBtn)
cy.get(governanceBtn)
.should('be.visible')
.parent()
.should('have.attr', 'href')
@@ -21,11 +21,6 @@ context(
// 1005-VEST-001
// 1005-VEST-002
it('Able to view tranches', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('tranche-item')
.should('have.length', 2)
.first()
@@ -56,11 +51,6 @@ context(
cy.get('span').eq(1).should('have.text', 0);
});
cy.getByTestId('key-value-table').within(() => {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('link')
.should('have.length', 8)
.each((ethLink) => {
@@ -68,11 +58,6 @@ context(
.should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/address/');
});
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('redeem-link')
.should('have.length', 8)
.each((redeemLink) => {
@@ -86,11 +71,6 @@ context(
it('Able to view tranches with less than 10 vega', function () {
navigateTo(navigation.supply);
cy.getByTestId('show-all-tranches').click();
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('tranche-item')
.should('have.length', 8)
.first()
@@ -12,26 +12,28 @@ import {
} from '../../support/staking.functions';
import { previousEpochData } from '../../fixtures/mocks/previous-epoch';
const guideLink = 'staking-guide-link';
const validatorTitle = 'validator-node-title';
const validatorId = 'validator-id';
const validatorPubKey = 'validator-public-key';
const ethAddressLink = 'link';
const validatorStatus = 'validator-status';
const totalStake = 'total-stake';
const pendingStake = 'pending-stake';
const stakedByOperator = 'staked-by-operator';
const stakedByDelegates = 'staked-by-delegates';
const stakeShare = 'stake-percentage';
const stakedByOperatorToolTip = 'staked-operator-tooltip';
const stakedByDelegatesToolTip = 'staked-delegates-tooltip';
const totalStakedToolTip = 'total-staked-tooltip';
const unnormalisedVotingPowerToolTip = 'unnormalised-voting-power-tooltip';
const normalisedVotingPowerToolTip = 'normalised-voting-power-tooltip';
const performancePenaltyToolTip = 'performance-penalty-tooltip';
const overstakedPenaltyToolTip = 'overstaked-penalty-tooltip';
const multisigPenaltyToolTip = 'multisig-error-tooltip';
const epochCountDown = 'epoch-countdown';
const guideLink = '[data-testid="staking-guide-link"]';
const validatorTitle = '[data-testid="validator-node-title"]';
const validatorId = '[data-testid="validator-id"]';
const validatorPubKey = '[data-testid="validator-public-key"]';
const ethAddressLink = '[data-testid="link"]';
const validatorStatus = '[data-testid="validator-status"]';
const totalStake = '[data-testid="total-stake"]';
const pendingStake = '[data-testid="pending-stake"]';
const stakedByOperator = '[data-testid="staked-by-operator"]';
const stakedByDelegates = '[data-testid="staked-by-delegates"]';
const stakeShare = '[data-testid="stake-percentage"]';
const stakedByOperatorToolTip = '[data-testid="staked-operator-tooltip"]';
const stakedByDelegatesToolTip = '[data-testid="staked-delegates-tooltip"]';
const totalStakedToolTip = '[data-testid="total-staked-tooltip"]';
const unnormalisedVotingPowerToolTip =
'[data-testid="unnormalised-voting-power-tooltip"]';
const normalisedVotingPowerToolTip =
'[data-testid="normalised-voting-power-tooltip"]';
const performancePenaltyToolTip = '[data-testid="performance-penalty-tooltip"]';
const overstakedPenaltyToolTip = '[data-testid="overstaked-penalty-tooltip"]';
const multisigPenaltyToolTip = '[data-testid="multisig-error-tooltip"]';
const epochCountDown = '[data-testid="epoch-countdown"]';
const stakeNumberRegex = /^\d{1,3}(,\d{3})*(\.\d+)?$/;
context('Validators Page - verify elements on page', function () {
@@ -50,7 +52,7 @@ context('Validators Page - verify elements on page', function () {
it('Should have Staking Guide link visible', function () {
// 1002-STKE-003
cy.getByTestId(guideLink)
cy.get(guideLink)
.should('be.visible')
.and('have.text', 'Read more about staking on Vega')
.and(
@@ -74,11 +76,6 @@ context('Validators Page - verify elements on page', function () {
function () {
// 1002-STKE-050
it('Should be able to see validator names', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.get('[col-id="validator"] > div > span')
.should('have.length.at.least', 1)
.each(($name) => {
@@ -87,11 +84,6 @@ context('Validators Page - verify elements on page', function () {
});
it('Should be able to see validator stake', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('total-stake')
.should('have.length.at.least', 1)
.each(($stake) => {
@@ -103,23 +95,18 @@ context('Validators Page - verify elements on page', function () {
waitForBeginningOfEpoch();
cy.getByTestId('total-stake').first().realHover();
cy.getByTestId(stakedByOperatorToolTip)
cy.get(stakedByOperatorToolTip)
.invoke('text')
.should('contain', 'Staked by operator: 3,000.00');
cy.getByTestId(stakedByDelegatesToolTip)
cy.get(stakedByDelegatesToolTip)
.invoke('text')
.should('contain', 'Staked by delegates: 0.00');
cy.getByTestId(totalStakedToolTip)
cy.get(totalStakedToolTip)
.invoke('text')
.should('contain', 'Total stake: 3,000.00');
});
it('Should be able to see validator normalised voting power', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('normalised-voting-power')
.should('have.length.at.least', 1)
.each(($vPower) => {
@@ -131,21 +118,16 @@ context('Validators Page - verify elements on page', function () {
waitForBeginningOfEpoch();
cy.getByTestId('normalised-voting-power').first().realHover();
cy.getByTestId(unnormalisedVotingPowerToolTip)
cy.get(unnormalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Unnormalised voting power: 20.00%');
cy.getByTestId(normalisedVotingPowerToolTip)
cy.get(normalisedVotingPowerToolTip)
.invoke('text')
.should('contain', 'Normalised voting power: 50.00%');
});
// 2002-SINC-018
it('Should be able to see validator total penalties', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('total-penalty')
.should('have.length.at.least', 1)
.each(($penalties) => {
@@ -157,20 +139,15 @@ context('Validators Page - verify elements on page', function () {
waitForBeginningOfEpoch();
cy.getByTestId('total-penalty').realHover();
cy.getByTestId(performancePenaltyToolTip)
cy.get(performancePenaltyToolTip)
.invoke('text')
.should('contain', 'Performance penalty: 0.00%');
cy.getByTestId(overstakedPenaltyToolTip)
cy.get(overstakedPenaltyToolTip)
.invoke('text')
.should('contain', 'Overstaked penalty: 60.00%'); // value not asserted due to #2886
});
it('Should be able to see validator pending stake', function () {
/**
* TODO(@nx/cypress): Nesting Cypress commands in a should assertion now throws.
* You should use .then() to chain commands instead.
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
**/
cy.getByTestId('total-pending-stake')
.should('have.length.at.least', 1)
.each(($pendingStake) => {
@@ -184,12 +161,12 @@ context('Validators Page - verify elements on page', function () {
});
waitForBeginningOfEpoch();
cy.getByTestId('total-penalty').first().realHover();
cy.getByTestId(multisigPenaltyToolTip)
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
cy.getByTestId('total-penalty').eq(1).realHover();
cy.getByTestId(multisigPenaltyToolTip)
cy.get(multisigPenaltyToolTip)
.invoke('text')
.should('contain', 'Multisig penalty: 100%');
});
@@ -208,59 +185,53 @@ context('Validators Page - verify elements on page', function () {
// 1002-STKE-006
it('Should be able to see validator name', function () {
cy.getByTestId(validatorTitle).should('not.be.empty');
cy.get(validatorTitle).should('not.be.empty');
});
// 1002-STKE-007
it('Should be able to see validator id', function () {
cy.getByTestId(validatorId).should('not.be.empty');
cy.get(validatorId).should('not.be.empty');
});
// 1002-STKE-008
it('Should be able to see validator public key', function () {
cy.getByTestId(validatorPubKey).should('not.be.empty');
cy.get(validatorPubKey).should('not.be.empty');
});
// 1002-STKE-010
it('Should be able to see Ethereum address', function () {
cy.getByTestId(ethAddressLink)
.should('not.be.empty')
.and('have.attr', 'href');
cy.get(ethAddressLink).should('not.be.empty').and('have.attr', 'href');
});
// TODO validators missing url for more information about them 1002-STKE-09
it('Should be able to see validator status', function () {
cy.getByTestId(validatorStatus).should('have.text', 'Consensus');
cy.get(validatorStatus).should('have.text', 'Consensus');
});
// 1002-STKE-012
it('Should be able to see total stake', function () {
cy.getByTestId(totalStake)
.invoke('text')
.should('match', stakeNumberRegex);
cy.get(totalStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see pending stake', function () {
cy.getByTestId(pendingStake)
.invoke('text')
.should('match', stakeNumberRegex);
cy.get(pendingStake).invoke('text').should('match', stakeNumberRegex);
});
it('Should be able to see staked by operator', function () {
cy.getByTestId(stakedByOperator)
cy.get(stakedByOperator)
.invoke('text')
.should('match', stakeNumberRegex);
});
it('Should be able to see staked by delegates', function () {
cy.getByTestId(stakedByDelegates)
cy.get(stakedByDelegates)
.invoke('text')
.should('match', stakeNumberRegex);
});
// 1002-STKE-051
it('Should be able to see stake share in percentage', function () {
cy.getByTestId(stakeShare)
cy.get(stakeShare)
.invoke('text')
.then(($stakePercentage) => {
// The pattern must start at a word boundary (\b).
@@ -286,7 +257,7 @@ context('Validators Page - verify elements on page', function () {
const epochTitle = 'h3';
const nextEpochInfo = 'p';
cy.getByTestId(epochCountDown).within(() => {
cy.get(epochCountDown).within(() => {
cy.get(epochTitle).should('not.be.empty');
cy.get(nextEpochInfo).should('contain.text', 'Next epoch');
});
@@ -6,7 +6,7 @@ import {
} from '../../support/common.functions';
import { ethereumWalletConnect } from '../../support/wallet-eth.functions';
const connectButton = 'connect-to-eth-btn';
const connectButton = '[data-testid="connect-to-eth-btn"]';
const lockedTokensInVestingContract = '6,499,972.30';
context(
@@ -29,7 +29,7 @@ context(
// 1005-VEST-018
it('should have connect Eth wallet button', function () {
cy.getByTestId(connectButton)
cy.get(connectButton)
.should('be.visible')
.and('have.text', 'Connect Ethereum wallet');
});
@@ -5,11 +5,11 @@ const walletContainer = 'aside [data-testid="ethereum-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
const connectToEthButton =
'[data-testid="connect-to-eth-wallet-button"]:visible';
const connectorList = 'web3-connector-list';
const connectorList = '[data-testid="web3-connector-list"]';
const associate = '[href="/token/associate"]';
const disassociate = '[href="/token/disassociate"]';
const disconnect = 'disconnect-from-eth-wallet-button';
const accountNo = 'ethereum-account-truncated';
const disconnect = '[data-testid="disconnect-from-eth-wallet-button"]';
const accountNo = '[data-testid="ethereum-account-truncated"]';
const currencyTitle = '[data-testid="currency-title"]:visible';
const currencyValue = '[data-testid="currency-value"]:visible';
const vegaInVesting = '[data-testid="vega-in-vesting-contract"]:visible';
@@ -18,8 +18,8 @@ const progressBar = '[data-testid="progress-bar"]:visible';
const currencyLocked = '[data-testid="currency-locked"]:visible';
const currencyUnlocked = '[data-testid="currency-unlocked"]:visible';
const dialog = '[role="dialog"]:visible';
const dialogHeader = 'dialog-title';
const dialogCloseBtn = 'dialog-close';
const dialogHeader = '[data-testid="dialog-title"]';
const dialogCloseBtn = '[data-testid="dialog-close"]';
context(
'Ethereum Wallet - verify elements on widget',
@@ -59,7 +59,7 @@ context(
it('should have Connect Ethereum header visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogHeader)
cy.get(dialogHeader)
.should('be.visible')
.and('have.text', 'Connect to your Ethereum wallet');
});
@@ -73,7 +73,7 @@ context(
'WalletConnect',
'WalletConnect Legacy',
];
cy.getByTestId(connectorList).within(() => {
cy.get(connectorList).within(() => {
cy.get('button').each(($btn, i) => {
cy.wrap($btn).should('be.visible').and('have.text', connectList[i]);
});
@@ -83,7 +83,7 @@ context(
after('close popup', function () {
cy.get(dialog)
.within(() => {
cy.getByTestId(dialogCloseBtn).click();
cy.get(dialogCloseBtn).click();
})
.should('not.exist');
});
@@ -106,7 +106,7 @@ context(
// 0004-EWAL-005
it('should have account number visible', function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(accountNo)
cy.get(accountNo)
.should('be.visible')
.and('have.text', Cypress.env('ethWalletPublicKeyTruncated'));
});
@@ -129,7 +129,7 @@ context(
// 0004-EWAL-007
it('should have Disconnect button visible', function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(disconnect)
cy.get(disconnect)
.should('be.visible')
.and('have.text', 'Disconnect');
});
@@ -7,28 +7,28 @@ import {
const walletContainer = 'aside [data-testid="vega-wallet"]';
const walletHeader = '[data-testid="wallet-header"] h1';
const connectButton = 'connect-vega-wallet';
const getVegaLink = 'link';
const connectButton = '[data-testid="connect-vega-wallet"]';
const getVegaLink = '[data-testid="link"]';
const dialog = '[role="dialog"]:visible';
const dialogHeader = 'dialog-title';
const walletDialogHeader = 'wallet-dialog-title';
const connectorsList = 'connectors-list';
const dialogCloseBtn = 'dialog-close';
const restConnectorForm = 'rest-connector-form';
const dialogHeader = '[data-testid="dialog-title"]';
const walletDialogHeader = '[data-testid="wallet-dialog-title"]';
const connectorsList = '[data-testid="connectors-list"]';
const dialogCloseBtn = '[data-testid="dialog-close"]';
const restConnectorForm = '[data-testid="rest-connector-form"]';
const restWallet = '#wallet';
const restPassphrase = '#passphrase';
const restConnectBtn = '[type="submit"]';
const accountNo = 'vega-account-truncated';
const currencyTitle = 'currency-title';
const currencyValue = 'currency-value';
const accountNo = '[data-testid="vega-account-truncated"]';
const currencyTitle = '[data-testid="currency-title"]';
const currencyValue = '[data-testid="currency-value"]';
const vegaUnstaked = '[data-testid="vega-wallet-balance-unstaked"] .text-right';
const governanceBtn = '[href="/proposals"]';
const stakingBtn = '[href="/validators"]';
const manageLink = 'manage-vega-wallet';
const dialogVegaKey = 'vega-public-key-full';
const dialogDisconnectBtn = 'disconnect';
const copyPublicKeyBtn = 'copy-vega-public-key';
const vegaWalletCurrencyTitle = 'currency-title';
const manageLink = '[data-testid="manage-vega-wallet"]';
const dialogVegaKey = '[data-testid="vega-public-key-full"]';
const dialogDisconnectBtn = '[data-testid="disconnect"]';
const copyPublicKeyBtn = '[data-testid="copy-vega-public-key"]';
const vegaWalletCurrencyTitle = '[data-testid="currency-title"]';
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const txTimeout = Cypress.env('txTimeout');
@@ -47,10 +47,10 @@ context(
cy.get(walletHeader)
.should('be.visible')
.and('have.text', 'Vega Wallet');
cy.getByTestId(connectButton)
cy.get(connectButton)
.should('be.visible')
.and('have.text', 'Connect Vega wallet to use associated $VEGA');
cy.getByTestId(getVegaLink)
cy.get(getVegaLink)
.should('be.visible')
.and('have.text', 'Get a Vega wallet')
.and('have.attr', 'href', 'https://vega.xyz/wallet');
@@ -61,20 +61,20 @@ context(
describe('when connect button clicked', () => {
before('click connect vega wallet button', () => {
cy.get(walletContainer).within(() => {
cy.getByTestId(connectButton).click();
cy.get(connectButton).click();
});
});
it('should have Connect Vega header visible', () => {
cy.get(dialog).within(() => {
cy.getByTestId(walletDialogHeader)
cy.get(walletDialogHeader)
.should('be.visible')
.and('have.text', 'Connect');
});
});
it('should have jsonRpc and hosted connection options visible on list', function () {
cy.getByTestId(connectorsList).within(() => {
cy.get(connectorsList).within(() => {
cy.getByTestId('connector-jsonRpc')
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
@@ -86,33 +86,33 @@ context(
it('should have close button visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogCloseBtn).should('be.visible');
cy.get(dialogCloseBtn).should('be.visible');
});
});
});
describe('when rest connector form opened', function () {
before('click hosted wallet app button', function () {
cy.getByTestId(connectorsList).within(() => {
cy.get(connectorsList).within(() => {
cy.getByTestId('connector-hosted').click();
});
});
// 0002-WCON-002
it('should have wallet field visible', function () {
cy.getByTestId(restConnectorForm).within(() => {
cy.get(restConnectorForm).within(() => {
cy.get(restWallet).should('be.visible');
});
});
it('should have password field visible', function () {
cy.getByTestId(restConnectorForm).within(() => {
cy.get(restConnectorForm).within(() => {
cy.get(restPassphrase).should('be.visible');
});
});
it('should have connect button visible', function () {
cy.getByTestId(restConnectorForm).within(() => {
cy.get(restConnectorForm).within(() => {
cy.get(restConnectBtn)
.should('be.visible')
.and('have.text', 'Connect');
@@ -121,12 +121,12 @@ context(
it('should have close button visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogCloseBtn).should('be.visible');
cy.get(dialogCloseBtn).should('be.visible');
});
});
after('close dialog', function () {
cy.getByTestId(dialogCloseBtn).click().should('not.exist');
cy.get(dialogCloseBtn).click().should('not.exist');
});
});
@@ -152,7 +152,7 @@ context(
{ tags: '@smoke' },
function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(accountNo)
cy.get(accountNo)
.should('be.visible')
.and('have.text', Cypress.env('vegaWalletPublicKeyShort'));
});
@@ -161,7 +161,7 @@ context(
it('should have Vega Associated currency title visible', function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(currencyTitle)
cy.get(currencyTitle)
.should('be.visible')
.and('contain.text', `VEGAAssociated`);
});
@@ -172,7 +172,7 @@ context(
{ tags: '@smoke' },
function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(currencyValue)
cy.get(currencyValue)
.should('be.visible')
.and('contain.text', `0.00`);
});
@@ -204,23 +204,21 @@ context(
it('should have Manage link visible', function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(manageLink)
.should('be.visible')
.and('have.text', 'Manage');
cy.get(manageLink).should('be.visible').and('have.text', 'Manage');
});
});
describe('when Manage dialog opened', function () {
before('click Manage link', function () {
cy.get(walletContainer).within(() => {
cy.getByTestId(manageLink).click();
cy.get(manageLink).click();
});
});
// 0002-WCON-025, 0002-WCON-026
it('should have SELECT A VEGA KEY dialog title visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogHeader)
cy.get(dialogHeader)
.should('be.visible')
.and('have.text', 'SELECT A VEGA KEY');
});
@@ -240,7 +238,7 @@ context(
'contain.text',
truncatedPubKey1
);
cy.getByTestId(dialogVegaKey)
cy.get(dialogVegaKey)
.should('be.visible')
.and('contain.text', truncatedPubKey1)
.and('contain.text', truncatedPubKey2);
@@ -250,7 +248,7 @@ context(
// 0002-WCON-029
it('should have copy public key button visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(copyPublicKeyBtn)
cy.get(copyPublicKeyBtn)
.should('be.visible')
.and('contain.text', 'Copy');
});
@@ -258,13 +256,13 @@ context(
it('should have close button visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogCloseBtn).should('be.visible');
cy.get(dialogCloseBtn).should('be.visible');
});
});
it('should have vega Disconnect all keys button visible', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogDisconnectBtn)
cy.get(dialogDisconnectBtn)
.should('be.visible')
.and('have.text', 'Disconnect all keys');
});
@@ -273,10 +271,10 @@ context(
// 0002-WCON-022
it('should be able to disconnect all keys', function () {
cy.get(dialog).within(() => {
cy.getByTestId(dialogDisconnectBtn).click();
cy.get(dialogDisconnectBtn).click();
});
cy.get(walletContainer).within(() => {
cy.getByTestId(connectButton).should('be.visible'); // 0002-WCON-023
cy.get(connectButton).should('be.visible'); // 0002-WCON-023
});
});
});
@@ -321,22 +319,16 @@ context(
cy.reload();
waitForSpinner();
cy.connectVegaWallet();
cy.get(walletContainer).within(() => {
cy.getByTestId('currency-title', txTimeout).should(
'have.length.at.least',
5
);
});
});
for (const { name, symbol, expectedAmount } of assets) {
it(`should see ${name} within vega wallet`, () => {
cy.get(walletContainer).within(() => {
cy.getByTestId(vegaWalletCurrencyTitle)
cy.get(vegaWalletCurrencyTitle)
.contains(name, txTimeout)
.should('be.visible');
cy.getByTestId(vegaWalletCurrencyTitle)
cy.get(vegaWalletCurrencyTitle)
.contains(name)
.parent()
.siblings()
@@ -345,7 +337,7 @@ context(
expect(displayedAmount).be.gte(expectedAmount);
});
cy.getByTestId(vegaWalletCurrencyTitle)
cy.get(vegaWalletCurrencyTitle)
.contains(name)
.parent()
.contains(symbol);
@@ -5,6 +5,8 @@ import {
verifyTabHighlighted,
} from '../../support/common.functions';
const connectToVegaBtn = '[data-testid="connect-to-vega-wallet-btn"]';
context(
'Withdraw Page - verify elements on page',
{ tags: '@smoke' },
@@ -24,7 +26,7 @@ context(
});
it('should have connect Vega wallet button', function () {
cy.getByTestId('connect-to-vega-wallet-btn')
cy.get(connectToVegaBtn)
.should('be.visible')
.and('have.text', 'Connect Vega wallet');
});
@@ -139,9 +139,6 @@ async function vegaWalletTeardownStaking(stakingBridgeContract: StakingBridge) {
$associatedAmount
);
});
// Wait needed to allow Eth transaction to complete - otherwise could result in nonce error
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(2000);
}
});
});
+1 -1
View File
@@ -1,7 +1,7 @@
{
"presets": [
[
"@nx/react/babel",
"@nrwl/react/babel",
{
"runtime": "automatic"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__"],
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
+1 -1
View File
@@ -31,7 +31,7 @@ Example configurations are provided here:
For convenience, you can boot the app injecting one of the configurations above by running:
```bash
yarn env-cmd -f .\apps\governance\.env.{env} yarn nx run governance:serve # e.g. stagnet1
yarn nx run governance:serve --env={env} # e.g. stagnet1
```
There are a few different configuration options offered for this app:
+6
View File
@@ -0,0 +1,6 @@
function ReactMarkdown({ children }) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>{children}</>;
}
export default ReactMarkdown;
+3 -2
View File
@@ -3,8 +3,8 @@ export default {
displayName: 'governance',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/react/babel'] }],
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
'^.+\\.[tj]sx?$': 'babel-jest',
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/governance',
@@ -16,5 +16,6 @@ export default {
'**/*.{ts,tsx}',
'!**/node_modules/**',
'!**/__generated__/**',
'!**/__generated___/**',
],
};
+7 -14
View File
@@ -1,11 +1,10 @@
{
"name": "governance",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/governance/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"executor": "./tools/executors/webpack:build",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
@@ -42,7 +41,7 @@
}
},
"serve": {
"executor": "@nx/webpack:dev-server",
"executor": "./tools/executors/webpack:serve",
"options": {
"port": 4210,
"buildTarget": "governance:build:development",
@@ -56,28 +55,22 @@
}
},
"lint": {
"executor": "@nx/linter:eslint",
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": ["apps/governance/**/*.{ts,tsx,js,jsx}"]
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": ["{workspaceRoot}/coverage/apps/governance"],
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/apps/governance"],
"options": {
"jestConfig": "apps/governance/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"build-netlify": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"cp apps/governance/netlify.toml netlify.toml",
@@ -86,7 +79,7 @@
}
},
"build-spec": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/governance/tsconfig.spec.json"
@@ -1,69 +0,0 @@
import { render, fireEvent } from '@testing-library/react';
import { CollapsibleToggle } from './collapsible-toggle';
describe('CollapsibleToggle', () => {
const testId = 'collapsible-toggle';
it('renders without crashing', () => {
const mockSetToggleState = jest.fn();
const { getByTestId, getByText } = render(
<CollapsibleToggle
toggleState={false}
setToggleState={mockSetToggleState}
dataTestId={testId}
>
<div>Test</div>
</CollapsibleToggle>
);
expect(getByTestId(testId)).toBeInTheDocument();
expect(getByText('Test')).toBeInTheDocument();
});
it('calls setToggleState with the opposite of current toggleState when clicked', () => {
const mockSetToggleState = jest.fn();
const { getByTestId } = render(
<CollapsibleToggle
toggleState={false}
setToggleState={mockSetToggleState}
dataTestId={testId}
>
<div>Test</div>
</CollapsibleToggle>
);
fireEvent.click(getByTestId(testId));
expect(mockSetToggleState).toHaveBeenCalledWith(true);
});
it('has the rotate-180 class if toggleState is true', () => {
const mockSetToggleState = jest.fn();
const { getByTestId } = render(
<CollapsibleToggle
toggleState={true}
setToggleState={mockSetToggleState}
dataTestId={testId}
>
<div>Test</div>
</CollapsibleToggle>
);
expect(getByTestId('toggle-icon-wrapper')).toHaveClass('rotate-180');
});
it('does not have the rotate-180 class if toggleState is false', () => {
const mockSetToggleState = jest.fn();
const { getByTestId } = render(
<CollapsibleToggle
toggleState={false}
setToggleState={mockSetToggleState}
dataTestId={testId}
>
<div>Test</div>
</CollapsibleToggle>
);
expect(getByTestId('toggle-icon-wrapper')).not.toHaveClass('rotate-180');
});
});
@@ -1,38 +0,0 @@
import classnames from 'classnames';
import { Icon } from '@vegaprotocol/ui-toolkit';
import type { Dispatch, SetStateAction, ReactNode } from 'react';
interface CollapsibleToggleProps {
toggleState: boolean;
setToggleState: Dispatch<SetStateAction<boolean>>;
children: ReactNode;
dataTestId?: string;
}
export const CollapsibleToggle = ({
toggleState,
setToggleState,
dataTestId,
children,
}: CollapsibleToggleProps) => {
const classes = classnames(
'mb-4 transition-transform ease-in-out duration-300',
{
'rotate-180': toggleState,
}
);
return (
<button
onClick={() => setToggleState(!toggleState)}
data-testid={dataTestId}
>
<div className="flex items-center gap-3">
{children}
<div className={classes} data-testid="toggle-icon-wrapper">
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
);
};
@@ -1 +0,0 @@
export * from './collapsible-toggle';
@@ -833,9 +833,5 @@
"multisigContractIncorrect": "is incorrectly configured. Validator and delegator rewards will be penalised until this is resolved.",
"learnMore": "Learn more",
"AllValidators": "All validators",
"AllProposals": "All proposals",
"RejectedProposals": "Rejected proposals",
"networkGovernance": "Network governance",
"networkUpgrades": "Network upgrades",
"assetSpecification": "Asset specification"
"AllProposals": "All proposals"
}
@@ -0,0 +1,6 @@
import classnames from 'classnames';
export const collapsibleToggleStyles = (toggleState: boolean) =>
classnames('mb-4 transition-transform ease-in-out duration-300', {
'rotate-180': toggleState,
});
@@ -0,0 +1,159 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ProposalAssetQueryVariables = Types.Exact<{
assetId: Types.Scalars['ID'];
}>;
export type ProposalAssetQuery = {
__typename?: 'Query';
asset?: {
__typename?: 'Asset';
status: Types.AssetStatus;
source:
| { __typename?: 'BuiltinAsset' }
| { __typename?: 'ERC20'; contractAddress: string };
} | null;
};
export type AssetListBundleQueryVariables = Types.Exact<{
assetId: Types.Scalars['ID'];
}>;
export type AssetListBundleQuery = {
__typename?: 'Query';
erc20ListAssetBundle?: {
__typename?: 'Erc20ListAssetBundle';
assetSource: string;
vegaAssetId: string;
nonce: string;
signatures: string;
} | null;
};
export const ProposalAssetDocument = gql`
query ProposalAsset($assetId: ID!) {
asset(id: $assetId) {
status
source {
... on ERC20 {
contractAddress
}
}
}
}
`;
/**
* __useProposalAssetQuery__
*
* To run a query within a React component, call `useProposalAssetQuery` and pass it any options that fit your needs.
* When your component renders, `useProposalAssetQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useProposalAssetQuery({
* variables: {
* assetId: // value for 'assetId'
* },
* });
*/
export function useProposalAssetQuery(
baseOptions: Apollo.QueryHookOptions<
ProposalAssetQuery,
ProposalAssetQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<ProposalAssetQuery, ProposalAssetQueryVariables>(
ProposalAssetDocument,
options
);
}
export function useProposalAssetLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
ProposalAssetQuery,
ProposalAssetQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<ProposalAssetQuery, ProposalAssetQueryVariables>(
ProposalAssetDocument,
options
);
}
export type ProposalAssetQueryHookResult = ReturnType<
typeof useProposalAssetQuery
>;
export type ProposalAssetLazyQueryHookResult = ReturnType<
typeof useProposalAssetLazyQuery
>;
export type ProposalAssetQueryResult = Apollo.QueryResult<
ProposalAssetQuery,
ProposalAssetQueryVariables
>;
export const AssetListBundleDocument = gql`
query AssetListBundle($assetId: ID!) {
erc20ListAssetBundle(assetId: $assetId) {
assetSource
vegaAssetId
nonce
signatures
}
}
`;
/**
* __useAssetListBundleQuery__
*
* To run a query within a React component, call `useAssetListBundleQuery` and pass it any options that fit your needs.
* When your component renders, `useAssetListBundleQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useAssetListBundleQuery({
* variables: {
* assetId: // value for 'assetId'
* },
* });
*/
export function useAssetListBundleQuery(
baseOptions: Apollo.QueryHookOptions<
AssetListBundleQuery,
AssetListBundleQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<AssetListBundleQuery, AssetListBundleQueryVariables>(
AssetListBundleDocument,
options
);
}
export function useAssetListBundleLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
AssetListBundleQuery,
AssetListBundleQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<
AssetListBundleQuery,
AssetListBundleQueryVariables
>(AssetListBundleDocument, options);
}
export type AssetListBundleQueryHookResult = ReturnType<
typeof useAssetListBundleQuery
>;
export type AssetListBundleLazyQueryHookResult = ReturnType<
typeof useAssetListBundleLazyQuery
>;
export type AssetListBundleQueryResult = Apollo.QueryResult<
AssetListBundleQuery,
AssetListBundleQueryVariables
>;
@@ -5,11 +5,13 @@ import { MockedProvider } from '@apollo/client/testing';
import type {
AssetListBundleQuery,
ProposalAssetQuery,
} from './__generated__/Asset';
import { AssetListBundleDocument } from './__generated__/Asset';
import { ProposalAssetDocument } from './__generated__/Asset';
} from './__generated___/Asset';
import { AssetListBundleDocument } from './__generated___/Asset';
import { ProposalAssetDocument } from './__generated___/Asset';
import * as Schema from '@vegaprotocol/types';
import type { useWeb3React } from '@web3-react/core';
import BigNumber from 'bignumber.js';
import type { AppState } from '../../../../contexts/app-state/app-state-context';
const mockUseEthTx = {
perform: jest.fn(),
@@ -45,6 +47,23 @@ jest.mock('@web3-react/core', () => {
};
});
const mockAppState: AppState = {
totalAssociated: new BigNumber('50063005'),
decimals: 18,
totalSupply: new BigNumber(65000000),
vegaWalletOverlay: false,
vegaWalletManageOverlay: false,
transactionOverlay: false,
bannerMessage: '',
disconnectNotice: false,
};
jest.mock('../../../contexts/app-state/app-state-context', () => ({
useAppState: () => ({
appState: mockAppState,
}),
}));
const ASSET_ID = 'foo';
const DEFAULT__ASSET: ProposalAssetQuery = {
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import {
useAssetListBundleQuery,
useProposalAssetQuery,
} from './__generated__/Asset';
} from './__generated___/Asset';
import { EthWalletContainer } from '../../../../components/eth-wallet-container';
const useListAsset = (assetId: string) => {
@@ -1 +0,0 @@
export * from './proposal-asset-details';
@@ -1,48 +0,0 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { AssetDetail, AssetDetailsTable } from '@vegaprotocol/assets';
import type { AssetFieldsFragment } from '@vegaprotocol/assets';
export const ProposalAssetDetails = ({
asset,
}: {
asset: AssetFieldsFragment;
}) => {
const { t } = useTranslation();
const [showAssetDetails, setShowAssetDetails] = useState(false);
return (
<section data-testid="proposal-asset-details">
<CollapsibleToggle
toggleState={showAssetDetails}
setToggleState={setShowAssetDetails}
dataTestId={'proposal-asset-details-toggle'}
>
<SubHeading title={t('assetSpecification')} />
</CollapsibleToggle>
{showAssetDetails && (
<div className="mb-10 pb-4">
<AssetDetailsTable
asset={asset}
omitRows={[
AssetDetail.STATUS,
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
AssetDetail.GLOBAL_REWARD_POOL_ACCOUNT_BALANCE,
AssetDetail.MAKER_PAID_FEES_ACCOUNT_BALANCE,
AssetDetail.MAKER_RECEIVED_FEES_ACCOUNT_BALANCE,
AssetDetail.LP_FEE_REWARD_ACCOUNT_BALANCE,
AssetDetail.MARKET_PROPOSER_REWARD_ACCOUNT_BALANCE,
]}
inline={true}
noBorder={true}
dtClassName="text-black dark:text-white text-ui !px-0 !font-normal"
ddClassName="text-black dark:text-white text-ui !px-0 !font-normal max-w-full"
/>
</div>
)}
</section>
);
};
@@ -1,9 +1,9 @@
import ReactMarkdown from 'react-markdown';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
export const ProposalDescription = ({
description,
@@ -15,13 +15,17 @@ export const ProposalDescription = ({
return (
<section data-testid="proposal-description">
<CollapsibleToggle
toggleState={showDescription}
setToggleState={setShowDescription}
dataTestId={'proposal-description-toggle'}
<button
onClick={() => setShowDescription(!showDescription)}
data-testid="proposal-description-toggle"
>
<SubHeading title={t('proposalDescription')} />
</CollapsibleToggle>
<div className="flex items-center gap-3">
<SubHeading title={t('proposalDescription')} />
<div className={collapsibleToggleStyles(showDescription)}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDescription && (
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
@@ -1,8 +1,8 @@
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
@@ -16,13 +16,17 @@ export const ProposalJson = ({
return (
<section data-testid="proposal-json">
<CollapsibleToggle
toggleState={showDetails}
setToggleState={setShowDetails}
dataTestId="proposal-json-toggle"
<button
onClick={() => setShowDetails(!showDetails)}
data-testid="proposal-json-toggle"
>
<SubHeading title={t('proposalJson')} />
</CollapsibleToggle>
<div className="flex items-center gap-3">
<SubHeading title={t('proposalJson')} />
<div className={collapsibleToggleStyles(showDetails)}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDetails && <SyntaxHighlighter data={proposal} />}
</section>
@@ -24,7 +24,7 @@ import {
SyntaxHighlighter,
} from '@vegaprotocol/ui-toolkit';
import { SubHeading } from '../../../../components/heading';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { DataSourceDefinition } from '@vegaprotocol/types';
import { create } from 'zustand';
@@ -77,13 +77,17 @@ export const ProposalMarketData = ({
return (
<section className="relative" data-testid="proposal-market-data">
<CollapsibleToggle
toggleState={showDetails}
setToggleState={setShowDetails}
dataTestId="proposal-market-data-toggle"
<button
onClick={() => setShowDetails(!showDetails)}
data-testid="proposal-market-data-toggle"
>
<SubHeading title={t('marketSpecification')} />
</CollapsibleToggle>
<div className="flex items-center gap-3">
<SubHeading title={t('marketSpecification')} />
<div className={collapsibleToggleStyles(showDetails)}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDetails && (
<>
@@ -5,13 +5,14 @@ import {
KeyValueTableRow,
Thumbs,
RoundedWrapper,
Icon,
} from '@vegaprotocol/ui-toolkit';
import { formatNumber, formatNumberPercentage } from '@vegaprotocol/utils';
import { SubHeading } from '../../../../components/heading';
import { useVoteInformation } from '../../hooks';
import { useAppState } from '../../../../contexts/app-state/app-state-context';
import { ProposalType } from '../proposal/proposal';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
@@ -58,13 +59,17 @@ export const ProposalVotesTable = ({
return (
<>
<CollapsibleToggle
toggleState={showDetails}
setToggleState={setShowDetails}
dataTestId="vote-breakdown-toggle"
<button
onClick={() => setShowDetails(!showDetails)}
data-testid="vote-breakdown-toggle"
>
<SubHeading title={t('voteBreakdown')} />
</CollapsibleToggle>
<div className="flex items-center gap-3">
<SubHeading title={t('voteBreakdown')} />
<div className={collapsibleToggleStyles(showDetails)}>
<Icon name="chevron-down" size={8} />
</div>
</div>
</button>
{showDetails && (
<RoundedWrapper marginBottomLarge={true} paddingBottom={true}>
@@ -3,7 +3,6 @@ import { render, screen } from '@testing-library/react';
import { generateProposal } from '../../test-helpers/generate-proposals';
import { Proposal } from './proposal';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import { ProposalState } from '@vegaprotocol/types';
jest.mock('@vegaprotocol/network-parameters', () => ({
...jest.requireActual('@vegaprotocol/network-parameters'),
@@ -65,17 +64,6 @@ it('Renders with a link back to "all proposals"', async () => {
expect(await screen.findByTestId('all-proposals-link')).toBeInTheDocument();
});
it('Renders a rejected proposals with a link back to "rejected proposals"', async () => {
const proposal = generateProposal({
state: ProposalState.STATE_REJECTED,
});
renderComponent(proposal);
expect(
await screen.findByTestId('rejected-proposals-link')
).toBeInTheDocument();
});
it('renders each section', async () => {
const proposal = generateProposal();
renderComponent(proposal);
@@ -10,7 +10,6 @@ import { ProposalDescription } from '../proposal-description';
import { ProposalChangeTable } from '../proposal-change-table';
import { ProposalJson } from '../proposal-json';
import { ProposalVotesTable } from '../proposal-votes-table';
import { ProposalAssetDetails } from '../proposal-asset-details';
import { VoteDetails } from '../vote-details';
import { ListAsset } from '../list-asset';
import Routes from '../../../routes';
@@ -18,9 +17,6 @@ import { ProposalMarketData } from '../proposal-market-data';
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { MarketInfoWithData } from '@vegaprotocol/markets';
import type { AssetQuery } from '@vegaprotocol/assets';
import { removePaginationWrapper } from '@vegaprotocol/utils';
import { ProposalState } from '@vegaprotocol/types';
export enum ProposalType {
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
@@ -33,7 +29,6 @@ export enum ProposalType {
export interface ProposalProps {
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
newMarketData?: MarketInfoWithData | null;
assetData?: AssetQuery | null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
restData: any;
}
@@ -42,7 +37,6 @@ export const Proposal = ({
proposal,
restData,
newMarketData,
assetData,
}: ProposalProps) => {
const { t } = useTranslation();
const { params, loading, error } = useNetworkParams([
@@ -59,23 +53,6 @@ export const Proposal = ({
return null;
}
let asset = assetData
? removePaginationWrapper(assetData.assetsConnection?.edges)[0]
: undefined;
if (proposal.terms.change.__typename === 'UpdateAsset' && asset) {
asset = {
...asset,
quantum: proposal.terms.change.quantum,
};
if (asset.source.__typename === 'ERC20') {
asset.source.lifetimeLimit = proposal.terms.change.source.lifetimeLimit;
asset.source.withdrawThreshold =
proposal.terms.change.source.withdrawThreshold;
}
}
let minVoterBalance = null;
let proposalType = null;
@@ -114,22 +91,14 @@ export const Proposal = ({
return (
<AsyncRenderer data={params} loading={loading} error={error}>
<section data-testid="proposal">
<div className="flex items-center gap-1">
<div
className="flex items-center gap-1"
data-testid="all-proposals-link"
>
<Icon name={'chevron-left'} />
{proposal.state === ProposalState.STATE_REJECTED ? (
<div data-testid="rejected-proposals-link">
<Link className="underline" to={Routes.PROPOSALS_REJECTED}>
{t('RejectedProposals')}
</Link>
</div>
) : (
<div data-testid="all-proposals-link">
<Link className="underline" to={Routes.PROPOSALS}>
{t('AllProposals')}
</Link>
</div>
)}
<Link className="underline" to={Routes.PROPOSALS}>
{t('AllProposals')}
</Link>
</div>
<ProposalHeader proposal={proposal} isListItem={false} />
@@ -160,14 +129,6 @@ export const Proposal = ({
</div>
)}
{(proposal.terms.change.__typename === 'NewAsset' ||
proposal.terms.change.__typename === 'UpdateAsset') &&
asset && (
<div className="mb-4">
<ProposalAssetDetails asset={asset} />
</div>
)}
<div className="mb-6">
<ProposalJson proposal={restData?.data?.proposal} />
</div>
@@ -1,34 +0,0 @@
import { render, fireEvent, screen } from '@testing-library/react';
import { ProposalsListFilter } from './proposals-list-filter';
describe('ProposalsListFilter', () => {
let setFilterString: jest.Mock;
beforeEach(() => {
setFilterString = jest.fn();
render(
<ProposalsListFilter filterString="" setFilterString={setFilterString} />
);
});
it('renders successfully', () => {
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
});
it('should handle the filter toggle click', () => {
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
});
it('should handle input change', () => {
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'test' },
});
expect(setFilterString).toHaveBeenCalledWith('test');
});
// 'clear filter' tests are handled in the proposals-list.spec.tsx file
// as it is responsible for the filter state
});
@@ -1,16 +1,13 @@
import { useTranslation } from 'react-i18next';
import { useState } from 'react';
import { FormGroup, Icon, Input } from '@vegaprotocol/ui-toolkit';
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
import { ButtonLink, FormGroup, Input } from '@vegaprotocol/ui-toolkit';
import type { Dispatch, SetStateAction } from 'react';
interface ProposalsListFilterProps {
filterString: string;
setFilterString: Dispatch<SetStateAction<string>>;
}
export const ProposalsListFilter = ({
filterString,
setFilterString,
}: ProposalsListFilterProps) => {
const { t } = useTranslation();
@@ -18,39 +15,27 @@ export const ProposalsListFilter = ({
return (
<div data-testid="proposals-list-filter" className="mb-4">
<CollapsibleToggle
toggleState={filterVisible}
setToggleState={setFilterVisible}
dataTestId={'proposal-filter-toggle'}
>
<div className="text-xl mb-4">{t('FilterProposals')}</div>
</CollapsibleToggle>
{!filterVisible && (
<ButtonLink
onClick={() => setFilterVisible(true)}
data-testid="set-proposals-filter-visible"
>
{t('FilterProposals')}
</ButtonLink>
)}
{filterVisible && (
<div data-testid="proposals-list-filter-visible">
<div data-testid="open-proposals-list-filter">
<p>{t('FilterProposalsDescription')}</p>
<FormGroup
label="Filter text input"
labelFor="filter-input"
hideLabel={true}
className="relative"
>
<Input
value={filterString}
data-testid="filter-input"
id="filter-input"
onChange={(e) => setFilterString(e.target.value)}
className="pr-8"
/>
{filterString && filterString.length > 0 && (
<button
className="absolute top-2 right-2"
onClick={() => setFilterString('')}
data-testid="clear-filter"
>
<Icon name="cross" size={6} className="text-vega-light-200" />
</button>
)}
</FormGroup>
</div>
)}
@@ -1,7 +1,4 @@
import {
generateProposal,
generateProtocolUpgradeProposal,
} from '../../test-helpers/generate-proposals';
import { generateProposal } from '../../test-helpers/generate-proposals';
import { MockedProvider } from '@apollo/client/testing';
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { BrowserRouter as Router } from 'react-router-dom';
@@ -18,7 +15,6 @@ import {
nextMonth,
} from '../../test-helpers/mocks';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
const openProposalClosesNextMonth = generateProposal({
id: 'proposal1',
@@ -58,22 +54,12 @@ const failedProposalClosedLastMonth = generateProposal({
},
});
const closedProtocolUpgradeProposal = generateProtocolUpgradeProposal({
upgradeBlockHeight: '1',
});
const renderComponent = (
proposals: ProposalQuery['proposal'][],
protocolUpgradeProposals?: ProtocolUpgradeProposalFieldsFragment[]
) => (
const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
<Router>
<MockedProvider mocks={[networkParamsQueryMock]}>
<AppStateProvider>
<VegaWalletContext.Provider value={mockWalletContext}>
<ProposalsList
proposals={proposals}
protocolUpgradeProposals={protocolUpgradeProposals || []}
/>
<ProposalsList proposals={proposals} protocolUpgradeProposals={[]} />
</VegaWalletContext.Provider>
</AppStateProvider>
</MockedProvider>
@@ -157,15 +143,17 @@ describe('Proposals list', () => {
render(
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
expect(screen.getByTestId('proposals-list-filter')).toBeInTheDocument();
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
expect(
screen.getByTestId('open-proposals-list-filter')
).toBeInTheDocument();
});
it('Filters list by text - party id', () => {
render(
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'bvcx' },
});
@@ -178,7 +166,7 @@ describe('Proposals list', () => {
render(
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'proposal1' },
});
@@ -191,7 +179,7 @@ describe('Proposals list', () => {
render(
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.click(screen.getByTestId('set-proposals-filter-visible'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'osal1' },
});
@@ -199,92 +187,4 @@ describe('Proposals list', () => {
expect(container.querySelector('#proposal1')).toBeInTheDocument();
expect(container.querySelector('#proposal2')).not.toBeInTheDocument();
});
it('When filter is used, clear button is visible', () => {
render(
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'test' },
});
expect(screen.getByTestId('clear-filter')).toBeInTheDocument();
});
it('When clear filter button is used, input is cleared', () => {
render(
renderComponent([openProposalClosesNextMonth, openProposalClosesNextWeek])
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'test' },
});
fireEvent.click(screen.getByTestId('clear-filter'));
expect((screen.getByTestId('filter-input') as HTMLInputElement).value).toBe(
''
);
});
it('Displays a toggle for closed proposals if there are both closed governance proposals and closed upgrade proposals', () => {
render(
renderComponent(
[enactedProposalClosedLastWeek],
[closedProtocolUpgradeProposal]
)
);
expect(screen.getByTestId('toggle-closed-proposals')).toBeInTheDocument();
});
it('Does not display a toggle for closed proposals if there are only closed upgrade proposals', () => {
render(renderComponent([], [closedProtocolUpgradeProposal]));
expect(
screen.queryByTestId('toggle-closed-proposals')
).not.toBeInTheDocument();
});
it('Does not display a toggle for closed proposals if there are only closed governance proposals', () => {
render(renderComponent([enactedProposalClosedLastWeek]));
expect(
screen.queryByTestId('toggle-closed-proposals')
).not.toBeInTheDocument();
});
it('Does not display a toggle for closed proposals if the proposal filter is engaged', () => {
render(
renderComponent(
[enactedProposalClosedLastWeek],
[closedProtocolUpgradeProposal]
)
);
fireEvent.click(screen.getByTestId('proposal-filter-toggle'));
fireEvent.change(screen.getByTestId('filter-input'), {
target: { value: 'test' },
});
expect(
screen.queryByTestId('toggle-closed-proposals')
).not.toBeInTheDocument();
});
it('Displays closed governance proposals by default due to default for the toggle', () => {
render(
renderComponent(
[enactedProposalClosedLastWeek],
[closedProtocolUpgradeProposal]
)
);
expect(
screen.getByTestId('closed-governance-proposals')
).toBeInTheDocument();
});
it('Displays closed upgrade proposals when the toggle is clicked', () => {
render(
renderComponent(
[enactedProposalClosedLastWeek],
[closedProtocolUpgradeProposal]
)
);
fireEvent.click(screen.getByText('Network upgrades'));
expect(screen.getByTestId('closed-upgrade-proposals')).toBeInTheDocument();
});
});
@@ -7,12 +7,7 @@ import { ProposalsListItem } from '../proposals-list-item';
import { ProtocolUpgradeProposalsListItem } from '../protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
import { ProposalsListFilter } from '../proposals-list-filter';
import Routes from '../../../routes';
import {
Button,
Toggle,
VegaIcon,
VegaIconNames,
} from '@vegaprotocol/ui-toolkit';
import { Button, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
import { Link } from 'react-router-dom';
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
@@ -59,11 +54,6 @@ export const orderByUpgradeBlockHeight = (
['desc', 'desc']
);
enum ClosedProposalsViewOptions {
NetworkGovernance = 'networkGovernance',
NetworkUpgrades = 'networkUpgrades',
}
export const ProposalsList = ({
proposals,
protocolUpgradeProposals,
@@ -71,10 +61,6 @@ export const ProposalsList = ({
}: ProposalsListProps) => {
const { t } = useTranslation();
const [filterString, setFilterString] = useState('');
const [closedProposalsView, setClosedProposalsView] =
useState<ClosedProposalsViewOptions>(
ClosedProposalsViewOptions.NetworkGovernance
);
const sortedProposals: SortedProposalsProps = useMemo(() => {
const initialSorting = proposals.reduce(
@@ -123,7 +109,7 @@ export const ProposalsList = ({
);
return {
open: orderByUpgradeBlockHeight(initialSorting.open),
closed: orderByUpgradeBlockHeight(initialSorting.closed),
closed: orderByUpgradeBlockHeight(initialSorting.closed).reverse(),
};
}, [protocolUpgradeProposals, lastBlockHeight]);
@@ -141,7 +127,6 @@ export const ProposalsList = ({
marginBottom={false}
title={t('pageTitleProposals')}
/>
{DocsLinks && (
<div className="xs:justify-self-end" data-testid="new-proposal-link">
<ExternalLink href={DocsLinks.PROPOSALS_GUIDE}>
@@ -155,7 +140,6 @@ export const ProposalsList = ({
</div>
)}
</div>
<p className="mb-8">
{t(
`The Vega network is governed by the community. View active proposals, vote on them or propose changes to the network. Network upgrades are proposed and approved by validators.`
@@ -168,37 +152,20 @@ export const ProposalsList = ({
{t(`Find out more about Vega governance`)}
</ExternalLink>
</p>
{proposals.length > 0 && (
<ProposalsListFilter
filterString={filterString}
setFilterString={(value) => {
setFilterString(value);
if (value.length > 0) {
// If the filter is engaged, ensure the user is viewing governance proposals,
// as network upgrades do not have IDs to filter by and will be excluded.
setClosedProposalsView(
ClosedProposalsViewOptions.NetworkGovernance
);
}
}}
/>
<ProposalsListFilter setFilterString={setFilterString} />
)}
<section className="-mx-4 p-4 mb-8 bg-vega-dark-100">
<SubHeading title={t('openProposals')} />
{sortedProposals.open.length > 0 ||
sortedProtocolUpgradeProposals.open.length > 0 ? (
<ul data-testid="open-proposals">
{filterString.length < 1 &&
sortedProtocolUpgradeProposals.open.map((proposal) => (
<ProtocolUpgradeProposalsListItem
key={proposal.upgradeBlockHeight}
proposal={proposal}
/>
))}
{sortedProtocolUpgradeProposals.open.map((proposal) => (
<ProtocolUpgradeProposalsListItem
key={proposal.upgradeBlockHeight}
proposal={proposal}
/>
))}
{sortedProposals.open.filter(filterPredicate).map((proposal) => (
<ProposalsListItem key={proposal?.id} proposal={proposal} />
))}
@@ -209,81 +176,22 @@ export const ProposalsList = ({
</p>
)}
</section>
<section className="relative">
<section>
<SubHeading title={t('closedProposals')} />
{sortedProposals.closed.length > 0 ||
sortedProtocolUpgradeProposals.closed.length > 0 ? (
<>
{
// We need both the closed proposals and closed protocol upgrade
// proposals to be present for there to be a toggle. It also gets
// hidden if the user has filtered the list, as the upgrade proposals
// do not have the necessary fields for filtering.
sortedProposals.closed.length > 0 &&
sortedProtocolUpgradeProposals.closed.length > 0 &&
filterString.length < 1 && (
<div
className="grid w-full justify-end xl:-mt-12 pb-6"
data-testid="toggle-closed-proposals"
>
<div className="w-[440px]">
<Toggle
name="closed-proposals-toggle"
toggles={[
{
label: t(
ClosedProposalsViewOptions.NetworkGovernance
),
value: ClosedProposalsViewOptions.NetworkGovernance,
},
{
label: t(
ClosedProposalsViewOptions.NetworkUpgrades
),
value: ClosedProposalsViewOptions.NetworkUpgrades,
},
]}
checkedValue={closedProposalsView}
onChange={(e) =>
setClosedProposalsView(
e.target.value as ClosedProposalsViewOptions
)
}
/>
</div>
</div>
)
}
<ul data-testid="closed-proposals">
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
<ProtocolUpgradeProposalsListItem
key={proposal.upgradeBlockHeight}
proposal={proposal}
/>
))}
<ul data-testid="closed-proposals">
{closedProposalsView ===
ClosedProposalsViewOptions.NetworkUpgrades && (
<div data-testid="closed-upgrade-proposals">
{sortedProtocolUpgradeProposals.closed.map((proposal) => (
<ProtocolUpgradeProposalsListItem
key={proposal.upgradeBlockHeight}
proposal={proposal}
/>
))}
</div>
)}
{closedProposalsView ===
ClosedProposalsViewOptions.NetworkGovernance && (
<div data-testid="closed-governance-proposals">
{sortedProposals.closed
.filter(filterPredicate)
.map((proposal) => (
<ProposalsListItem
key={proposal?.id}
proposal={proposal}
/>
))}
</div>
)}
</ul>
</>
{sortedProposals.closed.filter(filterPredicate).map((proposal) => (
<ProposalsListItem key={proposal?.id} proposal={proposal} />
))}
</ul>
) : (
<p className="mb-0" data-testid="no-closed-proposals">
{t('noClosedProposals')}
@@ -23,10 +23,7 @@ export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
return (
<>
<Heading title={t('pageTitleRejectedProposals')} />
<ProposalsListFilter
filterString={filterString}
setFilterString={setFilterString}
/>
<ProposalsListFilter setFilterString={setFilterString} />
<section>
{proposals.length > 0 ? (
<ul data-testid="rejected-proposals">
@@ -9,7 +9,6 @@ import { useFetch } from '@vegaprotocol/react-helpers';
import { ENV } from '../../../config';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
import { useAssetQuery } from '@vegaprotocol/assets';
export const ProposalContainer = () => {
const params = useParams<{ proposalId: string }>();
@@ -36,25 +35,6 @@ export const ProposalContainer = () => {
},
});
const {
data: assetData,
loading: assetLoading,
error: assetError,
} = useAssetQuery({
fetchPolicy: 'network-only',
variables: {
assetId:
(data?.proposal?.terms.change.__typename === 'NewAsset' &&
data?.proposal?.id) ||
(data?.proposal?.terms.change.__typename === 'UpdateAsset' &&
data.proposal.terms.change.assetId) ||
'',
},
skip: !['NewAsset', 'UpdateAsset'].includes(
data?.proposal?.terms?.change?.__typename || ''
),
});
useEffect(() => {
const interval = setInterval(refetch, 2000);
return () => clearInterval(interval);
@@ -62,20 +42,15 @@ export const ProposalContainer = () => {
return (
<AsyncRenderer
loading={loading || newMarketLoading || assetLoading}
error={error || newMarketError || assetError}
data={{
...data,
...(newMarketData ? { newMarketData } : {}),
...(assetData ? { assetData } : {}),
}}
loading={loading || newMarketLoading}
error={error || newMarketError}
data={newMarketData ? { newMarketData, data } : data}
>
{data?.proposal ? (
<Proposal
proposal={data.proposal}
restData={restData}
newMarketData={newMarketData}
assetData={assetData}
/>
) : (
<ProposalNotFound />
@@ -1,5 +1,4 @@
import * as Schema from '@vegaprotocol/types';
import { ProtocolUpgradeProposalStatus } from '@vegaprotocol/types';
import BigNumber from 'bignumber.js';
import * as faker from 'faker';
import isArray from 'lodash/isArray';
@@ -7,40 +6,6 @@ import mergeWith from 'lodash/mergeWith';
import type { PartialDeep } from 'type-fest';
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
export function generateProtocolUpgradeProposal(
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
): ProtocolUpgradeProposalFieldsFragment {
const defaultProposal: ProtocolUpgradeProposalFieldsFragment = {
__typename: 'ProtocolUpgradeProposal',
upgradeBlockHeight: '3917600',
vegaReleaseTag: 'v0.71.6',
approvers: [
'0ac70c4ccc7f961614fe49b93e639ddf916269b7dcf8391db264cefeadf5a6b7',
'63a1755006642bda9ab1bfa84660f944d30a113d1609590ca90c50b24aede472',
'68ed0770fc3e67b74d09c05443243d27e29a8513dc0e8628beb98338cd509159',
'a6e6f7daf8610f9242ab6ab46b394f6fb79cf9533d48051ca7a2f142b8b700a8',
'aad2be546ba83cbcab4c1d57ebe22b4a942f294f54333f1a7c2c9ef0e9fe19bb',
'acc55c7205cfcd5480e0235acab56a01487a39dc858a641fc04df6ba016870ee',
'b7e500deb24cc19bd6ebb2311997f0904ca0d9e51541249e9650ab41fd8ac376',
'cf295dff6d9506e8a905d168a44dfcff2f64bd0a6671783a469f8322959c62e2',
'f4686749895bf51c6df4092ef6be4279c384a3c380c24ea7a2fd20afc602a35d',
],
status:
ProtocolUpgradeProposalStatus.PROTOCOL_UPGRADE_PROPOSAL_STATUS_APPROVED,
};
return mergeWith<
ProtocolUpgradeProposalFieldsFragment,
PartialDeep<ProtocolUpgradeProposalFieldsFragment>
>(defaultProposal, override, (objValue, srcValue) => {
if (!isArray(objValue)) {
return;
}
return srcValue;
});
}
export function generateProposal(
override: PartialDeep<ProposalQuery['proposal']> = {}
@@ -6,7 +6,7 @@ jest.mock('../../../../../components/connect-to-vega', () => ({
ConnectToVega: () => <div data-testid="connect-to-vega" />,
}));
jest.mock('../../../../../components/eth-connect-prompt', () => ({
jest.mock('../../../../components/eth-connect-prompt', () => ({
EthConnectPrompt: () => <div data-testid="eth-connect-prompt" />,
}));
@@ -0,0 +1,64 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type NodeDataQueryVariables = Types.Exact<{ [key: string]: never }>;
export type NodeDataQuery = {
__typename?: 'Query';
nodeData?: { __typename?: 'NodeData'; stakedTotal: string } | null;
};
export const NodeDataDocument = gql`
query NodeData {
nodeData {
stakedTotal
}
}
`;
/**
* __useNodeDataQuery__
*
* To run a query within a React component, call `useNodeDataQuery` and pass it any options that fit your needs.
* When your component renders, `useNodeDataQuery` returns an object from Apollo Client that contains loading, error, and data properties
* you can use to render your UI.
*
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
*
* @example
* const { data, loading, error } = useNodeDataQuery({
* variables: {
* },
* });
*/
export function useNodeDataQuery(
baseOptions?: Apollo.QueryHookOptions<NodeDataQuery, NodeDataQueryVariables>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useQuery<NodeDataQuery, NodeDataQueryVariables>(
NodeDataDocument,
options
);
}
export function useNodeDataLazyQuery(
baseOptions?: Apollo.LazyQueryHookOptions<
NodeDataQuery,
NodeDataQueryVariables
>
) {
const options = { ...defaultOptions, ...baseOptions };
return Apollo.useLazyQuery<NodeDataQuery, NodeDataQueryVariables>(
NodeDataDocument,
options
);
}
export type NodeDataQueryHookResult = ReturnType<typeof useNodeDataQuery>;
export type NodeDataLazyQueryHookResult = ReturnType<
typeof useNodeDataLazyQuery
>;
export type NodeDataQueryResult = Apollo.QueryResult<
NodeDataQuery,
NodeDataQueryVariables
>;
+1 -1
View File
@@ -11,7 +11,7 @@ import type { RouteChildProps } from '..';
import Routes from '../routes';
import { TokenDetails } from './token-details';
import { Button } from '@vegaprotocol/ui-toolkit';
import { useNodeDataQuery } from './__generated__/NodeData';
import { useNodeDataQuery } from './__generated___/NodeData';
const Home = ({ name }: RouteChildProps) => {
useDocumentTitle(name);
+3 -2
View File
@@ -1,5 +1,6 @@
@import 'ag-grid-community/styles/ag-grid.css';
@import 'ag-grid-community/styles/ag-theme-balham.css';
@import 'ag-grid-community/dist/styles/ag-grid.css';
@import 'ag-grid-community/dist/styles/ag-theme-balham.css';
@import 'ag-grid-community/dist/styles/ag-theme-balham-dark.css';
@tailwind base;
@tailwind components;
+1 -1
View File
@@ -1,5 +1,5 @@
const { join } = require('path');
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
const { createGlobPatternsForDependencies } = require('@nrwl/next/tailwind');
const theme = require('../../libs/tailwindcss-config/src/theme');
const vegaCustomClasses = require('../../libs/tailwindcss-config/src/vega-custom-classes');
+2 -2
View File
@@ -5,8 +5,8 @@
"types": ["node"]
},
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"**/*.spec.ts",
+2 -2
View File
@@ -18,7 +18,7 @@
"jest.config.ts"
],
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
]
}
+2 -4
View File
@@ -1,8 +1,6 @@
const { composePlugins, withNx } = require('@nx/webpack');
const { withReact } = require('@nx/react');
const SentryPlugin = require('@sentry/webpack-plugin');
module.exports = composePlugins(withNx(), withReact(), (config, context) => {
module.exports = (config, context) => {
const additionalPlugins = process.env.SENTRY_AUTH_TOKEN
? [
new SentryPlugin({
@@ -15,4 +13,4 @@ module.exports = composePlugins(withNx(), withReact(), (config, context) => {
...config,
plugins: [...additionalPlugins, ...config.plugins],
};
});
};
+1 -1
View File
@@ -1,7 +1,7 @@
{
"presets": [
[
"@nx/react/babel",
"@nrwl/react/babel",
{
"runtime": "automatic"
}
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__"],
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
@@ -3,8 +3,8 @@ export default {
displayName: 'liquidity-provision-dashboard',
preset: '../../jest.preset.js',
transform: {
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nrwl/next/babel'] }],
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/apps/liquidity-provision-dashboard',
@@ -1,11 +1,10 @@
{
"name": "liquidity-provision-dashboard",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/liquidity-provision-dashboard/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/webpack:webpack",
"executor": "@nrwl/web:webpack",
"outputs": ["{options.outputPath}"],
"defaultConfiguration": "production",
"options": {
@@ -22,7 +21,7 @@
],
"styles": ["apps/liquidity-provision-dashboard/src/styles.scss"],
"scripts": [],
"webpackConfig": "@nx/react/plugins/webpack"
"webpackConfig": "@nrwl/react/plugins/webpack"
},
"configurations": {
"development": {
@@ -48,7 +47,7 @@
}
},
"serve": {
"executor": "@nx/webpack:dev-server",
"executor": "./tools/executors/webpack:serve",
"options": {
"buildTarget": "liquidity-provision-dashboard:build",
"hmr": true,
@@ -65,7 +64,7 @@
}
},
"lint": {
"executor": "@nx/linter:eslint",
"executor": "@nrwl/linter:eslint",
"outputs": ["{options.outputFile}"],
"options": {
"lintFilePatterns": [
@@ -74,23 +73,15 @@
}
},
"test": {
"executor": "@nx/jest:jest",
"outputs": [
"{workspaceRoot}/coverage/apps/liquidity-provision-dashboard"
],
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/apps/liquidity-provision-dashboard"],
"options": {
"jestConfig": "apps/liquidity-provision-dashboard/jest.config.ts",
"passWithNoTests": true
},
"configurations": {
"ci": {
"ci": true,
"codeCoverage": true
}
}
},
"build-netlify": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"cp apps/liquidity-provision-dashboard/netlify.toml netlify.toml",
@@ -99,7 +90,7 @@
}
},
"build-spec": {
"executor": "nx:run-commands",
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/liquidity-provision-dashboard/tsconfig.spec.json"
@@ -21,14 +21,11 @@ import {
HealthBar,
TooltipCellComponent,
} from '@vegaprotocol/ui-toolkit';
import type {
GetRowIdParams,
RowClickedEvent,
ColDef,
} from 'ag-grid-community';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import { useCallback, useState, useMemo } from 'react';
import type { GetRowIdParams, RowClickedEvent } from 'ag-grid-community';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import { AgGridColumn } from 'ag-grid-react';
import { useCallback, useState } from 'react';
import { Grid } from '../../grid';
import { HealthDialog } from '../../health-dialog';
@@ -42,234 +39,6 @@ export const MarketList = () => {
const consoleLink = useLinks(DApp.Console);
const getRowId = useCallback(({ data }: GetRowIdParams) => data.id, []);
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('Market (futures)'),
field: 'tradableInstrument.instrument.name',
cellRenderer: ({ value, data }: { value: string; data: Market }) => {
return (
<>
<span className="leading-3">{value}</span>
<span className="leading-3">
{
data?.tradableInstrument?.instrument?.product?.settlementAsset
?.symbol
}
</span>
</>
);
},
minWidth: 100,
flex: 1,
headerTooltip: t('The market name and settlement asset'),
},
{
headerName: t('Market Code'),
headerTooltip: t(
'The market code is a unique identifier for this market'
),
field: 'tradableInstrument.instrument.code',
},
{
headerName: t('Type'),
headerTooltip: t('Type'),
field: 'tradableInstrument.instrument.product.__typename',
},
{
headerName: t('Last Price'),
headerTooltip: t('Latest price for this market'),
field: 'data.markPrice',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Market, 'data.markPrice'>) =>
value && data
? formatWithAsset(
value,
data.tradableInstrument.instrument.product.settlementAsset
)
: '-',
},
{
headerName: t('Change (24h)'),
headerTooltip: t('Change in price over the last 24h'),
cellRenderer: ({
data,
}: VegaValueFormatterParams<Market, 'data.candles'>) => {
if (data && data.candles) {
const prices = data.candles.map((candle) => candle.close);
return (
<PriceChangeCell
candles={prices}
decimalPlaces={data?.decimalPlaces}
/>
);
} else return <div>{t('-')}</div>;
},
},
{
headerName: t('Volume (24h)'),
field: 'dayVolume',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Market, 'dayVolume'>) =>
value && data
? `${addDecimalsFormatNumber(
value,
data.tradableInstrument.instrument.product.settlementAsset
.decimals
)} (${displayChange(data.volumeChange)})`
: '-',
headerTooltip: t('The trade volume over the last 24h'),
},
{
headerName: t('Total staked by LPs'),
field: 'liquidityCommitted',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Market, 'liquidityCommitted'>) =>
data && value
? formatWithAsset(
value.toString(),
data.tradableInstrument.instrument.product.settlementAsset
)
: '-',
headerTooltip: t('The amount of funds allocated to provide liquidity'),
},
{
headerName: t('Target stake'),
field: 'target',
valueFormatter: ({
value,
data,
}: VegaValueFormatterParams<Market, 'target'>) =>
data && value
? formatWithAsset(
value,
data.tradableInstrument.instrument.product.settlementAsset
)
: '-',
headerTooltip: t(
'The ideal committed liquidity to operate the market. If total commitment currently below this level then LPs can set the fee level with new commitment.'
),
},
{
headerName: t('% Target stake met'),
valueFormatter: ({ data }: VegaValueFormatterParams<Market, ''>) => {
if (data) {
const roundedPercentage =
parseInt(
(data.liquidityCommitted / parseFloat(data.target)).toFixed(0)
) * 100;
const display = Number.isNaN(roundedPercentage)
? 'N/A'
: formatNumberPercentage(toBigNum(roundedPercentage, 0), 0);
return display;
} else return '-';
},
headerTooltip: t('% Target stake met'),
},
{
headerName: t('Fee levels'),
field: 'fees',
valueFormatter: ({ value }: VegaValueFormatterParams<Market, 'fees'>) =>
value ? `${value.factors.liquidityFee}%` : '-',
headerTooltip: t('Fee level for this market'),
},
{
headerName: t('Status'),
field: 'tradingMode',
cellRenderer: ({
value,
data,
}: {
value: Schema.MarketTradingMode;
data: Market;
}) => {
return <Status trigger={data.data?.trigger} tradingMode={value} />;
},
headerTooltip: t(
'The current market status - those below the target stake mark are most in need of liquidity'
),
},
{
headerComponent: () => {
return (
<div>
<span>{t('Health')}</span>{' '}
<button
onClick={() => setIsHealthDialogOpen(true)}
aria-label={t('open tooltip')}
>
<Icon name="info-sign" />
</button>
</div>
);
},
field: 'tradingMode',
cellRenderer: ({
value,
data,
}: {
value: Schema.MarketTradingMode;
data: Market;
}) => (
<HealthBar
target={data.target}
decimals={
data.tradableInstrument.instrument.product.settlementAsset
.decimals
}
levels={data.feeLevels}
intent={intentForStatus(value)}
/>
),
sortable: false,
cellStyle: { overflow: 'unset' },
},
{
headerName: t('Age'),
field: 'marketTimestamps.open',
headerTooltip: t('Age of the market'),
valueFormatter: ({
value,
}: VegaValueFormatterParams<Market, 'marketTimestamps.open'>) => {
return value ? formatDistanceToNow(new Date(value)) : '-';
},
},
{
headerName: t('Closing Time'),
field: 'tradableInstrument.instrument.metadata.tags',
headerTooltip: t('Closing time of the market'),
valueFormatter: ({ data }: VegaValueFormatterParams<Market, ''>) => {
let expiry;
if (data?.tradableInstrument.instrument.metadata.tags) {
expiry = getExpiryDate(
data?.tradableInstrument.instrument.metadata.tags,
data?.marketTimestamps.close,
data?.state
);
}
return expiry ? expiry : '-';
},
},
],
[]
);
return (
<AsyncRenderer loading={loading} error={error} data={data}>
@@ -295,11 +64,258 @@ export const MarketList = () => {
cellClass: ['flex', 'flex-col', 'justify-center'],
tooltipComponent: TooltipCellComponent,
}}
columnDefs={columnDefs}
getRowId={getRowId}
isRowClickable
tooltipShowDelay={500}
/>
>
<AgGridColumn
headerName={t('Market (futures)')}
field="tradableInstrument.instrument.name"
cellRenderer={({
value,
data,
}: {
value: string;
data: Market;
}) => {
return (
<>
<span className="leading-3">{value}</span>
<span className="leading-3">
{
data?.tradableInstrument?.instrument?.product
?.settlementAsset?.symbol
}
</span>
</>
);
}}
minWidth={100}
flex="1"
headerTooltip={t('The market name and settlement asset')}
/>
<AgGridColumn
headerName={t('Market Code')}
headerTooltip={t(
'The market code is a unique identifier for this market'
)}
field="tradableInstrument.instrument.code"
/>
<AgGridColumn
headerName={t('Type')}
headerTooltip={t('Type')}
field="tradableInstrument.instrument.product.__typename"
/>
<AgGridColumn
headerName={t('Last Price')}
headerTooltip={t('Latest price for this market')}
field="data.markPrice"
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<Market, 'data.markPrice'>) =>
value && data
? formatWithAsset(
value,
data.tradableInstrument.instrument.product.settlementAsset
)
: '-'
}
/>
<AgGridColumn
headerName={t('Change (24h)')}
headerTooltip={t('Change in price over the last 24h')}
cellRenderer={({
data,
}: VegaValueFormatterParams<Market, 'data.candles'>) => {
if (data && data.candles) {
const prices = data.candles.map((candle) => candle.close);
return (
<PriceChangeCell
candles={prices}
decimalPlaces={data?.decimalPlaces}
/>
);
} else return <div>{t('-')}</div>;
}}
/>
<AgGridColumn
headerName={t('Volume (24h)')}
field="dayVolume"
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<Market, 'dayVolume'>) =>
value && data
? `${addDecimalsFormatNumber(
value,
data.tradableInstrument.instrument.product.settlementAsset
.decimals
)} (${displayChange(data.volumeChange)})`
: '-'
}
headerTooltip={t('The trade volume over the last 24h')}
/>
<AgGridColumn
headerName={t('Total staked by LPs')}
field="liquidityCommitted"
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<Market, 'liquidityCommitted'>) =>
data && value
? formatWithAsset(
value.toString(),
data.tradableInstrument.instrument.product.settlementAsset
)
: '-'
}
headerTooltip={t(
'The amount of funds allocated to provide liquidity'
)}
/>
<AgGridColumn
headerName={t('Target stake')}
field="target"
valueFormatter={({
value,
data,
}: VegaValueFormatterParams<Market, 'target'>) =>
data && value
? formatWithAsset(
value,
data.tradableInstrument.instrument.product.settlementAsset
)
: '-'
}
headerTooltip={t(
'The ideal committed liquidity to operate the market. If total commitment currently below this level then LPs can set the fee level with new commitment.'
)}
/>
<AgGridColumn
headerName={t('% Target stake met')}
valueFormatter={({
data,
}: VegaValueFormatterParams<Market, ''>) => {
if (data) {
const roundedPercentage =
parseInt(
(data.liquidityCommitted / parseFloat(data.target)).toFixed(
0
)
) * 100;
const display = Number.isNaN(roundedPercentage)
? 'N/A'
: formatNumberPercentage(toBigNum(roundedPercentage, 0), 0);
return display;
} else return '-';
}}
headerTooltip={t('% Target stake met')}
/>
<AgGridColumn
headerName={t('Fee levels')}
field="fees"
valueFormatter={({
value,
}: VegaValueFormatterParams<Market, 'fees'>) =>
value ? `${value.factors.liquidityFee}%` : '-'
}
headerTooltip={t('Fee level for this market')}
/>
<AgGridColumn
headerName={t('Status')}
field="tradingMode"
cellRenderer={({
value,
data,
}: {
value: Schema.MarketTradingMode;
data: Market;
}) => {
return (
<Status trigger={data.data?.trigger} tradingMode={value} />
);
}}
headerTooltip={t(
'The current market status - those below the target stake mark are most in need of liquidity'
)}
/>
<AgGridColumn
headerComponent={() => {
return (
<div>
<span>{t('Health')}</span>{' '}
<button
onClick={() => setIsHealthDialogOpen(true)}
aria-label={t('open tooltip')}
>
<Icon name="info-sign" />
</button>
</div>
);
}}
field="tradingMode"
cellRenderer={({
value,
data,
}: {
value: Schema.MarketTradingMode;
data: Market;
}) => (
<HealthBar
target={data.target}
decimals={
data.tradableInstrument.instrument.product.settlementAsset
.decimals
}
levels={data.feeLevels}
intent={intentForStatus(value)}
/>
)}
sortable={false}
cellStyle={{ overflow: 'unset' }}
/>
<AgGridColumn
headerName={t('Age')}
field="marketTimestamps.open"
headerTooltip={t('Age of the market')}
valueFormatter={({
value,
}: VegaValueFormatterParams<Market, 'marketTimestamps.open'>) => {
return value ? formatDistanceToNow(new Date(value)) : '-';
}}
/>
<AgGridColumn
headerName={t('Closing Time')}
field="tradableInstrument.instrument.metadata.tags"
headerTooltip={t('Closing time of the market')}
valueFormatter={({
data,
}: VegaValueFormatterParams<Market, ''>) => {
let expiry;
if (data?.tradableInstrument.instrument.metadata.tags) {
expiry = getExpiryDate(
data?.tradableInstrument.instrument.metadata.tags,
data?.marketTimestamps.close,
data?.state
);
}
return expiry ? expiry : '-';
}}
/>
</Grid>
<HealthDialog
isOpen={isHealthDialogOpen}
onChange={() => {
@@ -1,6 +1,7 @@
import { useCallback, useMemo } from 'react';
import { useCallback } from 'react';
import { AgGridColumn } from 'ag-grid-react';
import type { GetRowIdParams, ColDef } from 'ag-grid-community';
import type { GetRowIdParams } from 'ag-grid-community';
import { t } from '@vegaprotocol/i18n';
import type {
@@ -35,75 +36,6 @@ export const LPProvidersGrid = ({
};
}) => {
const getRowId = useCallback(({ data }: GetRowIdParams) => data.party.id, []);
const columnDefs = useMemo<ColDef[]>(
() => [
{
headerName: t('LPs'),
field: 'party.id',
flex: 1,
minWidth: 100,
headerTooltip: t('Liquidity providers'),
},
{
headerName: t('Duration'),
valueFormatter: formatToHours,
field: 'createdAt',
headerTooltip: t('Time in market'),
},
{
headerName: t('Equity-like share'),
field: 'equityLikeShare',
valueFormatter: ({ value }: { value?: string | null }) => {
return value
? `${parseFloat(parseFloat(value).toFixed(2)) * 100}%`
: '';
},
headerTooltip: t(
'The share of the markets liquidity held - the earlier you commit liquidity the greater % fees you earn'
),
minWidth: 140,
},
{
headerName: t('committed bond'),
field: 'commitmentAmount',
valueFormatter: ({ value }: { value?: string | null }) =>
value ? formatWithAsset(value, settlementAsset) : '0',
headerTooltip: t('The amount of funds allocated to provide liquidity'),
minWidth: 140,
},
{
headerName: t('Margin Req.'),
field: 'margin',
headerTooltip: t(
'Margin required for arising positions based on liquidity commitment'
),
},
{
headerName: t('24h Fees'),
field: 'fees',
headerTooltip: t(
'Total fees earned by the liquidity provider in the last 24 hours'
),
},
{
headerName: t('Fee level'),
valueFormatter: ({ value }: { value?: string | null }) => `${value}%`,
field: 'fee',
headerTooltip: t(
"The market's liquidity fee, or the percentage of a trade's value which is collected from the price taker for every trade"
),
},
{
headerName: t('APY'),
field: 'apy',
headerTooltip: t(
'An annualised estimate based on the total liquidity provision fees and maker fees collected by liquidity providers, the maximum margin needed and maximum commitment (bond) over the course of 7 epochs'
),
},
],
[settlementAsset]
);
return (
<Grid
@@ -117,9 +49,74 @@ export const LPProvidersGrid = ({
tooltipComponent: TooltipCellComponent,
minWidth: 100,
}}
columnDefs={columnDefs}
getRowId={getRowId}
rowHeight={92}
/>
>
<AgGridColumn
headerName={t('LPs')}
field="party.id"
flex="1"
minWidth={100}
headerTooltip={t('Liquidity providers')}
/>
<AgGridColumn
headerName={t('Duration')}
valueFormatter={formatToHours}
field="createdAt"
headerTooltip={t('Time in market')}
/>
<AgGridColumn
headerName={t('Equity-like share')}
field="equityLikeShare"
valueFormatter={({ value }: { value?: string | null }) => {
return value
? `${parseFloat(parseFloat(value).toFixed(2)) * 100}%`
: '';
}}
headerTooltip={t(
'The share of the markets liquidity held - the earlier you commit liquidity the greater % fees you earn'
)}
minWidth={140}
/>
<AgGridColumn
headerName={t('committed bond')}
field="commitmentAmount"
valueFormatter={({ value }: { value?: string | null }) =>
value ? formatWithAsset(value, settlementAsset) : '0'
}
headerTooltip={t('The amount of funds allocated to provide liquidity')}
minWidth={140}
/>
<AgGridColumn
headerName={t('Margin Req.')}
field="margin"
headerTooltip={t(
'Margin required for arising positions based on liquidity commitment'
)}
/>
<AgGridColumn
headerName={t('24h Fees')}
field="fees"
headerTooltip={t(
'Total fees earned by the liquidity provider in the last 24 hours'
)}
/>
<AgGridColumn
headerName={t('Fee level')}
valueFormatter={({ value }: { value?: string | null }) => `${value}%`}
field="fee"
headerTooltip={t(
"The market's liquidity fee, or the percentage of a trade's value which is collected from the price taker for every trade"
)}
/>
<AgGridColumn
headerName={t('APY')}
field="apy"
headerTooltip={t(
'An annualised estimate based on the total liquidity provision fees and maker fees collected by liquidity providers, the maximum margin needed and maximum commitment (bond) over the course of 7 epochs'
)}
/>
</Grid>
);
};
@@ -1,4 +1,5 @@
import { useRef, useCallback, useEffect } from 'react';
import type { ReactNode } from 'react';
import { AgGridReact } from 'ag-grid-react';
import type {
AgGridReactProps,
@@ -6,17 +7,18 @@ import type {
AgGridReact as AgGridReactType,
} from 'ag-grid-react';
import classNames from 'classnames';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';
import 'ag-grid-community/dist/styles/ag-grid.css';
import 'ag-grid-community/dist/styles/ag-theme-alpine.css';
import './grid.scss';
type Props = (AgGridReactProps | AgReactUiProps) & {
isRowClickable?: boolean;
style?: React.CSSProperties;
children: ReactNode;
};
export const Grid = ({ isRowClickable, ...props }: Props) => {
export const Grid = ({ isRowClickable, children, ...props }: Props) => {
const gridRef = useRef<AgGridReactType | null>(null);
const resizeGrid = useCallback(() => {
@@ -42,6 +44,8 @@ export const Grid = ({ isRowClickable, ...props }: Props) => {
onGridReady={handleOnGridReady}
suppressRowClickSelection
{...props}
/>
>
{children}
</AgGridReact>
);
};
@@ -1,5 +1,5 @@
const { join } = require('path');
const { createGlobPatternsForDependencies } = require('@nx/react/tailwind');
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');
@@ -5,8 +5,8 @@
"types": ["node"]
},
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": [
"jest.config.ts",
@@ -21,7 +21,7 @@
"**/*.d.ts"
],
"files": [
"../../node_modules/@nx/react/typings/cssmodule.d.ts",
"../../node_modules/@nx/react/typings/image.d.ts"
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
]
}
+1 -1
View File
@@ -1,7 +1,7 @@
{
"presets": [
[
"@nx/react/babel",
"@nrwl/react/babel",
{
"runtime": "automatic"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__"],
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],

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