Compare commits

..
Author SHA1 Message Date
Matthew Russell 829d3112e1 chore: change test to mock requestNode 2022-07-12 15:09:25 +01:00
Botond 73cda26e17 feat: validate connected node on init WIP 2022-07-11 17:07:30 +01:00
Botond f34ddaa7b9 chore: merge upstream 2022-07-07 14:32:09 +01:00
Botond 20371f56f7 chore: merge master 2022-07-07 12:17:21 +01:00
Botond 261ec8c5e4 fix: lint 2022-07-07 11:43:53 +01:00
Botond d577080a86 fix: format 2022-07-07 11:32:13 +01:00
Botond c5deddb6d8 fix: cache key error 2022-07-07 11:10:50 +01:00
Botond 4acf2a7f8c fix: lint 2022-07-07 10:15:19 +01:00
Botond fdfae74524 fix: lint 2022-07-07 10:14:24 +01:00
Botond 7f9c1178c4 fix: dont open node switcher on init 2022-07-07 09:49:59 +01:00
Botond b633dbc049 chore: merge master 2022-07-07 09:46:02 +01:00
Botond 92315181e1 feat: wip refactor config hook to run on init 2022-07-06 19:15:10 +01:00
Botond 9b7c26386b feat: add custom input for node and more tests 2022-07-06 12:22:07 +01:00
Botond 312db3ea0f fix: flaky node hook test 2022-07-04 15:25:34 +01:00
Botond 4d9bcde560 fix: add controlled value to radio group 2022-07-04 14:23:26 +01:00
Botond e5fc3d1bc4 fix: layout styles 2022-07-04 14:21:56 +01:00
Botond 799cb04cae fix: remove redundant wrapper 2022-07-04 13:49:18 +01:00
Botond e03ba3256f fix: types 2022-07-04 13:17:38 +01:00
Botond 0c6212e82b fix: format 2022-07-04 12:15:03 +01:00
Botond 71ae12007b chore: merge master 2022-07-04 11:28:34 +01:00
Botond ccc4d39aec feat: generate apollo types and add tests 2022-07-04 11:24:33 +01:00
Botond 5d4ce7e2f2 chore: remove hook form from node switcher 2022-07-01 14:04:20 +01:00
Botond 3f77986885 feat: add node swicther 2022-07-01 10:19:25 +01:00
2270 changed files with 49986 additions and 148860 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
apps/**/dist/*
apps/**/node_modules/*
dist/*
node_modules/*
tmp/*
.dockerignore
dockerfiles
Dockerfile
+3 -3
View File
@@ -51,7 +51,8 @@
"ul": ["list"],
"ol": ["list"]
}
]
],
"curly": "warn"
}
},
{
@@ -74,8 +75,7 @@
"prefer": "type-imports",
"disallowTypeAnnotations": true
}
],
"curly": ["error", "multi-line"]
]
}
},
{
-45
View File
@@ -1,45 +0,0 @@
---
name: "\U0001F41B Bug"
about: Create a report to help us improve
title: ''
labels: "\U0001F41B bug"
assignees: ''
---
## Description
A clear and concise description of what the bug is.
## Steps to Reproduce
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
## Expected behavior
A clear and concise description of what you expected to happen.
## Screenshots
If applicable, add screenshots to help explain your problem.
## Device details
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
## Additional context
Add any other context about the problem here.
-21
View File
@@ -1,21 +0,0 @@
---
name: Chore
about:
A template to capture non feature / enhancement work we need to do like work
to support other functions or housekeeping etc.
title: ''
labels: chore
assignees: ''
---
## The Chore
What we need to achieve and who for
## Tasks
- [ ] What do we need to do first
- [ ] and then what?
- [ ] Etc.
## Additional details / background info
-33
View File
@@ -1,33 +0,0 @@
---
name: User Story
about:
A template to describe a features or enhancements we want to make from a users
perspective to ensure we understand what is needed and the value it would add
title: ''
labels: feature
assignees: ''
---
## Story
As a
I want
So that
## Acceptance Criteria
- [ ] I can
- [ ] I can
- [ ] I can
## Tasks
- [ ] Explore and sketch
- [ ] Team and stakeholder review
- [ ] Visual Design
- [ ] Team review
- [ ] Etc.
## Sketch
## Additional details / background info
@@ -1,51 +0,0 @@
outputs:
token:
description: 'api-token of wallet'
value: ${{ steps.generate-api-token.outputs.api-token }}
runs:
using: 'composite'
steps:
- name: Vegacapsule version
shell: bash
run: vegacapsule version
- name: Vega wallet version
shell: bash
run: vega wallet software version
- name: Start nomad
shell: bash
run: vegacapsule nomad &
- name: Bootstrap network
shell: bash
run: vegacapsule network bootstrap --config-path=./frontend-monorepo/vegacapsule/config.hcl --force
- name: Initialize wallet
shell: bash
run: vega wallet init -f --home ~/.vegacapsule/testnet/wallet
- name: Import wallet
shell: bash
run: vega wallet import -w capsule_wallet --recovery-phrase-file ./frontend-monorepo/vegacapsule/recovery -p ./frontend-monorepo/vegacapsule/passphrase --home ~/.vegacapsule/testnet/wallet
- name: Generate second public key
shell: bash
run: vega wallet key generate -w capsule_wallet -p ./frontend-monorepo/vegacapsule/passphrase --home ~/.vegacapsule/testnet/wallet
- name: Import network
shell: bash
run: vega wallet network import --force --from-file ./frontend-monorepo/vegacapsule/wallet-config.toml --home ~/.vegacapsule/testnet/wallet
- name: Init api-token
shell: bash
run: vega wallet api-token init --home ~/.vegacapsule/testnet/wallet --passphrase-file ./frontend-monorepo/vegacapsule/passphrase
- name: Generate api-token
id: generate-api-token
shell: bash
run: echo api-token=$(vega wallet api-token generate --wallet-name capsule_wallet --tokens-passphrase-file ./frontend-monorepo/vegacapsule/passphrase --wallet-passphrase-file ./frontend-monorepo/vegacapsule/passphrase --home ~/.vegacapsule/testnet/wallet | grep -Eo '[a-zA-Z0-9]{64}') >> $GITHUB_OUTPUT
- name: Start service using capsule network
shell: bash
run: vega wallet service run -n DV --load-tokens --tokens-passphrase-file ./frontend-monorepo/vegacapsule/passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet &
+11 -13
View File
@@ -1,27 +1,25 @@
---
name: 'Add Issues To Project Board'
name: Auto Assign Issue to New Project
'on':
issues:
types:
- opened
env:
GH_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
ISSUE_ID: ${{ github.event.issue.node_id }}
USER: ${{ github.actor }}
types: [opened]
jobs:
add_issue:
runs-on: ubuntu-latest
steps:
- name: 'Add issue to project board'
- name: Add Issue to New Front End Project Board
env:
GITHUB_TOKEN: ${{ secrets.GH_NEW_CARD_TO_PROJECT }}
PROJECT_ID: ${{ secrets.FRONT_END_PROJECT_ID }}
ISSUE_ID: ${{ github.event.issue.node_id }}
run: |
gh api graphql -f query='
mutation($user:String!, $project:ID!, $issue:ID!) {
addProjectV2ItemById(input: {clientMutationId: $user, projectId: $project, contentId: $issue}) {
item {
mutation($project:ID!, $issue:ID!) {
addProjectNextItem(input: {projectId: $project, contentId: $issue}) {
projectNextItem {
id
}
}
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID -f user=$USER
}' -f project=$PROJECT_ID -f issue=$ISSUE_ID --jq '.data.addProjectNextItem.projectNextItem.id'
@@ -0,0 +1,165 @@
name: Capsule tests -- nightly
# This workflow runs the frontend tests against latest develop of the core to preempt breaking changes
on:
schedule:
- cron: '0 4 * * *'
jobs:
nightly:
name: Run capsule tests -- nightly
runs-on: ubuntu-latest
env:
GO111MODULE: 'on'
GOPROXY: ${{ secrets.GO_PROXY }}
steps:
#######
## Setup langs
#######
- name: Set up Go
uses: actions/setup-go@v3
id: go
with:
go-version: 1.18
- name: Set up Node 16
uses: actions/setup-node@v2
id: npm
with:
node-version: 16
#######
## Checkout all the things
#######
# Checkout repos required to build vega capsule
- name: Checkout vega
uses: actions/checkout@v2
with:
repository: vegaprotocol/vega
ref: master
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vega'
- name: Checkout data node
uses: actions/checkout@v2
with:
repository: vegaprotocol/data-node
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './data-node'
- name: Checkout protos
uses: actions/checkout@v2
with:
repository: vegaprotocol/protos
ref: master
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './protos'
- name: Checkout vegatools
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegatools
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './vegatools'
- name: Checkout vega wallet
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegawallet
ref: master
path: './vegawallet'
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegacapsule
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './capsule'
#######
## Build binaries
#######
- name: Build capsule
run: go install
working-directory: capsule
- name: Build vega
run: go install ./cmd/vega/
working-directory: vega
- name: Build vegawallet
run: go install
working-directory: vegawallet
- name: Build data-node
run: go install ./cmd/data-node/
working-directory: data-node
######
## Start capsule
######
- name: Login to docker
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
- name: Start nomad
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl
working-directory: capsule
######
## Setup a Vega wallet for our user
######
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
- name: Import wallet
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Create public key 2
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Start service
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
######
## Run some tests
######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Install root dependencies
run: yarn install
- name: Run Cypress tests
run: yarn nx run-many --target=e2e --all --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
CYPRESS_NIGHTLY_RUN: true
######
## Upload logs
######
- name: Logs
if: ${{ always() }}
run: vegacapsule network logs > vega-capsule-logs.txt
- uses: actions/upload-artifact@v2
with:
name: logs
path: ./vega-capsule-logs.txt
+130
View File
@@ -0,0 +1,130 @@
name: Capsule tests
on:
push:
branches:
- master
- develop
pull_request:
jobs:
pr:
name: Run capsule tests - PR
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
env:
GO111MODULE: 'on'
GOPROXY: ${{ secrets.GO_PROXY }}
steps:
#######
## Setup langs
#######
- name: Set up Go
uses: actions/setup-go@v3
id: go
with:
go-version: 1.18
- name: Set up Node 16
uses: actions/setup-node@v2
id: npm
with:
node-version: 16
#######
## Checkout capsule
#######
# Checkout capsule to build local network
- name: Checkout capsule
uses: actions/checkout@v2
with:
repository: vegaprotocol/vegacapsule
ref: main
token: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
path: './capsule'
#######
## Build binaries
#######
- name: Build capsule
run: go install
working-directory: capsule
- name: Set GOBIN
run: echo GOBIN=$(go env GOPATH)/bin >> $GITHUB_ENV
- name: Install binaries
run: vegacapsule install-bins
env:
GITHUB_TOKEN: ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }}
GOBIN: ${{ env.GOBIN }}
######
## Start capsule
######
- name: Login to docker
run: echo -n ${{ secrets.VEGA_CI_BOT_GITHUB_TOKEN }} | docker login https://ghcr.io -u vega-ci-bot --password-stdin
- name: Start nomad
run: vegacapsule nomad &
- name: Bootstrap network
run: vegacapsule network bootstrap --config-path=./net_confs/config.hcl --force
working-directory: capsule
######
## Setup a Vega wallet for our user
######
- name: Create passphrase
run: echo "${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}" > ./passphrase
- name: Create recovery
run: echo "${{ secrets.TRADING_TEST_VEGA_WALLET_RECOVERY }}" > ./recovery
- name: Initialize wallet
run: vegawallet init -f --home ~/.vegacapsule/testnet/wallet
- name: Import wallet
run: vegawallet import -w UI_Trading_Test --recovery-phrase-file ./recovery -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Create public key 2
run: vegawallet key generate -w UI_Trading_Test -p ./passphrase --home ~/.vegacapsule/testnet/wallet
- name: Start service
run: vegawallet service run --network DV --automatic-consent --home ~/.vegacapsule/testnet/wallet &
######
## Run some tests
######
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: master
- name: Install root dependencies
run: yarn install
- name: Run Cypress tests
run: npx nx affected:e2e --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
env:
CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE: ${{ secrets.CYPRESS_TRADING_TEST_VEGA_WALLET_PASSPHRASE }}
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPESS_ETH_WALLET_MNEMONIC }}
######
## Upload logs
######
- name: Logs
if: ${{ always() }}
run: vegacapsule network logs > vega-capsule-logs.txt
- uses: actions/upload-artifact@v2
if: ${{ always() }}
with:
name: logs
path: ./vega-capsule-logs.txt
-31
View File
@@ -1,31 +0,0 @@
name: Cypress Console tests -- live environment
# This workflow runs using provided url
on:
workflow_dispatch:
inputs:
url:
description: 'Url'
required: true
type: string
jobs:
cypress-run:
name: Run Cypress Trading tests -- live environment
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Run Cypress tests
uses: cypress-io/github-action@v4
with:
browser: chrome
record: true
project: ./apps/trading-e2e
config: baseUrl=${{ github.event.inputs.url }}
env: grepTags=@live
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -1,33 +0,0 @@
name: Cypress tests -- manual trigger
# This workflow runs the frontend tests against chosen branch
on:
workflow_dispatch:
inputs:
project:
description: 'Project'
required: true
type: choice
options:
- explorer-e2e
- token-e2e
- trading-e2e
tags:
description: 'Test tags to run'
required: true
type: string
default: '@smoke @regression @slow'
skip-nx-cache:
description: 'Skip NX cache'
required: false
type: boolean
default: false
jobs:
manual:
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
projects: '["${{inputs.project}}"]'
skip-cache: ${{inputs.skip-nx-cache}}
tags: ${{inputs.tags}}
-14
View File
@@ -1,14 +0,0 @@
name: Cypress tests -- night run
on:
schedule:
- cron: '0 4 * * *'
workflow_dispatch:
jobs:
nightly:
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
projects: '["explorer-e2e","token-e2e","trading-e2e"]'
tags: '@smoke @regression @slow'
-78
View File
@@ -1,78 +0,0 @@
name: Cypress tests - PR
on:
push:
branches:
- develop
- main
pull_request:
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
pr:
runs-on: ubuntu-latest
steps:
- name: Checkout frontend mono repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup node
uses: actions/setup-node@v3
- name: Remove package.json & yarn.lock to avoid installing everything
run: rm package.json yarn.lock
- name: Install nx
run: yarn add nx
# Check SHAs
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: ${{ github.base_ref || github.ref_name }}
set-environment-variables-for-job: true
# See affected apps
- name: See affected apps
run: |
affected=$(yarn nx print-affected --base=${{ env.NX_BASE }} --head=${{ env.NX_HEAD }} --select=projects)
echo -n "Affected projects: $affected"
projects=""
if [[ $affected == *"token"* ]]; then projects+='"token-e2e" '; fi
if [[ $affected == *"trading"* ]]; then projects+='"trading-e2e" '; fi
if [[ $affected == *"explorer"* ]]; then projects+='"explorer-e2e" '; fi
if [[ -z "$projects" ]]; then projects+='"token-e2e" "trading-e2e" "explorer-e2e" '; fi
projects=${projects%?}
projects=[${projects// /,}]
echo PROJECTS=$projects >> $GITHUB_ENV
outputs:
projects: ${{ env.PROJECTS }}
run:
needs: pr
if: ${{ needs.pr.outputs.projects != '[]' }}
uses: ./.github/workflows/cypress-run.yml
secrets: inherit
with:
projects: ${{ needs.pr.outputs.projects }}
tags: '@smoke @regression'
# Report single result at the end, to avoid mess with required checks in PR
result:
if: ${{ always() }}
needs: run
runs-on: ubuntu-latest
name: Cypress result
steps:
- run: |
result="${{ needs.run.result }}"
if [[ $result == "success" || $result == "skipped" ]]; then
exit 0
else
exit 1
fi
-91
View File
@@ -1,91 +0,0 @@
on:
workflow_call:
inputs:
projects:
required: true
type: string
skip-cache:
required: false
type: boolean
tags:
required: false
type: string
jobs:
e2e:
strategy:
fail-fast: false
matrix:
project: ${{ fromJSON(inputs.projects) }}
runs-on: self-hosted-runner
timeout-minutes: 30
steps:
# Checks if skip cache was requested
- name: Set skip-nx-cache flag
if: ${{ inputs.skip-cache == true }}
run: echo "SKIP_CACHE=--skip-nx-cache" >> $GITHUB_ENV
# Checkout front ends
- name: Checkout frontend mono repo
uses: actions/checkout@v3
with:
fetch-depth: 0
path: './frontend-monorepo'
# Restore node_modules from cache if possible
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: |
frontend-monorepo/node_modules
/home/runner/.cache/Cypress
key: node_modules_cypress-${{ hashFiles('frontend-monorepo/yarn.lock', 'frontend-monorepo/package.json') }}
# Install frontend dependencies
- name: Install root dependencies
run: yarn install --frozen-lockfile
working-directory: frontend-monorepo
# Make sure that all Cypress binaries are installed properly
- name: Install cypress bins
run: yarn cypress install
working-directory: frontend-monorepo
######
## Setup Vegacapsule and Vega wallet
######
- name: Run Vegacapsule network and Vega wallet
id: setup-vega
uses: ./frontend-monorepo/.github/actions/run-vegacapsule
######
## Run some tests
######
- name: Run Cypress tests
run: yarn nx run ${{ matrix.project }}:e2e ${{ env.SKIP_CACHE }} --record --key ${{ secrets.CYPRESS_RECORD_KEY }} --browser chrome
working-directory: frontend-monorepo
env:
CYPRESS_SLACK_WEBHOOK: ${{ secrets.CYPRESS_SLACK_WEBHOOK }}
CYPRESS_ETH_WALLET_MNEMONIC: ${{ secrets.CYPRESS_ETH_WALLET_MNEMONIC }}
CYPRESS_VEGA_WALLET_API_TOKEN: ${{ steps.setup-vega.outputs.token }}
CYPRESS_grepTags: ${{ inputs.tags }}
######
## Upload logs
######
# Artifact path is not valid: /ganache-1/capsule-logscolletor.stderr-2022-12-22T10:20:30Z.log. Contains the following character: Colon :
- name: Rename files to allow archive
if: ${{ always() }}
run: |
while read -r file; do
mv "${file}" "$(echo ${file} | sed 's|:|-|g')"
done< <(find /home/runner/.vegacapsule/testnet/logs -type f)
- uses: actions/upload-artifact@v3
if: ${{ always() }}
with:
name: logs-${{ matrix.project }}
path: /home/runner/.vegacapsule/testnet/logs
+1 -1
View File
@@ -3,7 +3,7 @@ name: Generate queries
on:
push:
branches:
- develop
- master
jobs:
master:
+32
View File
@@ -0,0 +1,32 @@
---
name: Deploy affected projects to IPFS
'on':
# Triggers the workflow on push to main branch
push:
branches: [master]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch: null
jobs:
master:
name: Publish NX Affected
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.14.0
- name: Install root dependencies
run: yarn install
- name: Run deploy for affected sites
env:
FLEEK_API_KEY: ${{ secrets.FLEEK_API_KEY }}
run: ./tools/ipfs-deploy.js
-21
View File
@@ -1,21 +0,0 @@
---
name: Verify PR title
on:
pull_request:
types: [opened, ready_for_review, reopened, edited, synchronize]
jobs:
lint_pr:
runs-on: ubuntu-latest
steps:
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.14.0
- name: Install commitlint cli and config
run: npm install @commitlint/cli @commitlint/config-conventional
- name: Create config
run: echo "module.exports = {extends:['@commitlint/config-conventional']};" > commitlint.config.js
- name: Check PR title
run: echo "${{ github.event.pull_request.title }}" | npx commitlint
@@ -1,126 +0,0 @@
name: Publish docker containers
'on':
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
- 'v[0-9]+.[0-9]+.[0-9]+-*'
workflow_dispatch:
inputs:
publish:
description: 'Publish tag to Docker Hub & GitHub Registry'
required: false
type: boolean
default: false
tag:
description: 'Git Tag to build and publish'
required: false
type: string
default: ''
apps:
description: 'Applications to build and publish'
required: false
type: choice
options:
- '["explorer", "token", "trading"]'
- '["explorer"]'
- '["token"]'
- '["trading"]'
archs:
description: 'Architecture to build and publish'
required: false
type: choice
options:
- linux/amd64, linux/arm64
- linux/amd64
- linux/arm64
jobs:
master:
strategy:
fail-fast: false
matrix:
app: ${{ fromJson(inputs.apps || '["explorer", "token", "trading"]') }}
name: Build the ${{ matrix.app }} image
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
with:
ref: ${{ inputs.tag }}
- name: Set up QEMU
id: quemu
uses: docker/setup-qemu-action@v2
- name: Available platforms
run: echo ${{ steps.qemu.outputs.platforms }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
if: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Determine Docker Image tag
id: tags
run: |
hash=$(git rev-parse HEAD|cut -b1-8)
versionTag=${{ inputs.tag || startsWith(github.ref, 'refs/tags/') && github.ref_name || '${hash}' }}
echo ::set-output name=version::${versionTag}
echo ::set-output name=npmVersion::$(cat dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }} | grep FROM | head -n 1 | awk '{print $2}' | cut -d ':' -f 2 | cut -d '-' -f 1 )
- name: Print config
run: |
git rev-parse --verify HEAD
git status
echo "inputs.tag=${{ inputs.tag }}"
echo "inputs.publish=${{ inputs.publish }}"
echo "inputs.apps=${{ inputs.apps }}"
echo "inputs.archs=${{ inputs.archs }}"
echo "steps.tags.outputs.version=${{ steps.tags.outputs.version }}"
- uses: actions/setup-node@v3
with:
node-version: ${{ steps.tags.outputs.npmVersion }}
- name: Build frontend dists
run: |
yarn --verbose --pure-lockfile
yarn nx ${{ matrix.app =='trading' && 'export' || 'build' }} ${{ matrix.app }} --pure-lockfile
- name: Build and export to local Docker
uses: docker/build-push-action@v3
with:
context: .
push: false
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
load: true
tags: vegaprotocol/${{ matrix.app }}:local
- name: Sanity check docker image
run: |
docker run --rm vegaprotocol/${{ matrix.app }}:local cat .env
docker run --rm vegaprotocol/${{ matrix.app }}:local ls -lah
- name: Build and push to DockerHub
id: docker_build
uses: docker/build-push-action@v3
with:
context: .
push: ${{ inputs.publish || startsWith(github.ref, 'refs/tags/') }}
file: dockerfiles/${{ matrix.app =='trading' && 'Dockerfile.next' || 'Dockerfile.cra' }}.dist
build-args: APP=${{ matrix.app }}
platforms: ${{ inputs.archs || 'linux/amd64, linux/arm64' }}
tags: |
vegaprotocol/${{ matrix.app }}:latest
vegaprotocol/${{ matrix.app }}:${{ steps.tags.outputs.version }}
- name: Image digest
run: echo ${{ steps.docker_build.outputs.digest }}
+38 -17
View File
@@ -3,37 +3,60 @@ name: Unit tests & build
on:
push:
branches:
- master
- develop
- main
pull_request:
jobs:
pr:
name: Test and lint - PR
master:
name: Test and lint - main
runs-on: ubuntu-latest
permissions:
contents: 'read'
actions: 'read'
if: ${{ github.event_name != 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.ref }}
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: ${{ github.base_ref }}
main-branch-name: master
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v3
uses: actions/setup-node@v2
with:
node-version: 16.14.0
- name: Restore node_modules from cache
uses: actions/cache@v3
with:
path: '**/node_modules'
key: node_modules-${{ hashFiles('**/yarn.lock') }}
- name: Install root dependencies
run: yarn install --frozen-lockfile
run: yarn install
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
run: yarn nx affected:lint --max-warnings=0
- name: Test affected
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build
pr:
name: Test and lint - PR
runs-on: ubuntu-latest
if: ${{ github.event_name == 'pull_request' }}
steps:
- name: Checkout
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Derive appropriate SHAs for base and head for `nx affected` commands
uses: nrwl/nx-set-shas@v2
with:
main-branch-name: master
- name: Use Node.js 16
id: Node
uses: actions/setup-node@v2
with:
node-version: 16.14.0
- name: Install root dependencies
run: yarn install
- name: Check formatting
run: yarn nx format:check
- name: Lint affected
@@ -42,5 +65,3 @@ jobs:
run: yarn nx affected:test
- name: Build affected
run: yarn nx affected:build
- name: Build affected spec
run: yarn nx affected --target=build-spec
+1 -4
View File
@@ -21,7 +21,7 @@
# IDE - VSCode
.vscode/*
.vscode/settings.json
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
@@ -43,6 +43,3 @@ Thumbs.db
.local.env
.env.local
cypress.env.json
# Next.js
.next
+4
View File
@@ -6,3 +6,7 @@ yarn commitlint --edit "${1}"
# Lint all staged files
yarn lint-staged
# Build & test affected apps
yarn nx affected:build --uncommitted
yarn nx affected:test --uncommitted
+2 -2
View File
@@ -3,9 +3,9 @@
/dist
/coverage
__generated__
__generated___
apps/static/src/assets/devnet-tranches.json
apps/static/src/assets/mainnet-tranches.json
apps/static/src/assets/stagnet3-tranches.json
apps/static/src/assets/stagnet1-tranches.json
apps/static/src/assets/stagnet2-tranches.json
apps/static/src/assets/testnet-tranches.json
+1 -19
View File
@@ -1,24 +1,6 @@
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',
],
addons: ['@storybook/addon-essentials', '@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
+1 -2
View File
@@ -4,7 +4,6 @@
"esbenp.prettier-vscode",
"firsttris.vscode-jest-runner",
"dbaeumer.vscode-eslint",
"stevejpurves.cucumber",
"streetsidesoftware.code-spell-checker"
"stevejpurves.cucumber"
]
}
-1
View File
@@ -1,2 +1 @@
* @vegaprotocol/frontend
*.graphql @vegaprotocol/core
+2 -3
View File
@@ -7,9 +7,8 @@ ENV PATH /app/node_modules/.bin:$PATH
COPY package.json ./
COPY yarn.lock ./
COPY . ./
RUN apk add python3 make gcc g++
RUN yarn --network-timeout 100000 --verbose --pure-lockfile
RUN yarn nx build $APP --network-timeout 100000 --verbose --pure-lockfile
RUN yarn
RUN yarn nx build $APP
# Production environment
FROM nginx:stable-alpine
Vendored
-20
View File
@@ -1,20 +0,0 @@
@Library('vega-shared-library') _
def commitHash = 'UNKNOWN'
pipeline {
agent any
options {
skipDefaultCheckout true
parallelsAlwaysFailFast()
}
stages {
stage('approbation') {
steps {
sh 'printenv'
checkout scm
runApprobation ignoreFailure: false, frontendBranch: env.BRANCH_NAME, type: 'frontend'
}
}
}
}
+15 -45
View File
@@ -22,14 +22,14 @@ The utility dApp for interacting with the Vega token and using its' utility. Thi
The block explorer for the Vega network, showing details of raw chain states and the state of markets on the Vega network.
### [Stats](./apps/stats)
An application for the status of the Vega network. Showing block height and other network activity.
### [Static](./apps/static)
Hosting for static content being shared across apps, for example fonts.
### [Multisig-signer](./apps/multisig-signer)
The utility dApp for validators wishing to add or remove themselves as a signer of the multisig contract.
# 🧱 Libraries in this repo
### [UI toolkit](./libs/ui-toolkit)
@@ -69,10 +69,11 @@ Run `nx serve my-app` for a dev server. Navigate to the port specified in `app/<
### Using Apollo GraphQL and Generate Types
In order to generate the schemas for your GraphQL queries, you can run `GRAPHQL_SCHEMA_PATH=[YOUR SCHEMA FILE / API URL HERE] nx run types:generate`.
In order to generate the schemas for your GraphQL queries, you can run `nx run types:generate`.
If it is the first time you are running the command, make sure you are setting up the environment variable from `apollo.config.js`.
```bash
export GRAPHQL_SCHEMA_PATH=https://api.n11.testnet.vega.xyz/graphql
export NX_VEGA_URL=https://lb.testnet.vega.xyz/query
yarn nx run types:generate
```
@@ -82,14 +83,13 @@ Run `yarn nx run <my-app>-e2e:e2e` to execute the e2e tests with [cypress](https
Run `nx test my-app` to execute the unit tests with [Jest](https://jestjs.io), or `nx affected:test` to execute just unit tests affected by a change. You can also use `--watch` with these test to run jest in watch mode, see [Jest executor](https://nx.dev/packages/jest/executors/jest) for all CLI flags.
### Using wallet
#### Trading app E2E tests
To run tests locally using your own wallets make sure you have generated at least two public keys and update the following environment variables in `cypress.config.js` to match your wallet.
To run tests locally using your own wallets you can add the following environment variables to `cypress.json`
1. Set `VEGA_PUBLIC_KEY` and `TRUNCATED_VEGA_PUBLIC_KEY` to your first public key.
2. Set `VEGA_PUBLIC_KEY2` and `TRUNCATED_VEGA_PUBLIC_KEY2` to your second public key.
3. Set `TRADING_TEST_VEGA_WALLET_PASSPHRASE` as your wallet passphrase
4. Add `ETH_WALLET_MNEMONIC` as your Ethereum wallet mnemonic
1. Change `TRADING_TEST_VEGA_WALLET_NAME` to your Vega wallet name
2. Add `TRADING_TEST_VEGA_WALLET_PASSPHRASE` as your wallet passphrase
3. Add `ETH_WALLET_MNEMONIC` as your Ethereum wallet mnemonic
### Formatting
@@ -107,12 +107,12 @@ Visit the [Nx Documentation](https://nx.dev/getting-started/intro) to learn more
## Docker
The [Dockerfile](./dockerfiles) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
The [Dockerfile](./Dockerfile) for running the frontends is pretty basic, merely building the application with the APP arg that is passed in and serving the application from [nginx](./nginx/nginx.conf). The only complexity that exists is that there is a script which allows the passing of run time environment variables to the containers. See configuration below for how to do this.
You can build any of the containers locally with the following command:
```bash
docker build --dockerfile dockerfiles/Dockerfile.cra . --build-arg APP=[YOUR APP] --tag=[TAG]
docker build . --build-arg APP=[YOUR APP] --tag=[TAG]
```
In order to run a container:
@@ -121,8 +121,6 @@ In order to run a container:
docker run -p 3000:80 [TAG]
```
Images ending with `.dist` are to pack locally created transpiled HTML files into nginx container for non-compatible with yarn architectures like M1 Mac
## Config
As environment variables are build time and not run time in frontend applications. We have built a system which allows for passing run time environment variables, this generates a JSON file that will override the default environment variables that the container was built with (which is always testnet, using the default .env files).
@@ -130,7 +128,7 @@ As environment variables are build time and not run time in frontend application
In order to override specific environment variables you can pass these to the container like this:
```bash
docker run -e NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql -p 3000:80 [TAG]
docker run -e NX_VEGA_URL=https://n04.d.vega.xyz/query -p 3000:80 [TAG]
```
Which will now point the app to use a devnet data node. To see a list of all possible config properties see the readme.md for each app in the app directory.
@@ -141,34 +139,6 @@ Coming soon! You will be able to run the containers within Vega Capsule.
You can run against a local instance of Vega Capsule today by using the .env.capsule present in the apps.
If you wish to run E2E tests for Token and Block Explorer (other areas to be added soon)
- Vegacapsule must be used in order for these tests to succeed, the vegacapsule repo README.md file contains the steps required to set this up, it must be installed globally.
- However we start the capsule network a little differently to how it is laid out in those instructions:
In order to run the bootstrap command to generate and start a new network, we must do so using the following:
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl
```
In order to setup and run vegawallet for e2e capsule tests, in a separate terminal window:
1. cd into `./vegacapsule`
2. run:
```bash
bash setup-vegawallet.sh
```
3. copy generated `api-token` and paste the token into `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable in either `apps/token-e2e/.env` or `apps/explorer-e2e/.env` depending on which project needs testing.
Note: The script is only needed if capsule was built for first time or fresh. To run existing wallet service for capsule:
```bash
vega wallet service run -n DV --load-tokens --tokens-passphrase-file passphrase --no-version-check --automatic-consent --home ~/.vegacapsule/testnet/wallet
```
# 📑 License
[MIT](./LICENSE)
@@ -0,0 +1,3 @@
{
"stepDefinitions": "src/support/step_definitions"
}
+1 -5
View File
@@ -3,7 +3,7 @@ NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_ENV=CUSTOM
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/capsule-network.json
NX_VEGA_REST=http://localhost:3029
CYPRESS_VEGA_TENDERMINT_URL=http://localhost:26617
@@ -11,11 +11,7 @@ NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
CYPRESS_VEGA_WALLET_API_TOKEN=
CYPRESS_VEGA_URL=http://localhost:3028/query
+3 -4
View File
@@ -2,17 +2,16 @@
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n04.d.vega.xyz/graphql
NX_VEGA_URL=https://n04.d.vega.xyz/query
NX_VEGA_ENV=DEVNET
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_VEGA_REST=https://n04.d.vega.xyz/datanode/rest
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
+2 -2
View File
@@ -2,9 +2,9 @@
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
NX_VEGA_REST=https://api.token.vega.xyz/
# App flags
NX_EXPLORER_ASSETS=1
+17
View File
@@ -0,0 +1,17 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://n03.s.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n03.s.vega.xyz/tm/websocket
NX_VEGA_URL=https://n03.s.vega.xyz/query
NX_VEGA_ENV=STAGNET
NX_VEGA_REST=https://n03.s.vega.xyz/datanode/rest
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
@@ -1,18 +1,17 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
NX_VEGA_URL=https://api.stagnet3.vega.xyz/graphql
NX_VEGA_ENV=STAGNET3
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
NX_TENDERMINT_URL=https://n03.stagnet2.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n03.stagnet2.vega.xyz/tm/websocket
NX_VEGA_URL=https://n03.stagnet2.vega.xyz/query
NX_VEGA_ENV=STAGNET2
NX_VEGA_REST=https://n01.stagnet2.vega.xyz/datanode/rest
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
+4 -5
View File
@@ -1,18 +1,17 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://tm.n07.testnet.vega.xyz/tm
NX_TENDERMINT_URL=https://lb.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://api.n11.testnet.vega.xyz/graphql
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_VEGA_ENV=TESTNET
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
NX_VEGA_REST=https://lb.testnet.vega.xyz/datanode/rest
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
-39
View File
@@ -1,39 +0,0 @@
# Vega Explorer E2E tests
To run the UI automation tests with Vega Capsule, run:
```bash
yarn nx run explorer-e2e:e2e
```
To open Cypress and run in interactive mode, run:
```bash
yarn nx run explorer-e2e:e2e --watch
```
## Vega Capsule Setup
The e2e tests run against a locally running instance of the Vega network, managed and controlled by [Vega Capsule](https://github.com/vegaprotocol/vegacapsule). Vega Capsule will:
- Bootstrap and start up a Vega network
- Start up [Ganache](https://trufflesuite.com/ganache/) for a local Ethereum network
- Install the required Vega smart contracts
- Set up DataNodes with a running GraphQL and REST APIs.
1. Refer to the [Vega Capsule readme](https://github.com/vegaprotocol/vegacapsule#readme) for setting up and running Capsule - follow by Pre-start and Quick Start (points 1-2)
2. Bootstrap with auto-installed dependencies including wallet
```bash
vegacapsule network bootstrap --config-path=../frontend-monorepo/vegacapsule/config.hcl --force
```
### Troubleshooting
- You may need to run `vegacapsule nodes unsafe-reset-all` to get a clean network state
## Vega Wallet Setup
You can then refer to (or run) `frontend-monorepo/vegacapsule/setup-vegawallet.sh`. This will initialise and configure your wallet to have the correct public keys and network config to run against capsule.
Go to the .env file in `apps/explorer-e2e` and set the `CYPRESS_VEGA_WALLET_API_TOKEN` environment variable by pasting in your wallets long lived api token
+8 -21
View File
@@ -1,39 +1,26 @@
const { defineConfig } = require('cypress');
const setupNodeEvents = require('./src/plugins/index.js');
module.exports = defineConfig({
projectId: 'et4snf',
e2e: {
setupNodeEvents(on, config) {
require('@cypress/grep/src/plugin')(config);
return config;
},
setupNodeEvents,
baseUrl: 'http://localhost:3000',
fileServerFolder: '.',
fixturesFolder: './src/fixtures',
specPattern: '**/*.cy.{js,jsx,ts,tsx}',
fixturesFolder: false,
specPattern: '**/*.feature',
excludeSpecPattern: '**/*.js',
modifyObstructiveCode: false,
supportFile: './src/support/index.js',
video: false,
videoUploadOnPasses: false,
supportFile: './src/support/index.ts',
video: true,
videosFolder: '../../dist/cypress/apps/explorer-e2e/videos',
screenshotsFolder: '../../dist/cypress/apps/explorer-e2e/screenshots',
chromeWebSecurity: false,
viewportWidth: 1440,
viewportHeight: 900,
},
env: {
environment: 'CUSTOM',
networkQueryUrl: 'http://localhost:3028/query',
ethUrl: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8',
commitHash: 'dev',
tsConfig: 'tsconfig.json',
grepTags: '@regression @smoke @slow',
grepFilterSpecs: true,
grepOmitFiltered: true,
vegaWalletName: 'capsule_wallet',
vegaWalletLocation: '~/.vegacapsule/testnet/wallet',
vegaWalletPublicKey:
'02eceaba4df2bef76ea10caf728d8a099a2aa846cced25737cccaa9812342f65',
TAGS: 'not @todo and not @ignore and not @manual',
},
});
+1 -8
View File
@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"root": "apps/explorer-e2e",
"sourceRoot": "apps/explorer-e2e/src",
"projectType": "application",
"targets": {
@@ -21,13 +21,6 @@
"options": {
"lintFilePatterns": ["apps/explorer-e2e/**/*.{js,ts}"]
}
},
"build": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/explorer-e2e/"
}
}
},
"tags": [],
@@ -0,0 +1,4 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io"
}
@@ -1,121 +0,0 @@
{
"eighteenDecimal": [
"governance.proposal.asset.minProposerBalance",
"governance.proposal.asset.minVoterBalance",
"governance.proposal.freeform.minProposerBalance",
"governance.proposal.freeform.minVoterBalance",
"governance.proposal.market.minProposerBalance",
"governance.proposal.market.minVoterBalance",
"governance.proposal.updateMarket.minProposerBalance",
"governance.proposal.updateMarket.minVoterBalance",
"governance.proposal.updateNetParam.minProposerBalance",
"governance.proposal.updateNetParam.minVoterBalance",
"reward.staking.delegation.maxPayoutPerEpoch",
"reward.staking.delegation.maxPayoutPerParticipant",
"reward.staking.delegation.minimumValidatorStake",
"spam.protection.delegation.min.tokens",
"spam.protection.proposal.min.tokens",
"spam.protection.voting.min.tokens",
"validators.delegation.minAmount"
],
"fiveDecimal": [
"governance.proposal.updateAsset.minProposerBalance",
"governance.proposal.updateAsset.minVoterBalance",
"governance.proposal.updateAsset.requiredParticipation",
"governance.proposal.updateMarket.minProposerEquityLikeShare",
"market.fee.factors.infrastructureFee",
"market.fee.factors.makerFee",
"market.liquidity.bondPenaltyParameter",
"market.liquidity.maximumLiquidityFeeFactorLevel",
"market.liquidity.minimum.probabilityOfTrading.lpOrders",
"market.liquidity.probabilityOfTrading.tau.scaling",
"market.liquidity.stakeToCcyVolume",
"market.liquidity.targetstake.triggering.ratio",
"market.liquidityProvision.minLpStakeQuantumMultiple",
"market.liquidityProvision.shapes.maxSize",
"market.stake.target.scalingFactor",
"network.validators.ersatz.multipleOfTendermintValidators",
"network.validators.ersatz.rewardFactor",
"network.validators.incumbentBonus",
"network.validators.minimumEthereumEventsForNewValidator",
"network.validators.multisig.numberOfSigners",
"network.validators.tendermint.number",
"reward.staking.delegation.competitionLevel",
"reward.staking.delegation.delegatorShare",
"reward.staking.delegation.minValidators",
"reward.staking.delegation.optimalStakeMultiplier",
"reward.staking.delegation.payoutFraction",
"rewards.marketCreationQuantumMultiple",
"spam.pow.difficulty",
"spam.pow.increaseDifficulty",
"spam.pow.numberOfPastBlocks",
"spam.pow.numberOfTxPerBlock",
"spam.protection.max.batchSize",
"spam.protection.max.delegations",
"spam.protection.max.proposals",
"spam.protection.max.votes",
"spam.protection.maxUserTransfersPerEpoch",
"transfer.fee.factor",
"transfer.minTransferQuantumMultiple",
"snapshot.interval.length"
],
"json": [
"blockchains.ethereumConfig",
"market.margin.scalingFactors",
"market.monitor.price.defaultParameters"
],
"percentage": [
"governance.proposal.asset.requiredMajority",
"governance.proposal.asset.requiredParticipation",
"governance.proposal.freeform.requiredMajority",
"governance.proposal.freeform.requiredParticipation",
"governance.proposal.market.requiredMajority",
"governance.proposal.market.requiredParticipation",
"governance.proposal.updateMarket.requiredMajority",
"governance.proposal.updateMarket.requiredMajorityLP",
"governance.proposal.updateMarket.requiredParticipation",
"governance.proposal.updateMarket.requiredParticipationLP",
"governance.proposal.updateNetParam.requiredMajority",
"governance.proposal.updateNetParam.requiredParticipation",
"validators.vote.required"
],
"duration": [
"governance.proposal.asset.maxClose",
"governance.proposal.asset.maxEnact",
"governance.proposal.asset.minClose",
"governance.proposal.asset.minEnact",
"governance.proposal.freeform.maxClose",
"governance.proposal.freeform.minClose",
"governance.proposal.market.maxClose",
"governance.proposal.market.maxEnact",
"governance.proposal.market.minClose",
"governance.proposal.market.minEnact",
"governance.proposal.updateAsset.maxClose",
"governance.proposal.updateAsset.maxEnact",
"governance.proposal.updateAsset.minClose",
"governance.proposal.updateAsset.minEnact",
"governance.proposal.updateMarket.maxClose",
"governance.proposal.updateMarket.maxEnact",
"governance.proposal.updateMarket.minClose",
"governance.proposal.updateMarket.minEnact",
"governance.proposal.updateNetParam.maxClose",
"governance.proposal.updateNetParam.maxEnact",
"governance.proposal.updateNetParam.minClose",
"governance.proposal.updateNetParam.minEnact",
"market.auction.maximumDuration",
"market.auction.minimumDuration",
"market.liquidity.providers.fee.distributionTimeStep",
"market.stake.target.timeWindow",
"market.value.windowLength",
"network.checkpoint.timeElapsedBetweenCheckpoints",
"network.floatingPointUpdates.delay",
"reward.staking.delegation.payoutDelay",
"spam.pow.hashFunction",
"validators.epoch.length"
],
"date": [
"limits.assets.proposeEnabledFrom",
"limits.markets.proposeEnabledFrom"
],
"id": ["reward.asset"]
}
@@ -1 +0,0 @@
123
@@ -1 +0,0 @@
ozone access unlock valid olympic save include omit supply green clown session
@@ -0,0 +1,11 @@
Feature: Asset Page
Scenario: Navigate to Asset Page
Given I am on the homepage
When I navigate to the asset page
Then asset page is correctly displayed
Scenario: Navigate to Asset Page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the asset page
Then asset page is correctly displayed
@@ -1,151 +0,0 @@
context('Asset page', { tags: '@regression' }, function () {
before('gather system asset information', function () {
cy.get_asset_information().as('assetsInfo');
});
describe('Verify elements on page', function () {
const assetsNavigation = 'a[href="/assets"]';
const assetHeader = '[data-testid="asset-header"]';
const jsonSection = '.language-json';
before('Navigate to assets page', function () {
cy.visit('/');
cy.get(assetsNavigation).click();
// Check we have enough enough assets
const assetNames = Object.keys(this.assetsInfo);
assert.isAtLeast(
assetNames.length,
5,
'Ensuring we have at least 5 assets to test'
);
});
it('should be able to see assets page sections', function () {
const assetNames = Object.keys(this.assetsInfo);
assetNames.forEach((assetName) => {
cy.get(assetHeader)
.contains(assetName)
.should('be.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
});
});
it('should be able to see all asset details displayed in JSON', function () {
const assetNames = Object.keys(this.assetsInfo);
assetNames.forEach((assetName) => {
cy.get(assetHeader)
.contains(assetName)
.next()
.within(() => {
cy.get(jsonSection)
.invoke('text')
.convert_string_json_to_js_object()
.then((assetsListedInJson) => {
const assetInfo = this.assetsInfo[assetName];
assert.equal(assetsListedInJson.name, assetInfo.node.name);
assert.equal(assetsListedInJson.id, assetInfo.node.id);
assert.equal(
assetsListedInJson.decimals,
assetInfo.node.decimals
);
assert.equal(assetsListedInJson.symbol, assetInfo.node.symbol);
assert.equal(
assetsListedInJson.source.__typename,
assetInfo.node.source.__typename
);
if (assetInfo.node.source.__typename == 'ERC20') {
assert.equal(
assetsListedInJson.source.contractAddress,
assetInfo.node.source.contractAddress
);
}
if (assetInfo.node.source.__typename == 'BuiltinAsset') {
assert.equal(
assetsListedInJson.source.maxFaucetAmountMint,
assetInfo.node.source.maxFaucetAmountMint
);
}
let knownAssetTypes = ['BuiltinAsset', 'ERC20'];
assert.include(
knownAssetTypes,
assetInfo.node.source.__typename,
`Checking that current asset type of ${assetInfo.node.source.__typename} /
is one of: ${knownAssetTypes}: /
If fail then we need to add extra tests for un-encountered asset types`
);
});
});
});
});
it('should be able to switch assets between light and dark mode', function () {
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
const themeSwitcher = '[data-testid="theme-switcher"]';
const jsonFields = '.hljs';
const sideMenuBackground = '.absolute';
// Engage dark mode if not allready set
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.then((background_color) => {
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
cy.get(themeSwitcher).click();
});
// Engage white mode
cy.get(themeSwitcher).click();
// White Mode
cy.get(assetsNavigation)
.should('have.css', 'background-color')
.and('include', whiteThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', whiteThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', whiteThemeSideMenuBackgroundColor);
// Dark Mode
cy.get(themeSwitcher).click();
cy.get(assetsNavigation)
.should('have.css', 'background-color')
.and('include', darkThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', darkThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', darkThemeSideMenuBackgroundColor);
});
it('should be able to see assets page displayed in mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(assetsNavigation).click();
const assetNames = Object.keys(this.assetsInfo);
assetNames.forEach((assetName) => {
cy.get(assetHeader)
.contains(assetName)
.should('be.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
});
});
});
});
@@ -0,0 +1,36 @@
Feature: Blocks Page
Scenario: Navigate to blocks page
Given I am on the homepage
When I navigate to the blocks page
Then blocks page is correctly displayed
Scenario: Navigate to blocks page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the blocks page
Then blocks page is correctly displayed
Scenario: Navigate to block validator page
Given I am on the homepage
When I navigate to the blocks page
And I click on top block
Then validator block page is correctly displayed
Scenario: Navigate to previous block
Given I am on the homepage
When I navigate to the blocks page
And I click on top block
Then I am on the previous block when I click previous
Scenario: Previous button disabled on first block
Given I am on the homepage
When I navigate to the blocks page
And jump to first block
Then previous button is disabled
And I am on the second block when I click next
Scenario: Infinite scroll shows at least 300 new blocks
Given I am on the homepage
When I navigate to the blocks page
And I scroll down to the last block on the page
Then I can expect to see at least 100 blocks if i scroll 7 times
@@ -1,131 +0,0 @@
context('Blocks page', { tags: '@regression' }, function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('Verify elements on page', function () {
const blockNavigation = 'a[href="/blocks"]';
const blockHeight = '[data-testid="block-height"]';
const blockTime = '[data-testid="block-time"]';
const blockHeader = '[data-testid="block-header"]';
const previousBlockBtn = '[data-testid="previous-block"]';
const infiniteScrollWrapper = '[data-testid="infinite-scroll-wrapper"]';
beforeEach('navigate to blocks page', function () {
cy.get(blockNavigation).click();
});
it('Blocks page is displayed', function () {
validateBlocksDisplayed();
});
it('Blocks page is displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(blockNavigation).click();
validateBlocksDisplayed();
});
it('Block validator page is displayed', function () {
waitForBlocksResponse();
cy.get(blockHeight).eq(0).click();
cy.get('[data-testid="block-validator"]').should('not.be.empty');
cy.get(blockTime).should('not.be.empty');
//TODO: Add assertion for transactions when txs are added
});
it('Navigate to previous block', function () {
waitForBlocksResponse();
cy.get(blockHeight).eq(0).click();
cy.get(blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
cy.get(previousBlockBtn).click();
cy.get(blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.lessThan(blockHeight);
});
});
});
// Skipping - see https://github.com/vegaprotocol/frontend-monorepo/issues/2494
it.skip('Previous button disabled on first block', function () {
cy.get('[data-testid="block-input"]').type('1');
cy.get('[data-testid="go-submit"]').click();
cy.get(previousBlockBtn).find('button').should('be.disabled');
cy.get(blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
cy.get('[data-testid="next-block"]').click();
cy.get(blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.greaterThan(blockHeight);
});
});
});
// Skipping these tests for time being - since blockchain in capsule is now too small to show historical data - re-enable once addressed
it.skip('Infinite scroll shows at least 100 new blocks', function () {
const expectedBlocks = 100;
const scrollAttempts = 7;
waitForBlocksResponse();
cy.get(infiniteScrollWrapper).children().scrollTo('bottom');
cy.intercept('*blockchain?maxHeight*').as('blockchain_load');
cy.get(blockHeight)
.last()
.invoke('text')
.then(($initialLastBlockHeight) => {
for (let index = 0; index < scrollAttempts; index++) {
cy.get(infiniteScrollWrapper)
.children()
.children()
.invoke('css', 'height')
.then((scrollTarget) => {
cy.get(infiniteScrollWrapper)
.children()
.scrollTo(0, scrollTarget.toString(), { easing: 'linear' })
.wait('@blockchain_load');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(50); // Need this as although network response has arrived it takes a few millisecs for the css height to expand
});
}
cy.get(blockHeight)
.last()
.invoke('text')
.then(($lastBlockHeight) => {
const totalBlocksLoadedSinceScrollBegan =
parseInt($initialLastBlockHeight) - parseInt($lastBlockHeight);
expect(totalBlocksLoadedSinceScrollBegan).to.be.at.least(
expectedBlocks
);
});
});
});
function waitForBlocksResponse() {
cy.get('[data-testid="loader"]').should('not.exist', { timeout: 18000 });
}
function validateBlocksDisplayed() {
waitForBlocksResponse();
cy.get('[data-testid="block-row"]').should('have.length.above', 1);
cy.get(blockHeight).first().should('not.be.empty');
cy.get('[data-testid="num-txs"]').first().should('not.be.empty');
cy.get('[data-testid="validator-link"]').first().should('not.be.empty');
cy.get(blockTime).first().should('not.be.empty');
}
});
});
@@ -0,0 +1,27 @@
Feature: Home page
Scenario: Stats page displayed correctly
Given I am on the homepage
Then the stats for deployed environment are correctly displayed
Scenario Outline: Succesfful search for specific id by <IdType>
Given I am on the homepage
When I search for '<Id>'
Then I am redirected to page containing id '<Id>'
Examples:
| IdType | Id |
| Block Id | 973624 |
| Tx Hash | 9ED3718AA8308E7E08EC588EE7AADAF49711D2138860D8914B4D81A2054D9FB8 |
| Tx Id | 0x61DCCEBB955087F50D0B85382DAE138EDA9631BF1A4F92E563D528904AA38898 |
Scenario Outline: Error message displayed when invalid search by <invalidType>
Given I am on the homepage
When I search for '<Id>'
Then search error message "<errorMessage>" is displayed
Examples:
| invalidType | Id | errorMessage |
| wrong string length | 9ED3718AA8308E7E08EC588EE7AADAF497D2138860D8914B4D81A2054D9FB8 | Something doesn't look right |
| invalid hash | 9ED3718AA8308E7E08ECht8EE753DAF49711D2138860D8914B4D81A2054D9FB8 | Transaction is not hexadecimal |
| empty search | | Search required |
@@ -1,160 +0,0 @@
context('Home Page', function () {
before('visit home page', function () {
cy.visit('/');
});
describe('Stats page', { tags: '@smoke' }, function () {
const statsValue = '[data-testid="stats-value"]';
it('Should show connected environment', function () {
const deployedEnv = Cypress.env('environment').toUpperCase();
cy.get('[data-testid="stats-environment"]').should(
'have.text',
`/ ${deployedEnv}`
);
});
it('should show connected environment stats', function () {
const statTitles = {
0: 'Status',
1: 'Height',
2: 'Uptime',
3: 'Total nodes',
4: 'Total staked',
5: 'Backlog',
6: 'Trades / second',
7: 'Orders / block',
8: 'Orders / second',
9: 'Transactions / block',
10: 'Block time',
11: 'Time',
12: 'App',
13: 'Tendermint',
14: 'Up since',
15: 'Chain ID',
};
cy.get('[data-testid="stats-title"]')
.each(($list, index) => {
cy.wrap($list).should('have.text', statTitles[index]);
})
.then(($list) => {
cy.wrap($list).should('have.length', 16);
});
cy.get(statsValue).eq(0).should('have.text', 'CONNECTED');
cy.get(statsValue).eq(1).should('not.be.empty');
cy.get(statsValue)
.eq(2)
.invoke('text')
.should('match', /\d+d \d+h \d+m \d+s/i);
cy.get(statsValue).eq(3).should('have.text', '2');
cy.get(statsValue)
.eq(4)
.invoke('text')
.should('match', /\d+\.\d\d(?!\d)/i);
cy.get(statsValue).eq(5).should('have.text', '0');
cy.get(statsValue).eq(6).should('have.text', '0');
cy.get(statsValue).eq(7).should('have.text', '0');
cy.get(statsValue).eq(8).should('have.text', '0');
cy.get(statsValue).eq(9).should('not.be.empty');
cy.get(statsValue).eq(10).should('not.be.empty');
cy.get(statsValue).eq(11).should('not.be.empty');
cy.get(statsValue)
.eq(12)
.invoke('text')
.should('match', /v\d+\.\d+\.\d+/i);
cy.get(statsValue)
.eq(13)
.invoke('text')
.should('match', /\d+\.\d+\.\d+/i);
cy.get(statsValue).eq(14).should('not.be.empty');
cy.get(statsValue).eq(15).should('not.be.empty');
});
it('Block height should be updating', function () {
cy.get(statsValue)
.eq(1)
.invoke('text')
.then((blockHeightTxt) => {
cy.get(statsValue)
.eq(1)
.invoke('text')
.should((newBlockHeightTxt) => {
expect(blockHeightTxt).not.to.eq(newBlockHeightTxt);
});
});
});
});
describe('Git info', function () {
it('git info is rendered on the footer of the page', function () {
cy.getByTestId('git-info').within(() => {
cy.getByTestId('git-network-data').within(() => {
cy.contains('Reading network data from').should('be.visible');
cy.get('span').should('have.text', Cypress.env('networkQueryUrl'));
cy.getByTestId('link').should('be.visible');
});
cy.getByTestId('git-eth-data').within(() => {
cy.contains('Reading Ethereum data from').should('be.visible');
cy.get('span').should('have.text', Cypress.env('ethUrl'));
});
cy.getByTestId('git-commit-hash').within(() => {
cy.contains('Version/commit hash:').should('be.visible');
cy.getByTestId('link').should('have.text', Cypress.env('commitHash'));
});
});
});
});
describe('Search bar', function () {
it('Successful search for specific id by block id', function () {
const blockId = '973624';
search(blockId);
cy.url().should('include', blockId);
});
it('Successful search for specific id by tx hash', function () {
const txHash =
'9ED3718AA8308E7E08EC588EE7AADAF49711D2138860D8914B4D81A2054D9FB8';
search(txHash);
cy.url().should('include', txHash);
});
it('Successful search for specific id by tx id', function () {
const txId =
'0x61DCCEBB955087F50D0B85382DAE138EDA9631BF1A4F92E563D528904AA38898';
search(txId);
cy.url().should('include', txId);
});
it('Error message displayed when invalid search by wrong string length', function () {
search('9ED3718AA8308E7E08EC588EE7AADAF497D2138860D8914B4D81A2054D9FB8');
validateSearchError("Something doesn't look right");
});
it('Error message displayed when invalid search by invalid hash', function () {
search(
'9ED3718AA8308E7E08ECht8EE753DAF49711D2138860D8914B4D81A2054D9FB8'
);
validateSearchError('Transaction is not hexadecimal');
});
it('Error message displayed when searching empty field', function () {
cy.get('[data-testid="search"]').clear();
cy.get('[data-testid="search-button"]').click();
validateSearchError('Search required');
});
function search(searchTxt) {
cy.get('[data-testid="search"]').clear().type(searchTxt);
cy.get('[data-testid="search-button"]').click();
}
function validateSearchError(expectedError) {
cy.get('[data-testid="search-error"]').should('have.text', expectedError);
}
});
});
@@ -1,19 +0,0 @@
//Tests set to skip until market bug for capsule checkpoint is fixed
context.skip('Market page', { tags: '@regression' }, function () {
describe('Verify elements on page', function () {
const marketHeaders = '[data-testid="markets-header"]';
const marketNavigation = 'a[href="/markets"]';
it('Markets page is displayed', function () {
cy.visit('/');
cy.get(marketNavigation).click();
cy.common_validate_blocks_data_displayed(marketHeaders);
});
it('Markets page displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(marketNavigation).click();
cy.common_validate_blocks_data_displayed(marketHeaders);
});
});
});
@@ -0,0 +1,14 @@
@todo
Feature: Markets Page
@todo - Needs markets in capsule state
Scenario: Navigate to markets page
Given I am on the homepage
When I navigate to the markets page
Then markets page is correctly displayed
@todo
Scenario: Navigate to markets page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the markets page
Then markets page is correctly displayed
@@ -0,0 +1,11 @@
Feature: Network parameters Page
Scenario: Navigate to network parameters page
Given I am on the homepage
When I navigate to network parameters page
Then network parameters page is correctly displayed
Scenario: Navigate to network parameters page using mobile
Given I am on mobile and open the toggle menu
When I navigate to network parameters page
Then network parameters page is correctly displayed
@@ -1,267 +0,0 @@
context('Network parameters page', { tags: '@smoke' }, function () {
before('navigate to network parameter page', function () {
cy.fixture('net_parameter_format_lookup').as('networkParameterFormat');
});
describe('Verify elements on page', function () {
const networkParametersNavigation = 'a[href="/network-parameters"]';
const networkParametersHeader = '[data-testid="network-param-header"]';
const tableRows = '[data-testid="key-value-table-row"]';
before('navigate to network parameter page', function () {
cy.visit('/');
cy.get(networkParametersNavigation).click();
});
it('should show network parameter heading at top of page', function () {
cy.get(networkParametersHeader)
.should('have.text', 'Network Parameters')
.and('be.visible');
});
it('should list each of the network parameters available', function () {
cy.get_network_parameters().then((network_parameters) => {
const numberOfNetworkParametersInSystem =
Object.keys(network_parameters).length;
cy.get(tableRows).should(
'have.length',
numberOfNetworkParametersInSystem
);
});
});
it('should list each network parameter displayed with json - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.json.includes(parameterName)) {
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.invoke('text')
.convert_string_json_to_js_object()
.then((jsonOnPage) => {
cy.wrap(parameterValue, { log: false })
.convert_string_json_to_js_object()
.then((jsonInSystem) =>
assert.deepEqual(
jsonOnPage,
jsonInSystem,
`Checking ${parameterName} has the correct value of ${jsonInSystem}`
)
);
});
}
});
});
});
it('should list each network parameter displayed as a percentage - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.percentage.includes(parameterName)) {
const formattedPercentageParameter =
(parseFloat(parameterValue) * 100).toFixed(0) + '%';
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.invoke('text')
.then((parameterValueOnPage) => {
assert.equal(
parameterValueOnPage,
formattedPercentageParameter,
`Checking ${parameterName} has the correct value of ${formattedPercentageParameter}`
);
cy.contains(parameterValueOnPage).should('be.visible');
});
}
});
});
});
it('should list each network parameter displayed as an id - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.id.includes(parameterName)) {
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.should('contain', parameterValue)
.and('be.visible')
.invoke('text');
}
});
});
});
it('should list each network parameter displayed as a date - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.date.includes(parameterName)) {
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.should('contain', parameterValue)
.and('be.visible');
}
});
});
});
it('should list each network parameter displayed as a duration - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.duration.includes(parameterName)) {
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.should('contain', parameterValue)
.and('be.visible');
}
});
});
});
it('should list each network parameter displayed as a currency value with four decimals - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (this.networkParameterFormat.fiveDecimal.includes(parameterName)) {
cy.convert_number_to_max_four_decimal(parameterValue)
.add_commas_to_number_if_large_enough()
.then((parameterValueFormatted) => {
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.invoke('text')
.then((parameterValueOnPage) => {
assert.equal(
parameterValueOnPage,
parameterValueFormatted,
`Checking ${parameterName} has the correct value of ${parameterValueFormatted}`
);
cy.contains(parameterValueOnPage).should('be.visible');
});
});
}
});
});
});
it('should list each network parameter displayed as a currency value with eighteen decimals - in the correct format', function () {
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
const parameterValue = network_parameter[1];
if (
this.networkParameterFormat.eighteenDecimal.includes(parameterName)
) {
cy.convert_number_to_max_eighteen_decimal(parameterValue)
.add_commas_to_number_if_large_enough()
.then((parameterValueFormatted) => {
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.invoke('text')
.then((parameterValueOnPage) => {
assert.equal(
parameterValueOnPage,
parameterValueFormatted,
`Checking ${parameterName} has the correct value of ${parameterValueFormatted}`
);
cy.contains(parameterValueOnPage).should('be.visible');
});
});
}
});
});
});
it('should be able to switch network parameter page - between light and dark mode', function () {
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
const themeSwitcher = '[data-testid="theme-switcher"]';
const jsonFields = '.hljs';
const sideMenuBackground = '.absolute';
// Engage dark mode if not already set
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.then((background_color) => {
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
cy.get(themeSwitcher).click();
});
// Engage white mode
cy.get(themeSwitcher).click();
// White Mode
cy.get(networkParametersNavigation)
.should('have.css', 'background-color')
.and('include', whiteThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', whiteThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', whiteThemeSideMenuBackgroundColor);
// Dark Mode
cy.get(themeSwitcher).click();
cy.get(networkParametersNavigation)
.should('have.css', 'background-color')
.and('include', darkThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', darkThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', darkThemeSideMenuBackgroundColor);
});
it('should be able to see network parameters - on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(networkParametersNavigation).click();
cy.get_network_parameters().then((network_parameters) => {
network_parameters = Object.entries(network_parameters);
network_parameters.forEach((network_parameter) => {
const parameterName = network_parameter[0];
cy.get(tableRows)
.contains(parameterName)
.should('be.visible')
.next()
.should('not.be.empty')
.and('be.visible');
});
});
});
});
});
@@ -1,57 +0,0 @@
const nodeErrorType = 'node-error-type';
const nodeErrorMsg = 'node-error-message';
const nodeId = 'node-url-0';
const customNodeBtn = 'custom-node';
context.skip('Node switcher', { tags: '@regression' }, function () {
beforeEach('visit home page', function () {
cy.intercept('GET', 'https://static.vega.xyz/assets/capsule-network.json', {
hosts: ['http://localhost:3028/query'],
}).as('nodeData');
cy.visit('/');
cy.wait('@nodeData');
cy.getByTestId('git-network-data').within(() => {
cy.getByTestId('link').should('be.visible').click();
});
});
describe('form validations and responses', function () {
it('node data is displayed', function () {
cy.getByTestId('node-row').should('have.length.at.least', 2);
cy.getByTestId('node-row')
.eq(0)
.within(() => {
cy.getByTestId(nodeId)
.should('exist')
.and('have.attr', 'aria-checked', 'true');
cy.get('label').should('have.text', Cypress.env('networkQueryUrl'));
cy.getByTestId('ssl-cell').should('have.text', 'Checking');
cy.getByTestId('ssl-cell', { timeout: 6000 }).should(
'not.have.text',
'Checking'
);
cy.getByTestId('response-time-cell').should('contain.text', 'ms');
cy.getByTestId('block-cell').should('not.be.empty');
});
});
it('cannot connect to network using invalid url', function () {
const errorTypeTxt = 'Error: invalid url';
const nodeErrorTxt = 'fakeUrl is not a valid url.';
cy.getByTestId('node-url-custom').click();
cy.getByTestId(customNodeBtn).within(() => {
cy.get('input').clear().type('fakeUrl');
cy.getByTestId('link').click();
});
validateNodeError(errorTypeTxt, nodeErrorTxt);
});
function validateNodeError(errortype, errorMsg) {
cy.getByTestId(nodeErrorType).should('have.text', errortype);
cy.getByTestId(nodeErrorMsg).should('have.text', errorMsg);
}
});
});
@@ -1,256 +0,0 @@
const vegaWalletPublicKey = Cypress.env('vegaWalletPublicKey');
const partiesMenuHeader = 'a[href="/parties"]';
const partiesSearchBox = '[data-testid="party-input"]';
const partiesSearchAction = '[data-testid="go-submit"]';
const partiesJsonSection = '[data-testid="parties-json"]';
const txTimeout = Cypress.env('txTimeout');
let assetData = {
fUSDC: { id: 'fUSDC', name: 'USDC (fake)', amount: '10' },
fBTC: { id: 'fBTC', name: 'BTC (fake)', amount: '6' },
fEURO: { id: 'fEURO', name: 'EURO (fake)', amount: '8' },
fDAI: { id: 'fDAI', name: 'DAI (fake)', amount: '2' },
};
const assetsInTest = Object.keys(assetData);
// skipped due to existing issue
// https://github.com/vegaprotocol/frontend-monorepo/issues/2243
context.skip('Parties page', { tags: '@regression' }, function () {
before('send-faucet assets to connected vega wallet', function () {
assetsInTest.forEach((asset) => {
cy.vega_wallet_receive_fauceted_asset(
assetData[asset].name,
assetData[asset].amount,
vegaWalletPublicKey
);
});
cy.visit('/');
});
describe('Verify parties page content', function () {
before('navigate to parties page and search for party', function () {
cy.get(partiesMenuHeader).click();
// Deliberate slow entry of party id/key - enabling transactions to sync
cy.get(partiesSearchBox).type(vegaWalletPublicKey, { delay: 120 });
cy.get(partiesSearchAction).click();
cy.get_connected_parties_accounts().as('party_accounts');
// Ensure balance of each party asset is correct
cy.get('@party_accounts').then((accounts) => {
assetsInTest.forEach((asset) => {
cy.get_asset_decimals(assetData[asset].id).then((assetDecimals) => {
assert.equal(
accounts[assetData[asset].id].balance,
assetData[asset].amount + assetDecimals,
`Checking ${assetData[asset].id} faucet was successfull`
);
});
});
});
});
it('should see party address id - having searched', function () {
cy.getByTestId('parties-header')
.siblings()
.contains(vegaWalletPublicKey.substring(0, 14))
.should('be.visible');
});
it('should see each asset and balance - within asset data section', function () {
assetsInTest.forEach((asset) => {
cy.contains(assetData[asset].name, txTimeout).should('be.visible');
cy.contains(assetData[asset].name)
.siblings()
.contains(assetData[asset].id)
.should('be.visible');
cy.contains(assetData[asset].name, txTimeout)
.parent()
.siblings()
.within(() => {
cy.get_asset_decimals(asset).then((assetDecimals) => {
cy.contains_exactly(
assetData[asset].amount + '.' + assetDecimals
).should('be.visible');
});
});
});
});
it(
'should be able to copy the party address id',
{ browser: 'chrome' },
function () {
cy.monitor_clipboard().as('clipboard');
cy.getByTestId('parties-header')
.next()
.within(() => {
cy.get('button').click();
});
cy.get('@clipboard')
.get_copied_text_from_clipboard()
.should('equal', vegaWalletPublicKey);
}
);
it(
'should be able to copy an asset id',
{ browser: 'chrome' },
function () {
cy.monitor_clipboard().as('clipboard');
cy.contains(assetData.fDAI.name, txTimeout).should('be.visible');
cy.contains(assetData.fDAI.name)
.parent()
.parent()
.siblings()
.within(() => {
cy.get('[data-state="closed"]').last().click({ force: true });
});
cy.get('@clipboard')
.get_copied_text_from_clipboard()
.should('equal', assetData.fDAI.id);
}
);
it('should be able to see JSON of each asset containing correct balance and decimals', function () {
cy.get(partiesJsonSection).should('be.visible');
assetsInTest.forEach((assetInTest) => {
cy.get(partiesJsonSection)
.invoke('text')
.convert_string_json_to_js_object()
.get_party_accounts_data_from_js_object()
.then((accountsListedInJson) => {
cy.get_asset_information().then((assetsInfo) => {
const assetInfo =
assetsInfo[accountsListedInJson[assetInTest].asset.name];
assert.equal(
accountsListedInJson[assetInTest].asset.name,
assetInfo.name
);
assert.equal(
accountsListedInJson[assetInTest].asset.id,
assetInfo.id
);
assert.equal(
accountsListedInJson[assetInTest].asset.decimals,
assetInfo.decimals
);
assert.equal(
accountsListedInJson[assetInTest].asset.symbol,
assetInfo.symbol
);
assert.equal(
accountsListedInJson[assetInTest].asset.source.__typename,
assetInfo.source.__typename
);
cy.get_asset_decimals(assetInTest).then((assetDecimals) => {
assert.equal(
accountsListedInJson[assetInTest].balance,
assetData[assetInTest].amount + assetDecimals
);
});
});
});
});
});
it('should be able to switch parties page between light and dark mode', function () {
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
const themeSwitcher = '[data-testid="theme-switcher"]';
const jsonFields = '.hljs';
const sideMenuBackground = '.absolute';
// Engage dark mode if not allready set
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.then((background_color) => {
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
cy.get(themeSwitcher).click();
});
// Engage white mode
cy.get(themeSwitcher).click();
// White Mode
cy.get(partiesMenuHeader)
.should('have.css', 'background-color')
.and('include', whiteThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', whiteThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', whiteThemeSideMenuBackgroundColor);
// Dark Mode
cy.get(themeSwitcher).click();
cy.get(partiesMenuHeader)
.should('have.css', 'background-color')
.and('include', darkThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', darkThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', darkThemeSideMenuBackgroundColor);
});
Cypress.Commands.add('get_asset_decimals', (assetID) => {
cy.get_asset_information().then((assetsInfo) => {
const assetDecimals = assetsInfo[assetData[assetID].name].decimals;
let decimals = '';
for (let i = 0; i < assetDecimals; i++) decimals += '0';
return decimals;
});
});
Cypress.Commands.add('get_connected_parties_accounts', () => {
const mutation =
'{partiesConnection {edges{node{accountsConnection{edges{node{\
balance type asset {id symbol decimals}}}}}}}}';
cy.request({
method: 'POST',
url: `http://localhost:3028/query`,
body: {
query: mutation,
},
headers: { 'content-type': 'application/json' },
})
.its(
`body.data.partiesConnection.edges.1.node.accountsConnection.edges`
)
.then(function (response) {
let accounts = {};
response.forEach((account) => {
accounts[account.node.asset.id] = account.node;
});
return accounts;
});
});
Cypress.Commands.add(
'get_party_accounts_data_from_js_object',
{ prevSubject: true },
(jsObject) => {
const accounts = jsObject.party.accounts.reduce(function (
account,
entry
) {
account[entry.asset.id] = entry;
return account;
},
{});
return accounts;
}
);
});
});
@@ -0,0 +1,21 @@
@ignore
# tendermint times out getting txs on testnet atm
Feature: Transactions Page
Scenario: Navigate to transactions page
Given I am on the homepage
When I navigate to the transactions page
Then transactions page is correctly displayed
Scenario: Navigate to transaction details page
Given I am on the homepage
When I navigate to the transactions page
And I click on the top transaction
Then transaction details are displayed
Scenario: Navigate to transactions page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the transactions page
Then transactions page is correctly displayed
When I click on the top transaction
Then transaction details are displayed
@@ -1,87 +0,0 @@
//Tests set to skip until transactions are generated after capsule start up
context.skip('Transactions page', function () {
before('visit token home page', function () {
cy.visit('/');
});
describe('Verify elements on page', { tags: '@regression' }, function () {
const transactionNavigation = 'a[href="/txs"]';
const transactionRow = 'transaction-row';
const txHash = 'hash';
beforeEach('Navigate to transactions page', function () {
cy.get(transactionNavigation).click();
});
it('transactions are displayed', function () {
cy.get(transactionRow).should('have.length.above', 1);
});
it('transactions details page is displayed', function () {
clickTopTransaction();
validateTxDetailsAreDisplayed();
});
it('transactions details page is displayed in mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(transactionNavigation).click();
clickTopTransaction();
validateTxDetailsAreDisplayed();
});
function clickTopTransaction() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(transactionRow).length) {
cy.get(transactionRow)
.first()
.find('a')
.first()
.click({ force: true });
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
function validateTxDetailsAreDisplayed() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(txHash).length) {
cy.get(txHash).invoke('text').should('have.length', 64);
cy.get('submitted-by')
.find('a')
.then(($address) => {
cy.wrap($address).should('have.attr', 'href');
cy.wrap($address).invoke('text').should('have.length', 66);
});
cy.get('block').should('not.be.empty');
cy.get('encoded-tnx').should('not.be.empty');
cy.get('tx-type')
.should('not.be.empty')
.invoke('text')
.then((txTypeTxt) => {
if (txTypeTxt == 'Order Submission') {
cy.get('.hljs-attr')
.should('have.length.at.least', 8)
.each(($propertyName) => {
cy.wrap($propertyName).should('not.be.empty');
});
cy.get('.hljs-string')
.should('have.length.at.least', 8)
.each(($propertyValue) => {
cy.wrap($propertyValue).should('not.be.empty');
});
}
});
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
});
});
@@ -1,303 +0,0 @@
context('Validator page', { tags: '@smoke' }, function () {
const validatorMenuHeading = 'a[href="/validators"]';
const tendermintDataHeader = '[data-testid="tendermint-header"]';
const vegaDataHeader = '[data-testid="vega-header"]';
const jsonSection = '.language-json';
before('Visit validators page and obtain data', function () {
cy.visit('/');
cy.get(validatorMenuHeading).click();
cy.get_validators().as('validators');
cy.get_nodes().as('nodes');
});
describe('Verify elements on page', function () {
before('Ensure at least two validators are present', function () {
assert.isAtLeast(
this.validators.length,
2,
'Ensuring at least two validators exist'
);
});
it('should be able to see validator page sections', function () {
cy.get(vegaDataHeader)
.contains('Vega data')
.and('is.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
cy.get(tendermintDataHeader)
.contains('Tendermint data')
.and('is.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
});
it('should be able to see relevant validator information in tendermint section', function () {
cy.get(tendermintDataHeader)
.contains('Tendermint data')
.next()
.within(() => {
cy.get(jsonSection)
.invoke('text')
.convert_string_json_to_js_object()
.then((validatorsInJson) => {
this.validators.forEach((validator, index) => {
const validatorInJson =
validatorsInJson.result.validators[index];
assert.equal(
validatorInJson.address,
validator.address,
`Checking that validator address shown in json matches system data`
);
cy.contains(validator.address).should('be.visible');
assert.equal(
validatorInJson.pub_key.type,
validator.pub_key.type,
`Checking that validator public key type shown in json matches system data`
);
cy.contains(validator.pub_key.type).should('be.visible');
assert.equal(
validatorInJson.pub_key.value,
validator.pub_key.value,
`Checking that validator public key value shown in json matches system data`
);
cy.contains(validator.pub_key.value).should('be.visible');
assert.equal(
validatorInJson.voting_power,
validator.voting_power,
`Checking that validator voting power in json matches system data`
);
cy.contains(validator.voting_power).should('be.visible');
// Proposer priority can change frequently mid test
// Therefore only checking the field name is present.
cy.contains('proposer_priority').should('be.visible');
});
});
});
});
// Test disabled 2022/11/15 during the 0.62.1 upgrade. The JSON structure changed, and
// this test failed. Rather than fix it, it will be replaced when the validator view displays
// something useful rather than just dumping out the JSON.
xit('should be able to see relevant node information in vega data section', function () {
cy.get(vegaDataHeader)
.contains('Vega data')
.next()
.within(() => {
cy.get(jsonSection)
.invoke('text')
.convert_string_json_to_js_object()
.then((nodesInJson) => {
this.nodes.forEach((node, index) => {
const nodeInJson = nodesInJson.edges[index].node;
// Vegacapsule shows no info or null for following fields:
// name, infoURL, avatarUrl, location, epoch data
// Therefore, these values remain unchecked.
assert.equal(
nodeInJson.__typename,
node.__typename,
`Checking that node __typename shown in json matches system data`
);
cy.contains(node.__typename).should('be.visible');
assert.equal(
nodeInJson.id,
node.id,
`Checking that node id shown in json matches system data`
);
cy.contains(node.id).should('be.visible');
assert.equal(
nodeInJson.pubkey,
node.pubkey,
`Checking that node pubkey shown in json matches system data`
);
cy.contains(node.pubkey).should('be.visible');
assert.equal(
nodeInJson.tmPubkey,
node.tmPubkey,
`Checking that node tmPubkey shown in json matches system data`
);
cy.contains(node.tmPubkey).should('be.visible');
assert.equal(
nodeInJson.ethereumAddress,
node.ethereumAddress,
`Checking that node ethereumAddress shown in json matches system data`
);
cy.contains(node.ethereumAddress).should('be.visible');
assert.equal(
nodeInJson.stakedByOperator,
node.stakedByOperator,
`Checking that node stakedByOperator value shown in json matches system data`
);
cy.contains(node.stakedByOperator).should('be.visible');
assert.equal(
nodeInJson.stakedByDelegates,
node.stakedByDelegates,
`Checking that node stakedByDelegates value shown in json matches system data`
);
cy.contains(node.stakedByDelegates).should('be.visible');
assert.equal(
nodeInJson.stakedTotal,
node.stakedTotal,
`Checking that node stakedTotal shown in json matches system data`
);
cy.contains(node.stakedTotal).should('be.visible');
assert.equal(
nodeInJson.pendingStake,
node.pendingStake,
`Checking that node pendingStake shown in json matches system data`
);
cy.contains(node.pendingStake).should('be.visible');
assert.equal(
nodeInJson.status,
node.status,
`Checking that node status shown in json matches system data`
);
cy.contains(node.status).should('be.visible');
});
});
});
});
it('should be able to see validator page displayed on mobile', function () {
cy.common_switch_to_mobile_and_click_toggle();
cy.get(validatorMenuHeading).click();
cy.get(vegaDataHeader)
.contains('Vega data')
.and('is.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
cy.get(tendermintDataHeader)
.contains('Tendermint data')
.and('is.visible')
.next()
.within(() => {
cy.get(jsonSection).should('not.be.empty');
});
cy.get(tendermintDataHeader)
.contains('Tendermint data')
.next()
.within(() => {
this.validators.forEach((validator) => {
cy.contains(validator.address).should('be.visible');
cy.contains(validator.pub_key.type).should('be.visible');
cy.contains(validator.pub_key.value).should('be.visible');
cy.contains(validator.voting_power).should('be.visible');
// Proposer priority can change frequently mid test
// Therefore only checking the field name is present.
cy.contains('proposer_priority').should('be.visible');
});
});
});
it('should be able to switch validator page between light and dark mode', function () {
const whiteThemeSelectedMenuOptionColor = 'rgb(255, 7, 127)';
const whiteThemeJsonFieldBackColor = 'rgb(255, 255, 255)';
const whiteThemeSideMenuBackgroundColor = 'rgb(255, 255, 255)';
const darkThemeSelectedMenuOptionColor = 'rgb(215, 251, 80)';
const darkThemeJsonFieldBackColor = 'rgb(38, 38, 38)';
const darkThemeSideMenuBackgroundColor = 'rgb(0, 0, 0)';
const themeSwitcher = '[data-testid="theme-switcher"]';
const jsonFields = '.hljs';
const sideMenuBackground = '.absolute';
// Engage dark mode if not allready set
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.then((background_color) => {
if (background_color.includes(whiteThemeSideMenuBackgroundColor))
cy.get(themeSwitcher).click();
});
// Engage white mode
cy.get(themeSwitcher).click();
// White Mode
cy.get(validatorMenuHeading)
.should('have.css', 'background-color')
.and('include', whiteThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', whiteThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', whiteThemeSideMenuBackgroundColor);
// Dark Mode
cy.get(themeSwitcher).click();
cy.get(validatorMenuHeading)
.should('have.css', 'background-color')
.and('include', darkThemeSelectedMenuOptionColor);
cy.get(jsonFields)
.should('have.css', 'background-color')
.and('include', darkThemeJsonFieldBackColor);
cy.get(sideMenuBackground)
.should('have.css', 'background-color')
.and('include', darkThemeSideMenuBackgroundColor);
});
Cypress.Commands.add('get_validators', () => {
cy.request({
method: 'GET',
url: `http://localhost:26617/validators`,
headers: { 'content-type': 'application/json' },
})
.its(`body.result.validators`)
.then(function (response) {
let validators = [];
response.forEach((account, index) => {
validators[index] = account;
});
return validators;
});
});
Cypress.Commands.add('get_nodes', () => {
const mutation =
'{nodesConnection { edges { node { id name infoUrl avatarUrl pubkey tmPubkey ethereumAddress \
location stakedByOperator stakedByDelegates stakedTotal pendingStake \
epochData { total offline online __typename } status name __typename}}}}';
cy.request({
method: 'POST',
url: `http://localhost:3028/query`,
body: {
query: mutation,
},
headers: { 'content-type': 'application/json' },
})
.its(`body.data.nodesConnection.edges`)
.then(function (response) {
let nodes = [];
response.forEach((node) => {
nodes.push(node);
});
return nodes;
});
});
});
});
@@ -0,0 +1,11 @@
Feature: Validators Page
Scenario: Navigate to validators page
Given I am on the homepage
When I navigate to the validators page
Then validators page is correctly displayed
Scenario: Navigate to validators page using mobile
Given I am on mobile and open the toggle menu
When I navigate to the validators page
Then validators page is correctly displayed
+75
View File
@@ -0,0 +1,75 @@
/// <reference types="cypress" />
const webpackPreprocessor = require('@cypress/webpack-preprocessor');
const webpack = require('webpack');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
/**
* @type {Cypress.PluginConfig}
*/
module.exports = (on, config) => {
on(
'file:preprocessor',
webpackPreprocessor({
webpackOptions: {
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
plugins: [
new TsconfigPathsPlugin({
configFile: config.env.tsConfig,
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
}),
],
fallback: {
path: require.resolve('path-browserify'),
},
},
module: {
rules: [
{
test: /\.([jt])sx?$/,
loader: 'ts-loader',
exclude: [/node_modules/],
options: {
configFile: config.env.tsConfig,
// https://github.com/TypeStrong/ts-loader/pull/685
experimentalWatchApi: true,
transpileOnly: true,
},
},
{
test: /\.feature$/,
use: [
{
loader: 'cypress-cucumber-preprocessor/loader',
},
],
},
{
test: /\.features$/,
use: [
{
loader: 'cypress-cucumber-preprocessor/lib/featuresLoader',
},
],
},
],
},
plugins: [
new ForkTsCheckerWebpackPlugin({
typescript: {
enabled: true,
configFile: config.env.tsConfig,
},
}),
new webpack.ProvidePlugin({
process: 'process/browser',
}),
],
externals: [nodeExternals()],
},
})
);
};
@@ -1,86 +0,0 @@
import { BigNumber } from 'bignumber.js';
Cypress.Commands.add(
'common_validate_blocks_data_displayed',
function (headerTestId) {
cy.get(headerTestId).then(($assetHeaders) => {
const headersLength = Number($assetHeaders.length);
cy.wrap($assetHeaders).each(($header) => {
expect($header).to.not.be.empty;
});
cy.get('.language-json')
.each(($asset) => {
expect($asset).to.not.be.empty;
})
.then(($list) => {
expect($list).to.have.length(headersLength);
});
});
}
);
Cypress.Commands.add('common_switch_to_mobile_and_click_toggle', function () {
cy.viewport('iphone-x');
cy.visit('/');
cy.get('[data-testid="open-menu"]').click();
});
Cypress.Commands.add('monitor_clipboard', () => {
cy.window().then((win) => {
return cy.stub(win, 'prompt').returns(win.prompt);
});
});
Cypress.Commands.add(
'get_copied_text_from_clipboard',
{ prevSubject: true },
(clipboard) => {
// Must first setup with cy.monitor_clipboard().as('clipboard')
// This function then chained off a cy.get('@clipboard')
return clipboard.args[0][1];
}
);
Cypress.Commands.add(
'convert_string_json_to_js_object',
{ prevSubject: true },
(jsonBlobString) => {
// Note: this is a chaining function
return JSON.parse(jsonBlobString);
}
);
Cypress.Commands.add(
'add_commas_to_number_if_large_enough',
{ prevSubject: true },
(number) => {
// This will turn 10000000.0000 into 10,000,000.000
// Note: this is a chaining function
const beforeDecimal = number.split('.')[0];
const afterDecimal = number.split('.')[1];
const beforeDecimalWithCommas = beforeDecimal
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
const formattedValue = beforeDecimalWithCommas + '.' + afterDecimal;
return formattedValue;
}
);
Cypress.Commands.add('convert_number_to_max_eighteen_decimal', (number) => {
// this will take a number like this : 700000000000000000001
// and convert it to a number like this: 700.000000000000000001
const value = BigNumber(number / 1000000000000000000).toString();
return parseFloat(value).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 18,
});
});
Cypress.Commands.add('convert_number_to_max_four_decimal', (number) => {
return parseFloat(number).toLocaleString(undefined, {
minimumFractionDigits: 2,
maximumFractionDigits: 4,
});
});
@@ -14,6 +14,3 @@
// ***********************************************************
import '@vegaprotocol/cypress';
import './common.functions.js';
import registerCypressGrep from '@cypress/grep';
registerCypressGrep();
@@ -0,0 +1,9 @@
import BasePage from './base-page';
export default class AssetsPage extends BasePage {
assetHeader = 'asset-header';
validateAssetsDisplayed() {
this.validateBlockDataDisplayed(this.assetHeader);
}
}
@@ -0,0 +1,100 @@
export default class BasePage {
transactionsUrl = '/txs';
blocksUrl = '/blocks';
partiesUrl = '/parties';
assetsUrl = '/assets';
genesisUrl = '/genesis';
governanceUrl = '/governance';
marketsUrl = '/markets';
networkParametersUrl = '/network-parameters';
validatorsUrl = '/validators';
blockExplorerHeader = 'explorer-header';
searchField = 'search';
searchButton = 'search-button';
searchError = 'search-error';
openMobileMenuBtn = 'open-menu';
navigateToTxs() {
cy.get(`a[href='${this.transactionsUrl}']`).click();
}
navigateToBlocks() {
cy.get(`a[href='${this.blocksUrl}']`).click();
}
navigateToParties() {
cy.get(`a[href='${this.partiesUrl}']`).click();
}
navigateToAssets() {
cy.get(`a[href*='${this.assetsUrl}']`).click();
}
navigateToGenesis() {
cy.get(`a[href='${this.genesisUrl}']`).click();
}
navigateToGovernance() {
cy.get(`a[href='${this.governanceUrl}']`).click();
}
navigateToMarkets() {
cy.get(`a[href='${this.marketsUrl}']`).click();
}
navigateToNetworkParameters() {
cy.get(`a[href='${this.networkParametersUrl}']`).click();
}
navigateToValidators() {
cy.get(`a[href='${this.validatorsUrl}']`).click();
}
search(searchText: string) {
if (searchText) {
cy.getByTestId(this.searchField).type(searchText);
}
}
clickSearch() {
cy.getByTestId(this.searchButton).click();
}
clickOnToggle() {
cy.getByTestId(this.openMobileMenuBtn).click({ force: true });
}
validateUrl(expectedUrl: string) {
cy.url().should('include', expectedUrl);
}
validateSearchDisplayed() {
cy.getByTestId(this.blockExplorerHeader).should(
'have.text',
'Vega Explorer'
);
cy.getByTestId(this.searchField).should('be.visible');
}
validateSearchErrorDisplayed(errorMessage: string) {
cy.getByTestId(this.searchError).should('have.text', errorMessage);
}
validateBlockDataDisplayed(headerTestId: string) {
cy.getByTestId(headerTestId).then(($assetHeaders) => {
const headersAmount = Number($assetHeaders.length);
cy.wrap($assetHeaders).each(($header) => {
expect($header).to.not.be.empty;
});
cy.get('.language-json')
.each(($asset) => {
expect($asset).to.not.be.empty;
})
.then(($list) => {
expect($list).to.have.length(headersAmount);
});
});
}
}
@@ -0,0 +1,146 @@
import BasePage from './base-page';
export default class BlocksPage extends BasePage {
blockRow = 'block-row';
transactionsRow = 'transaction-row';
blockHeight = 'block-height';
numberOfTransactions = 'num-txs';
validatorLink = 'validator-link';
blockTime = 'block-time';
refreshBtn = 'refresh';
blockHeader = 'block-header';
minedByValidator = 'block-validator';
previousBlockBtn = 'previous-block';
nextBlockBtn = 'next-block';
jumpToBlockInput = 'block-input';
jumpToBlockSubmit = 'go-submit';
infiniteScrollWrapper = 'infinite-scroll-wrapper';
private waitForBlocksResponse() {
cy.contains('Loading...').should('not.exist', { timeout: 18000 });
}
validateBlocksPageDisplayed() {
this.waitForBlocksResponse();
cy.getByTestId(this.blockRow).should('have.length.above', 1);
cy.getByTestId(this.blockHeight).first().should('not.be.empty');
cy.getByTestId(this.numberOfTransactions).first().should('not.be.empty');
cy.getByTestId(this.validatorLink).first().should('not.be.empty');
cy.getByTestId(this.blockTime).first().should('not.be.empty');
}
clickOnTopBlockHeight() {
this.waitForBlocksResponse();
cy.getByTestId(this.blockHeight).first().click();
}
validateBlockValidatorPage() {
cy.getByTestId(this.minedByValidator).should('not.be.empty');
cy.getByTestId(this.blockTime).should('not.be.empty');
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.transactionsRow}] > td`).length) {
cy.get(`[data-testid=${this.transactionsRow}] > td`).each(($cell) => {
cy.wrap($cell).should('not.be.empty');
});
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
navigateToPreviousBlock() {
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
this.clickPreviousBlock();
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.lessThan(blockHeight);
});
});
}
navigateToNextBlock() {
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($blockHeaderTxt) => {
const blockHeight = parseInt($blockHeaderTxt.replace('BLOCK ', ''));
this.clickNextBlock();
cy.getByTestId(this.blockHeader)
.invoke('text')
.then(($newBlockHeaderTxt) => {
const newBlockHeight = parseInt(
$newBlockHeaderTxt.replace('BLOCK ', '')
);
expect(newBlockHeight).to.be.greaterThan(blockHeight);
});
});
}
navigateToLastBlockOnPage() {
this.waitForBlocksResponse();
cy.getByTestId(this.infiniteScrollWrapper).children().scrollTo('bottom');
}
navigateToOlderBlocksWithInfiniteScroll(
expectedBlocks: number,
scrollAttempts: number
) {
cy.intercept('*blockchain?maxHeight*').as('blockchain_load');
cy.getByTestId(this.blockHeight)
.last()
.invoke('text')
.then(($initialLastBlockHeight) => {
for (let index = 0; index < scrollAttempts; index++) {
cy.getByTestId(this.infiniteScrollWrapper)
.children()
.children()
.invoke('css', 'height')
.then((scrollTarget) => {
cy.getByTestId(this.infiniteScrollWrapper)
.children()
.scrollTo(0, scrollTarget.toString(), { easing: 'linear' })
.wait('@blockchain_load');
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(50); // Need this as although network response has arrived it takes a few millisecs for the css height to expand
});
}
cy.getByTestId(this.blockHeight)
.last()
.invoke('text')
.then(($lastBlockHeight) => {
const totalBlocksLoadedSinceScrollBegan =
parseInt($initialLastBlockHeight) - parseInt($lastBlockHeight);
expect(totalBlocksLoadedSinceScrollBegan).to.be.at.least(
expectedBlocks
);
});
});
}
clickPreviousBlock() {
cy.getByTestId(this.previousBlockBtn).click();
}
clickNextBlock() {
cy.getByTestId(this.nextBlockBtn).click();
}
jumpToBlock(blockNumber: string) {
cy.getByTestId(this.jumpToBlockInput).type(blockNumber);
cy.getByTestId(this.jumpToBlockSubmit).click();
}
verifyPreviousBtnDisabled() {
cy.getByTestId(this.previousBlockBtn).find('button').should('be.disabled');
}
}
@@ -0,0 +1,9 @@
import BasePage from './base-page';
export default class GenesisPage extends BasePage {
genesisHeader = 'genesis-header';
genesisFieldsDisplayed() {
this.validateBlockDataDisplayed(this.genesisHeader);
}
}
@@ -0,0 +1,95 @@
import BasePage from './base-page';
export default class HomePage extends BasePage {
statsEnvironmentTitle = 'stats-environment';
statsTitle = 'stats-title';
statsValue = 'stats-value';
verifyStatsEnvironment() {
const deployedEnv = Cypress.env('environment').toUpperCase();
cy.getByTestId(this.statsEnvironmentTitle).should(
'have.text',
`/ ${deployedEnv}`
);
}
verifyStatsTitlesDisplayed() {
const statTitles = [
'Status',
'Height',
'Validating nodes',
'Uptime',
'Total nodes',
'Inactive nodes',
'Total staked',
'Backlog',
'Trades / second',
'Orders / block',
'Orders / second',
'Transactions / block',
'Block time',
'Time',
'App',
'Tendermint',
'Up since',
'Chain ID',
];
cy.getByTestId(this.statsTitle).then(($list) => {
cy.wrap($list).should('have.length', 18);
});
cy.getByTestId(this.statsTitle)
.each(($title, index) => {
cy.wrap($title).should('have.text', statTitles[index]);
})
.then(($list) => {
cy.wrap($list).should('have.length', 18);
});
}
verifyStatsValuesDisplayed() {
cy.getByTestId(this.statsValue).eq(0).should('have.text', 'CONNECTED');
cy.getByTestId(this.statsValue).eq(1).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(2).should('have.text', '2');
cy.getByTestId(this.statsValue)
.eq(3)
.invoke('text')
.should('match', /\d+d \d+h \d+m \d+s/i);
cy.getByTestId(this.statsValue).eq(4).should('have.text', '2');
cy.getByTestId(this.statsValue).eq(5).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(6).should('have.text', '0.00');
cy.getByTestId(this.statsValue).eq(7).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(8).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(9).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(10).should('have.text', '0');
cy.getByTestId(this.statsValue).eq(11).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(12).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(13).should('not.be.empty');
if (Cypress.env('NIGHTLY_RUN') != true) {
cy.getByTestId(this.statsValue)
.eq(14)
.invoke('text')
.should('match', /v\d+\.\d+\.\d+/i);
}
cy.getByTestId(this.statsValue)
.eq(15)
.invoke('text')
.should('match', /\d+\.\d+\.\d+/i);
cy.getByTestId(this.statsValue).eq(16).should('not.be.empty');
cy.getByTestId(this.statsValue).eq(17).should('not.be.empty');
}
verifyStatsBlockHeightUpdating() {
cy.getByTestId(this.statsValue)
.eq(1)
.invoke('text')
.then((blockHeightTxt) => {
cy.getByTestId(this.statsValue)
.eq(1)
.invoke('text')
.should((newBlockHeightTxt) => {
expect(blockHeightTxt).not.to.eq(newBlockHeightTxt);
});
});
}
}
@@ -0,0 +1,9 @@
import BasePage from './base-page';
export default class MarketsPage extends BasePage {
marketHeaders = 'markets-header';
validateMarketDataDisplayed() {
this.validateBlockDataDisplayed(this.marketHeaders);
}
}
@@ -0,0 +1,39 @@
import BasePage from './base-page';
export default class NetworkParametersPage extends BasePage {
networkParametersHeader = 'network-param-header';
parameters = 'parameters';
jsonParamNameClassName = '.hljs-attr';
jsonParamValueStringClassName = '.hljs-string';
jsonParamValueNumberClassName = '.hljs-number';
parameterKeyValueRow = 'key-value-table-row';
verifyNetworkParametersDisplayed() {
cy.getByTestId(this.networkParametersHeader).should(
'have.text',
'Network Parameters'
);
cy.get(this.jsonParamNameClassName)
.should('have.length.at.least', 18)
.each(($paramName) => {
cy.wrap($paramName).should('not.be.empty');
});
cy.get(this.jsonParamValueStringClassName)
.should('have.length.at.least', 6)
.each(($paramValue) => {
cy.wrap($paramValue).should('not.be.empty');
});
cy.get(this.jsonParamValueNumberClassName)
.should('have.length.at.least', 7)
.each(($paramValue) => {
cy.wrap($paramValue).should('not.be.empty');
});
cy.getByTestId(this.parameterKeyValueRow).each(($row) => {
cy.wrap($row).find('dt').should('not.be.empty');
cy.wrap($row).find('dd').should('not.be.empty');
});
}
}
@@ -0,0 +1,85 @@
import BasePage from './base-page';
export default class TransactionsPage extends BasePage {
transactionsList = 'transactions-list';
transactionRow = 'transaction-row';
blockHeight = 'block-height';
numberOfTransactions = 'num-txs';
validatorLink = 'validator-link';
blockTime = 'block-time';
refreshBtn = 'refresh';
txHash = 'hash';
txSubmittedBy = 'submitted-by';
txBlock = 'block';
txEncodedTnx = 'encoded-tnx';
txType = 'tx-type';
validateTransactionsPagedisplayed() {
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(5000); // Wait for transactions to load if there are any
cy.getByTestId(this.transactionRow).should('have.length.above', 1);
}
validateRefreshBtn() {
cy.intercept('GET', '**/blockchain').as('get-blockchain');
cy.getByTestId(this.refreshBtn).click();
cy.wait('@get-blockchain').its('response.statusCode').should('eq', 200);
}
validateTxDetailsAreDisplayed() {
// TODO fail test when there are no txs once running with Capsule
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.txHash}]`).length) {
cy.getByTestId(this.txHash).invoke('text').should('have.length', 64);
cy.getByTestId(this.txSubmittedBy)
.find('a')
.then(($address) => {
cy.wrap($address).should('have.attr', 'href');
cy.wrap($address).invoke('text').should('have.length', 66);
});
cy.getByTestId(this.txBlock).should('not.be.empty');
cy.getByTestId(this.txEncodedTnx).should('not.be.empty');
cy.getByTestId(this.txType)
.should('not.be.empty')
.invoke('text')
.then((txTypeTxt) => {
if (txTypeTxt == 'Order Submission') {
cy.get('.hljs-attr')
.should('have.length.at.least', 8)
.each(($propertyName) => {
cy.wrap($propertyName).should('not.be.empty');
});
cy.get('.hljs-string')
.should('have.length.at.least', 8)
.each(($propertyValue) => {
cy.wrap($propertyValue).should('not.be.empty');
});
}
});
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
clickOnTopTransaction() {
// TODO fail test when there are no txs once running with Capsule
// eslint-disable-next-line cypress/no-unnecessary-waiting
cy.wait(1000); // Wait for transactions to load if there are any
cy.get('body').then(($body) => {
if ($body.find(`[data-testid=${this.transactionRow}]`).length) {
cy.getByTestId(this.transactionRow)
.first()
.find('a')
.first()
.click({ force: true });
} else {
cy.slack('Unable to find any transactions on page');
cy.screenshot();
}
});
}
}
@@ -0,0 +1,18 @@
import BasePage from './base-page';
export default class ValidatorPage extends BasePage {
tendermintHeader = 'tendermint-header';
vegaHeader = 'vega-header';
tendermintData = 'tendermint-data';
vegaData = 'vega-data';
validateValidatorsDisplayed() {
cy.getByTestId(this.tendermintHeader).should(
'have.text',
'Tendermint data'
);
cy.getByTestId(this.tendermintData).should('not.be.empty');
cy.getByTestId(this.vegaHeader).should('have.text', 'Vega data');
cy.getByTestId(this.vegaData).should('not.be.empty');
}
}
@@ -0,0 +1,12 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import AssetsPage from '../pages/assets-page';
const assetPage = new AssetsPage();
When('I navigate to the asset page', () => {
assetPage.navigateToAssets();
});
Then('asset page is correctly displayed', () => {
assetPage.validateAssetsDisplayed();
});
@@ -0,0 +1,50 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import BlocksPage from '../pages/blocks-page';
const blocksPage = new BlocksPage();
When('I navigate to the blocks page', () => {
blocksPage.navigateToBlocks();
});
When('I click on top block', () => {
blocksPage.clickOnTopBlockHeight();
});
When('jump to first block', () => {
blocksPage.jumpToBlock('1');
});
Then('blocks page is correctly displayed', () => {
blocksPage.validateBlocksPageDisplayed();
});
Then('validator block page is correctly displayed', () => {
blocksPage.validateBlockValidatorPage();
});
Then('I am on the previous block when I click previous', () => {
blocksPage.navigateToPreviousBlock();
});
Then('previous button is disabled', () => {
blocksPage.verifyPreviousBtnDisabled();
});
Then('I am on the second block when I click next', () => {
blocksPage.navigateToNextBlock();
});
Then('I scroll down to the last block on the page', () => {
blocksPage.navigateToLastBlockOnPage();
});
Then(
'I can expect to see at least {int} blocks if i scroll {int} times',
(expectedBlocks, scrollAttempts) => {
blocksPage.navigateToOlderBlocksWithInfiniteScroll(
expectedBlocks,
scrollAttempts
);
}
);
@@ -0,0 +1,26 @@
import { Given, Then, When } from 'cypress-cucumber-preprocessor/steps';
import BasePage from '../pages/base-page';
const basePage = new BasePage();
Given('I am on mobile and open the toggle menu', () => {
cy.viewport('iphone-x');
cy.visit('/');
basePage.clickOnToggle();
});
Given('I am on the homepage', () => {
cy.visit('/');
});
When('I search for {string}', (searchText) => {
basePage.search(searchText);
basePage.clickSearch();
});
Then('I am redirected to page containing id {string}', (expectedUrl) => {
basePage.validateUrl(expectedUrl);
});
Then('search error message {string} is displayed', (expectedErrorMsg) => {
basePage.validateSearchErrorDisplayed(expectedErrorMsg);
});
@@ -0,0 +1,11 @@
import { Then } from 'cypress-cucumber-preprocessor/steps';
import HomePage from '../pages/home-page';
const homePage = new HomePage();
Then('the stats for deployed environment are correctly displayed', () => {
homePage.verifyStatsEnvironment();
homePage.verifyStatsTitlesDisplayed();
homePage.verifyStatsValuesDisplayed();
homePage.verifyStatsBlockHeightUpdating();
});
@@ -0,0 +1,12 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import MarketsPage from '../pages/markets-page';
const marketsPage = new MarketsPage();
When('I navigate to the markets page', () => {
marketsPage.navigateToMarkets();
});
Then('markets page is correctly displayed', () => {
marketsPage.validateMarketDataDisplayed();
});
@@ -0,0 +1,11 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import NetworkPage from '../pages/network-page';
const networkPage = new NetworkPage();
When('I navigate to network parameters page', () => {
networkPage.navigateToNetworkParameters();
});
Then('network parameters page is correctly displayed', () => {
networkPage.verifyNetworkParametersDisplayed();
});
@@ -0,0 +1,30 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import TransactionsPage from '../pages/transactions-page';
const transactionsPage = new TransactionsPage();
When('I navigate to the transactions page', () => {
transactionsPage.navigateToTxs();
});
When('I navigate to the blocks page', () => {
transactionsPage.navigateToBlocks();
});
When('I click on the top transaction', () => {
transactionsPage.clickOnTopTransaction();
});
Then('transactions page is correctly displayed', () => {
transactionsPage.validateTransactionsPagedisplayed();
transactionsPage.validateRefreshBtn();
});
Then('blocks page is correctly displayed', () => {
transactionsPage.validateTransactionsPagedisplayed();
transactionsPage.validateRefreshBtn();
});
Then('transaction details are displayed', () => {
transactionsPage.validateTxDetailsAreDisplayed();
});
@@ -0,0 +1,12 @@
import { Then, When } from 'cypress-cucumber-preprocessor/steps';
import ValidatorPage from '../pages/validators-page';
const validatorsPage = new ValidatorPage();
When('I navigate to the validators page', () => {
validatorsPage.navigateToValidators();
});
Then('validators page is correctly displayed', () => {
validatorsPage.validateValidatorsDisplayed();
});
+1 -2
View File
@@ -1,10 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"sourceMap": false,
"outDir": "../../dist/out-tsc",
"types": ["cypress", "node", "@cypress/grep"],
"types": ["cypress", "node"],
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
+15 -10
View File
@@ -1,13 +1,19 @@
NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_ENV=STAGNET3
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_ENV=CUSTOM
NX_VEGA_REST=http://localhost:3029
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://lb.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_VEGA_ENV=TESTNET
NX_VEGA_REST=https://lb.testnet.vega.xyz/datanode/rest
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
CYPRESS_VEGA_TENDERMINT_URL=https://lb.testnet.vega.xyz/tm
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
# App flags
NX_EXPLORER_ASSETS=1
@@ -17,5 +23,4 @@ NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=1
+5 -4
View File
@@ -1,18 +1,19 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=http://localhost:26617
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_URL=http://localhost:3028/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=CUSTOM
NX_VEGA_REST=http://localhost:3029
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_MARKETS=1
NX_EXPLORER_ORACLES=1
NX_EXPLORER_TXS_LIST=0
NX_EXPLORER_TXS_LIST=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
+4 -3
View File
@@ -1,9 +1,10 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://n04.d.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n04.d.vega.xyz/tm/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/devnet-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_URL=https://n04.d.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=DEVNET
NX_VEGA_REST=https://n04.d.vega.xyz/datanode/rest
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.devnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
+6 -5
View File
@@ -1,9 +1,10 @@
# App configuration variables
NX_TENDERMINT_URL=https://be.explorer.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.explorer.vega.xyz
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mainnet-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_URL=https://api.token.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=MAINNET
NX_VEGA_REST=https://api.token.vega.xyz/
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest/
NX_ETHERSCAN_URL=https://etherscan.io
-20
View File
@@ -1,20 +0,0 @@
# App configuration variables
NX_TENDERMINT_URL=https://tm.be.mainnet-mirror.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=https://tm.be.mainnet-mirror.vega.xyz
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/mirror-network.json
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","STAGNET3":"https://stagnet3.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
NX_VEGA_ENV=MIRROR
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.mainnet-mirror.vega.xyz/rest/
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
# App flags
NX_EXPLORER_ASSETS=1
NX_EXPLORER_GENESIS=1
NX_EXPLORER_GOVERNANCE=1
NX_EXPLORER_NETWORK_PARAMETERS=1
NX_EXPLORER_PARTIES=1
NX_EXPLORER_VALIDATORS=1
NX_EXPLORER_MARKETS=0
NX_EXPLORER_ORACLES=0
NX_EXPLORER_TXS_LIST=1
-11
View File
@@ -1,11 +0,0 @@
# App configuration variables
NX_VEGA_ENV=SANDBOX
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/sandbox-network.json
NX_VEGA_EXPLORER_URL=https://sandbox.explorer.vega.xyz
NX_VEGA_DOCS_URL=https://docs.vega.xyz/testnet
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_TENDERMINT_URL=https://tm.n01.sandbox.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.sandbox.vega.xyz/websocket
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
+9 -12
View File
@@ -1,13 +1,10 @@
NX_ETHEREUM_PROVIDER_URL=https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://n03.s.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n03.s.vega.xyz/tm/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet1-network.json
NX_VEGA_ENV=STAGNET1
NX_VEGA_EXPLORER_URL=https://stagnet1.explorer.vega.xyz
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_TOKEN_URL=https://stagnet1.token.vega.xyz
NX_VEGA_WALLET_URL=http://localhost:1789
NX_TENDERMINT_URL=https://tm.n01.stagnet1.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n01.stagnet1.vega.xyz/websocket
NX_BLOCK_EXPLORER=https://be.stagnet1.vega.xyz/rest
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_URL=https://n03.s.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=STAGNET
NX_VEGA_REST=https://n03.s.vega.xyz/datanode/rest
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
+10
View File
@@ -0,0 +1,10 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://n03.stagnet2.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://n03.stagnet2.vega.xyz/tm/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet2-network.json
NX_VEGA_URL=https://n03.stagnet2.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=STAGNET2
NX_VEGA_REST=https://n01.stagnet2.vega.xyz/datanode/rest
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
-7
View File
@@ -1,7 +0,0 @@
NX_TENDERMINT_URL=https://tm.n00.stagnet3.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.n00.stagnet3.vega.xyz/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/stagnet3-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_ENV=STAGNET3
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=https://be.stagnet3.vega.xyz/rest
+6 -8
View File
@@ -1,12 +1,10 @@
# App configuration variables
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
NX_BLOCK_EXPLORER=https://be.testnet.vega.xyz/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://tm.be.testnet.vega.xyz/websocket/
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://lb.testnet.vega.xyz/tm
NX_TENDERMINT_WEBSOCKET_URL=wss://lb.testnet.vega.xyz/tm/websocket
NX_VEGA_CONFIG_URL=https://static.vega.xyz/assets/testnet-network.json
NX_VEGA_NETWORKS={\"MAINNET"\:\"https://explorer.vega.xyz"\,\"TESTNET\":\"https://explorer.fairground.wtf\"}
NX_VEGA_URL=https://lb.testnet.vega.xyz/query
NX_VEGA_NETWORKS='{"TESTNET":"https://explorer.fairground.wtf","MAINNET":"https://explorer.vega.xyz"}'
NX_VEGA_ENV=TESTNET
NX_VEGA_REST=https://lb.testnet.vega.xyz/datanode/rest
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_VEGA_NETWORKS={}
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
+3 -2
View File
@@ -1,7 +1,8 @@
# App configuration variables
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=http://localhost:26607/
NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26607/websocket
NX_VEGA_URL=http://localhost:3003/query
NX_VEGA_ENV=CUSTOM
NX_VEGA_REST=http://localhost:3029/rest
NX_GITHUB_FEEDBACK_URL=https://github.com/vegaprotocol/feedback/discussions
NX_BLOCK_EXPLORER=
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
+2 -5
View File
@@ -1,6 +1,6 @@
{
"extends": ["plugin:@nrwl/nx/react", "../../.eslintrc.json"],
"ignorePatterns": ["!**/*", "__generated__", "__generated___"],
"ignorePatterns": ["!**/*", "__generated__"],
"overrides": [
{
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
@@ -14,8 +14,5 @@
"files": ["*.js", "*.jsx"],
"rules": {}
}
],
"env": {
"jest": true
}
]
}
+19 -16
View File
@@ -35,30 +35,33 @@ Example configurations are provided here:
- [Devnet](./.env.devnet)
- [Capsule](./.env.capsule)
- [Testnet](./.env.testnet)
- [Stagnet3](./.env.stagnet3)
- [Stagnet1](./.env.stagnet1)
- [Stagnet2](./.env.stagnet2)
For convenience, you can boot the app injecting one of the configurations above by running:
```bash
yarn nx run explorer:serve --env={env} # e.g. stagnet3
yarn nx run explorer:serve --env={env} # e.g. stagnet1
```
There are a few different configuration options offered for this app:
| **Flag** | **Purpose** |
| -------------------------------- | ---------------------------------------------------------- | --- |
| `NX_TENDERMINT_URL` | The Tendermint REST URL for the Vega consesus engine |
| `NX_TENDERMINT_WEBSOCKET_URL` | The Tendermint Websocket URL for the Vega consensus engine |
| `NX_VEGA_ENV` | The name of the currently connected vega environment | |
| `NX_EXPLORER_ASSETS` | Enable the assets page for the explorer |
| `NX_EXPLORER_GENESIS` | Enable the genesis page for the explorer |
| `NX_EXPLORER_GOVERNANCE` | Enable the governance page for the explorer |
| `NX_EXPLORER_MARKETS` | Enable the markets page for the explorer |
| `NX_EXPLORER_ORACLES` | Enable the oracles page for the explorer |
| `NX_EXPLORER_TXS_LIST` | Enable the transactions list page for the explorer |
| `NX_EXPLORER_NETWORK_PARAMETERS` | Enable the network parameters page for the explorer |
| `NX_EXPLORER_PARTIES` | Enable the parties page for the explorer |
| `NX_EXPLORER_VALIDATORS` | Enable the validators page for the explorer |
| **Flag** | **Purpose** |
| -------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `NX_CHAIN_EXPLORER_URL` | The URL of the chain explorer service for decoding transactions |
| `NX_TENDERMINT_URL` | The Tendermint REST URL for the Vega consesus engine |
| `NX_TENDERMINT_WEBSOCKET_URL` | The Tendermint Websocket URL for the Vega consensus engine |
| `NX_VEGA_URL` | The GraphQl query endpoint of a [Vega data node](https://github.com/vegaprotocol/networks#data-node) |
| `NX_VEGA_ENV` | The name of the currently connected vega environment |
| `NX_VEGA_REST` | The REST URL for the Vega Data Node |
| `NX_EXPLORER_ASSETS` | Enable the assets page for the explorer |
| `NX_EXPLORER_GENESIS` | Enable the genesis page for the explorer |
| `NX_EXPLORER_GOVERNANCE` | Enable the governance page for the explorer |
| `NX_EXPLORER_MARKETS` | Enable the markets page for the explorer |
| `NX_EXPLORER_TXS_LIST` | Enable the transactions list page for the explorer |
| `NX_EXPLORER_NETWORK_PARAMETERS` | Enable the network parameters page for the explorer |
| `NX_EXPLORER_PARTIES` | Enable the parties page for the explorer |
| `NX_EXPLORER_VALIDATORS` | Enable the validators page for the explorer |
## Testing
@@ -1,6 +1,4 @@
/* eslint-disable */
process.env.TZ = 'UTC';
export default {
module.exports = {
displayName: 'explorer',
preset: '../../jest.preset.js',
transform: {
+4 -19
View File
@@ -1,5 +1,5 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"root": "apps/explorer",
"sourceRoot": "apps/explorer/src",
"projectType": "application",
"targets": {
@@ -41,7 +41,7 @@
"executor": "./tools/executors/webpack:serve",
"options": {
"port": 3000,
"buildTarget": "explorer:build:development",
"buildTarget": "explorer:build",
"hmr": true
},
"configurations": {
@@ -62,33 +62,18 @@
"executor": "@nrwl/jest:jest",
"outputs": ["coverage/apps/explorer"],
"options": {
"jestConfig": "apps/explorer/jest.config.ts",
"jestConfig": "apps/explorer/jest.config.js",
"passWithNoTests": true
}
},
"generate-types": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"npx openapi-typescript https://raw.githubusercontent.com/vegaprotocol/documentation/main/specs/v0.66.1/blockexplorer.openapi.json --output apps/explorer/src/types/explorer.d.ts --immutable-types"
]
}
},
"build-netlify": {
"executor": "@nrwl/workspace:run-commands",
"builder": "@nrwl/workspace:run-commands",
"options": {
"commands": [
"cp apps/explorer/netlify.toml netlify.toml",
"nx build explorer"
]
}
},
"build-spec": {
"executor": "@nrwl/workspace:run-commands",
"outputs": [],
"options": {
"command": "yarn tsc --project ./apps/explorer/tsconfig.spec.json"
}
}
},
"tags": []
+31 -41
View File
@@ -1,15 +1,16 @@
import classnames from 'classnames';
import { useState, useEffect } from 'react';
import { useLocation } from 'react-router-dom';
import { ThemeContext, useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { EnvironmentProvider, NetworkLoader } from '@vegaprotocol/environment';
import { NetworkInfo } from '@vegaprotocol/network-info';
import { createClient } from './lib/apollo-client';
import { Nav } from './components/nav';
import { Header } from './components/header';
import { Main } from './components/main';
import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-websocket-provider';
import type { InMemoryCacheConfig } from '@apollo/client';
import { Footer } from './components/footer/footer';
function App() {
const [theme, toggleTheme] = useThemeSwitcher();
const [menuOpen, setMenuOpen] = useState(false);
const location = useLocation();
@@ -18,45 +19,34 @@ function App() {
setMenuOpen(false);
}, [location]);
const cacheConfig: InMemoryCacheConfig = {
typePolicies: {
statistics: {
keyFields: false,
},
},
};
const layoutClasses = classnames(
'grid grid-rows-[auto_1fr_auto] grid-cols-[1fr] md:grid-rows-[auto_minmax(700px,_1fr)_auto] md:grid-cols-[300px_1fr]',
'min-h-[100vh] mx-auto my-0',
'border-neutral-700 dark:border-neutral-300 lg:border-l lg:border-r',
'bg-white dark:bg-black',
'antialiased text-black dark:text-white',
{
'h-[100vh] min-h-auto overflow-hidden': menuOpen,
}
);
return (
<TendermintWebsocketProvider>
<NetworkLoader cache={cacheConfig}>
<div className={layoutClasses}>
<Header menuOpen={menuOpen} setMenuOpen={setMenuOpen} />
<Nav menuOpen={menuOpen} />
<Main />
<Footer />
</div>
</NetworkLoader>
</TendermintWebsocketProvider>
<EnvironmentProvider>
<ThemeContext.Provider value={theme}>
<TendermintWebsocketProvider>
<NetworkLoader createClient={createClient}>
<div
className={`${
menuOpen && 'h-[100vh] overflow-hidden'
} antialiased m-0 bg-white dark:bg-black text-black dark:text-white`}
>
<div className="min-h-[100vh] max-w-[1300px] grid grid-rows-[repeat(2,_auto)_1fr] grid-cols-[1fr] md:grid-rows-[auto_minmax(700px,_1fr)] md:grid-cols-[300px_1fr] border-black dark:border-white lg:border-l-1 lg:border-r-1 mx-auto">
<Header
toggleTheme={toggleTheme}
menuOpen={menuOpen}
setMenuOpen={setMenuOpen}
/>
<Nav menuOpen={menuOpen} />
<Main />
<footer className="grid grid-rows-2 grid-cols-[1fr_auto] md:flex md:col-span-2 p-16 gap-12 border-t-1">
<NetworkInfo />
</footer>
</div>
</div>
</NetworkLoader>
</TendermintWebsocketProvider>
</ThemeContext.Provider>
</EnvironmentProvider>
);
}
const Wrapper = () => {
return (
<EnvironmentProvider>
<App />
</EnvironmentProvider>
);
};
export default Wrapper;
export default App;
@@ -1,5 +0,0 @@
query ExplorerGovernanceAsset {
networkParameter(key: "reward.asset") {
value
}
}
@@ -1,45 +0,0 @@
import * as Types from '@vegaprotocol/types';
import { gql } from '@apollo/client';
import * as Apollo from '@apollo/client';
const defaultOptions = {} as const;
export type ExplorerGovernanceAssetQueryVariables = Types.Exact<{ [key: string]: never; }>;
export type ExplorerGovernanceAssetQuery = { __typename?: 'Query', networkParameter?: { __typename?: 'NetworkParameter', value: string } | null };
export const ExplorerGovernanceAssetDocument = gql`
query ExplorerGovernanceAsset {
networkParameter(key: "reward.asset") {
value
}
}
`;
/**
* __useExplorerGovernanceAssetQuery__
*
* To run a query within a React component, call `useExplorerGovernanceAssetQuery` and pass it any options that fit your needs.
* When your component renders, `useExplorerGovernanceAssetQuery` 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 } = useExplorerGovernanceAssetQuery({
* variables: {
* },
* });
*/
export function useExplorerGovernanceAssetQuery(baseOptions?: Apollo.QueryHookOptions<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>(ExplorerGovernanceAssetDocument, options);
}
export function useExplorerGovernanceAssetLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useLazyQuery<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>(ExplorerGovernanceAssetDocument, options);
}
export type ExplorerGovernanceAssetQueryHookResult = ReturnType<typeof useExplorerGovernanceAssetQuery>;
export type ExplorerGovernanceAssetLazyQueryHookResult = ReturnType<typeof useExplorerGovernanceAssetLazyQuery>;
export type ExplorerGovernanceAssetQueryResult = Apollo.QueryResult<ExplorerGovernanceAssetQuery, ExplorerGovernanceAssetQueryVariables>;
@@ -1,39 +0,0 @@
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
import { AssetLink } from '../links';
import { useExplorerAssetQuery } from '../links/asset-link/__generated__/Asset';
export type AssetBalanceProps = {
assetId: string;
price: string;
showAssetLink?: boolean;
};
/**
* Given a market ID and a price it will fetch the market
* and format the price in that market's decimal places.
*/
const AssetBalance = ({
assetId,
price,
showAssetLink = true,
}: AssetBalanceProps) => {
const { data } = useExplorerAssetQuery({
variables: { id: assetId },
});
const label =
data && data.asset?.decimals
? addDecimalsFormatNumber(price, data.asset.decimals)
: price;
return (
<div className="inline-block">
<span>{label}</span>{' '}
{showAssetLink && data?.asset?.id ? (
<AssetLink id={data.asset.id} />
) : null}
</div>
);
};
export default AssetBalance;
@@ -1,30 +0,0 @@
import { addDecimalsFormatNumber } from '@vegaprotocol/react-helpers';
import { useExplorerGovernanceAssetQuery } from './__generated__/Governance-asset';
import AssetBalance from './asset-balance';
export type GovernanceAssetBalanceProps = {
price: string;
};
const DEFAULT_DECIMALS = 18;
/**
* Effectively a wrapper around AssetBalance that does an extra query to fetch
* the governance asset first, which is set by a network parameter
*/
const GovernanceAssetBalance = ({ price }: GovernanceAssetBalanceProps) => {
const { data } = useExplorerGovernanceAssetQuery();
if (data && data.networkParameter?.value) {
const governanceAssetId = data.networkParameter.value;
return <AssetBalance price={price} assetId={governanceAssetId} />;
} else {
return (
<div className="inline-block">
<span>{addDecimalsFormatNumber(price, DEFAULT_DECIMALS)}</span>
</div>
);
}
};
export default GovernanceAssetBalance;
@@ -2,9 +2,7 @@ import React from 'react';
import type { BlockMeta } from '../../routes/blocks/tendermint-blockchain-response';
import { Routes } from '../../routes/route-names';
import { Link } from 'react-router-dom';
import { getDateTimeFormat } from '@vegaprotocol/react-helpers';
import { Tooltip } from '@vegaprotocol/ui-toolkit';
import { TimeAgo } from '../time-ago';
import { SecondsAgo } from '../seconds-ago';
import { TableWithTbody, TableRow, TableCell } from '../table';
import { t } from '@vegaprotocol/react-helpers';
@@ -22,7 +20,7 @@ export const BlockData = ({ block, className }: BlockProps) => {
<TableRow data-testid="block-row" modifier="background">
<TableCell
data-testid="block-height"
className="p-0.5 font-mono"
className="pl-4 py-2 font-mono"
aria-label={t('Block height')}
>
<Link
@@ -34,7 +32,7 @@ export const BlockData = ({ block, className }: BlockProps) => {
</TableCell>
<TableCell
data-testid="num-txs"
className="text-center"
className="px-8 text-center"
aria-label={t('Number of transactions')}
>
{block.num_txs === '1'
@@ -43,7 +41,7 @@ export const BlockData = ({ block, className }: BlockProps) => {
</TableCell>
<TableCell
data-testid="validator-link"
className="text-center font-mono"
className="px-8 text-center font-mono"
aria-label={t('Validator')}
>
<Link to={`/${Routes.VALIDATORS}`}>
@@ -52,19 +50,10 @@ export const BlockData = ({ block, className }: BlockProps) => {
</TableCell>
<TableCell
data-testid="block-time"
className="text-center pr-6 w-[170px]"
className="text-center pr-28 text-neutral-300 w-[170px]"
aria-label={t('Block genesis')}
>
<Tooltip
description={getDateTimeFormat().format(
new Date(block.header.time)
)}
>
{/*For some reason we get forwardRef errors if we pass in the TimeAgo component directly*/}
<span>
<TimeAgo date={block.header.time} />
</span>
</Tooltip>
<SecondsAgo date={block.header?.time} />
</TableCell>
</TableRow>
</TableWithTbody>

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