fix: alignment of 'Add New' button labels. #6843
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-License-Identifier: Apache-2.0 | |
| name: Test Frontend | |
| on: | |
| push: | |
| branches: | |
| - main | |
| - release/** | |
| pull_request: | |
| branches: | |
| - main | |
| - release/** | |
| workflow_dispatch: | |
| defaults: | |
| run: | |
| shell: bash | |
| env: | |
| LC_ALL: C.UTF-8 | |
| permissions: | |
| contents: read | |
| concurrency: | |
| group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| name: Build | Front-end | |
| runs-on: transaction-tools-linux-medium | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Install pnpm | |
| uses: step-security/action-setup@7734f5522f614a4cf2820ad19aa26717c7abfd96 # v6.0.9 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| node-version-file: package.json | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Build Linux executable | |
| run: pnpm run build:linux:e2e | |
| working-directory: front-end | |
| - name: Cache Build Artifacts | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: front-end/release/linux-unpacked | |
| key: build-artifacts-${{ github.sha }} | |
| test: | |
| name: Automation | ${{ matrix.test-suite.name }} | |
| runs-on: transaction-tools-linux-medium | |
| needs: build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| test-suite: | |
| - name: Local Basic | |
| grep: '@local-basic' | |
| soloRequired: false | |
| backendRequired: false | |
| sharedEnvironment: true | |
| workers: 2 | |
| - name: Shared E2E | |
| grep: '@local-transactions|@organization-basic|@organization-advanced' | |
| soloRequired: true | |
| backendRequired: true | |
| sharedEnvironment: true | |
| workers: 8 | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Install pnpm | |
| uses: step-security/action-setup@7734f5522f614a4cf2820ad19aa26717c7abfd96 # v6.0.9 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| node-version-file: package.json | |
| - name: Install Docker Compose Plugin | |
| if: ${{ matrix.test-suite.backendRequired }} | |
| run: | | |
| set -euo pipefail | |
| if docker compose version >/dev/null 2>&1; then | |
| docker compose version | |
| exit 0 | |
| fi | |
| sudo install -d /usr/local/lib/docker/cli-plugins | |
| sudo curl -fLo /usr/local/lib/docker/cli-plugins/docker-compose \ | |
| https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-linux-x86_64 | |
| sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose | |
| docker compose version | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Setup Kind | |
| if: ${{ matrix.test-suite.soloRequired }} | |
| uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0 | |
| with: | |
| install_only: true | |
| node_image: kindest/node:v1.31.4@sha256:2cb39f7295fe7eafee0842b1052a599a4fb0f8bcf3f83d96c7f4864c357c6c30 | |
| version: v0.26.0 | |
| kubectl_version: v1.31.4 | |
| verbosity: 3 | |
| wait: 120s | |
| - name: Install Solo CLI | |
| if: ${{ matrix.test-suite.soloRequired }} | |
| run: | | |
| set -euo pipefail | |
| npm install -g @hashgraph/solo@0.64.2 | |
| solo --version | |
| kind --version | |
| - name: Deploy Solo | |
| if: ${{ matrix.test-suite.soloRequired }} | |
| id: solo_deploy | |
| env: | |
| SOLO_CLUSTER_NAME: solo | |
| SOLO_NAMESPACE: solo | |
| SOLO_CLUSTER_SETUP_NAMESPACE: solo-cluster | |
| SOLO_DEPLOYMENT: solo-deployment | |
| run: | | |
| set -euo pipefail | |
| kind create cluster -n "${SOLO_CLUSTER_NAME}" | |
| solo one-shot single deploy | tee solo-deploy.log | |
| - name: Check port forwarding | |
| if: ${{ matrix.test-suite.soloRequired }} | |
| run: | | |
| set -euo pipefail | |
| wait_for_tcp() { | |
| local host="$1" | |
| local port="$2" | |
| timeout 1 bash -c ">/dev/tcp/${host}/${port}" >/dev/null 2>&1 | |
| } | |
| ps -ef | grep port-forward || true | |
| for attempt in $(seq 1 60); do | |
| if curl -fsS http://localhost:8080/api/v1/network/nodes >/dev/null 2>&1 && \ | |
| curl -fsS http://localhost:8081/api/v1/network/nodes >/dev/null 2>&1 && \ | |
| wait_for_tcp 127.0.0.1 50211; then | |
| echo "Solo port forwarding is ready on attempt ${attempt}" | |
| break | |
| fi | |
| if [ "${attempt}" -eq 60 ]; then | |
| echo "Solo port forwarding did not become ready in time" >&2 | |
| ps -ef | grep port-forward || true | |
| kubectl get pods -A || true | |
| exit 1 | |
| fi | |
| echo "Waiting for Solo port forwarding... attempt ${attempt}/60" | |
| sleep 5 | |
| done | |
| curl -fsS http://localhost:8080/api/v1/network/nodes | |
| curl -fsS http://localhost:8081/api/v1/network/nodes | |
| wait_for_tcp 127.0.0.1 50211 | |
| - name: Deploy Back-end | |
| if: ${{ matrix.test-suite.backendRequired }} | |
| working-directory: back-end | |
| env: | |
| BACKEND_COMPOSE_FILE: docker-compose.yaml | |
| run: | | |
| set -euo pipefail | |
| wait_for_tcp() { | |
| local host="$1" | |
| local port="$2" | |
| timeout 1 bash -c ">/dev/tcp/${host}/${port}" >/dev/null 2>&1 | |
| } | |
| # Install Linux packages required for mkcert and host-to-container port proxies. | |
| sudo apt-get update -q | |
| sudo apt-get install -y --no-install-recommends libnss3-tools socat | |
| if ! command -v mkcert >/dev/null 2>&1; then | |
| curl -fsSL https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64 \ | |
| -o /tmp/mkcert | |
| sudo install -m755 /tmp/mkcert /usr/local/bin/mkcert | |
| fi | |
| mkcert -install | |
| mkdir -p cert | |
| mkcert -key-file cert/key.pem -cert-file cert/cert.pem localhost | |
| for dir in . apps/api apps/chain apps/notifications typeorm scripts; do | |
| cp "${dir}/example.env" "${dir}/.env" | |
| done | |
| FE_VERSION="$(node -p "require('../front-end/package.json').version")" | |
| sed -i "s/^LATEST_SUPPORTED_FRONTEND_VERSION=.*/LATEST_SUPPORTED_FRONTEND_VERSION=${FE_VERSION}/" apps/api/.env | |
| sed -i "s/^MINIMUM_SUPPORTED_FRONTEND_VERSION=.*/MINIMUM_SUPPORTED_FRONTEND_VERSION=${FE_VERSION}/" apps/api/.env | |
| sed -i "s/^MINIMUM_SUPPORTED_FRONTEND_VERSION=.*/MINIMUM_SUPPORTED_FRONTEND_VERSION=${FE_VERSION}/" apps/notifications/.env | |
| sed -i 's/^EMAIL_API_SECURE=.*/EMAIL_API_SECURE=false/' apps/notifications/.env | |
| sed -i 's/^ANONYMOUS_MINUTE_LIMIT=.*/ANONYMOUS_MINUTE_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^ANONYMOUS_FIVE_SECOND_LIMIT=.*/ANONYMOUS_FIVE_SECOND_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^GLOBAL_MINUTE_LIMIT=.*/GLOBAL_MINUTE_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^GLOBAL_SECOND_LIMIT=.*/GLOBAL_SECOND_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^LOGIN_IP_MINUTE_LIMIT=.*/LOGIN_IP_MINUTE_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^LOGIN_IP_TEN_SECOND_LIMIT=.*/LOGIN_IP_TEN_SECOND_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^RESET_IP_MINUTE_LIMIT=.*/RESET_IP_MINUTE_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^RESET_IP_TEN_SECOND_LIMIT=.*/RESET_IP_TEN_SECOND_LIMIT=99999/' apps/api/.env | |
| sed -i 's/^RESET_IP_UNIQUE_EMAIL_LIMIT=.*/RESET_IP_UNIQUE_EMAIL_LIMIT=99999/' apps/api/.env | |
| export COMPOSE_PARALLEL_LIMIT=4 | |
| if ! docker info &>/dev/null; then | |
| echo "Docker daemon is not reachable; attempting to start it..." | |
| sudo service docker start || true | |
| sudo systemctl start docker || true | |
| fi | |
| for i in $(seq 1 15); do | |
| if docker info &>/dev/null; then | |
| break | |
| fi | |
| if [ "${i}" -eq 15 ]; then | |
| echo "Docker daemon is unavailable after startup attempts" >&2 | |
| ls -l /var/run/docker.sock || true | |
| sudo ls -l /var/run/docker.sock || true | |
| ps -ef | grep -E 'dockerd|containerd' | grep -v grep || true | |
| exit 1 | |
| fi | |
| sleep 2 | |
| done | |
| for attempt in $(seq 1 60); do | |
| if curl -fsS http://localhost:8081/api/v1/network/nodes >/dev/null 2>&1 && \ | |
| wait_for_tcp 127.0.0.1 50211; then | |
| echo "Solo network endpoints are ready on attempt ${attempt}" | |
| break | |
| fi | |
| if [ "${attempt}" -eq 60 ]; then | |
| echo "Solo network endpoints did not become ready in time" >&2 | |
| exit 1 | |
| fi | |
| echo "Waiting for Solo network endpoints... attempt ${attempt}/60" | |
| sleep 5 | |
| done | |
| DOCKER_HOST_GATEWAY="$(docker network inspect bridge --format '{{(index .IPAM.Config 0).Gateway}}')" | |
| if [ -z "${DOCKER_HOST_GATEWAY}" ]; then | |
| echo "Failed to resolve the Docker bridge gateway IP" >&2 | |
| exit 1 | |
| fi | |
| echo "Docker bridge gateway: ${DOCKER_HOST_GATEWAY}" | |
| # Solo binds its localnet services to localhost on the runner. | |
| # Docker containers reach the host via the bridge gateway, so expose the ports there first. | |
| for port in 50211 8081; do | |
| sudo bash -c "nohup socat TCP-LISTEN:${port},bind=${DOCKER_HOST_GATEWAY},fork,reuseaddr TCP:127.0.0.1:${port} >/tmp/socat-${port}.log 2>&1 &" | |
| done | |
| sleep 2 | |
| if ! curl -fsS "http://${DOCKER_HOST_GATEWAY}:8081/api/v1/network/nodes" >/dev/null 2>&1 || \ | |
| ! wait_for_tcp "${DOCKER_HOST_GATEWAY}" 50211; then | |
| echo "One or more Solo port proxies are not reachable on ${DOCKER_HOST_GATEWAY}" >&2 | |
| ss -lnt || true | |
| sudo tail -n 50 /tmp/socat-50211.log || true | |
| sudo tail -n 50 /tmp/socat-8081.log || true | |
| exit 1 | |
| fi | |
| for service in redis migration api chain notifications; do | |
| for attempt in $(seq 1 3); do | |
| if docker compose -f "${BACKEND_COMPOSE_FILE}" build --progress plain "${service}"; then | |
| break | |
| fi | |
| if [ "${attempt}" -eq 3 ]; then | |
| echo "docker compose build failed for ${service} after 3 attempts" >&2 | |
| exit 1 | |
| fi | |
| echo "Build for ${service} failed on attempt ${attempt}; retrying in 15s..." | |
| sleep 15 | |
| done | |
| done | |
| for attempt in $(seq 1 5); do | |
| if docker compose -f "${BACKEND_COMPOSE_FILE}" up -d --no-build api chain notifications; then | |
| break | |
| fi | |
| if [ "${attempt}" -eq 5 ]; then | |
| echo "docker compose up failed after 5 attempts" >&2 | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" ps || true | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=200 || true | |
| exit 1 | |
| fi | |
| echo "Attempt ${attempt} failed; retrying in 15s..." | |
| sleep 15 | |
| done | |
| # Wait for the API to become ready. | |
| # The API serves HTTPS when NODE_ENV=development and cert files are present. | |
| MAX_ATTEMPTS=60 | |
| for attempt in $(seq 1 "${MAX_ATTEMPTS}"); do | |
| code="$(curl -k -s -o /dev/null -w '%{http_code}' https://localhost:3001/ 2>/dev/null || echo '000')" | |
| if [[ "${code}" == "200" || "${code}" == "426" ]]; then | |
| echo "Back-end is ready (attempt ${attempt})" | |
| break | |
| fi | |
| if (( attempt % 10 == 0 )); then | |
| echo "Waiting for back-end... attempt ${attempt}/${MAX_ATTEMPTS} (HTTP ${code})" | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" ps | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=50 api || true | |
| fi | |
| if [ "${attempt}" -eq "${MAX_ATTEMPTS}" ]; then | |
| echo "Back-end did not become ready in time" >&2 | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" ps || true | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=300 || true | |
| exit 1 | |
| fi | |
| sleep 5 | |
| done | |
| docker compose -f "${BACKEND_COMPOSE_FILE}" ps | |
| - name: Test API connectivity | |
| if: ${{ matrix.test-suite.backendRequired }} | |
| run: | | |
| set -euo pipefail | |
| code="$(curl -k -sS -o /dev/null -w '%{http_code}' https://localhost:3001/ || echo '000')" | |
| echo "API connectivity check returned HTTP ${code}" | |
| if [[ "${code}" != "200" && "${code}" != "426" ]]; then | |
| echo "Unexpected HTTP status from API: ${code}" >&2 | |
| exit 1 | |
| fi | |
| - name: Restore Build Artifacts | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| fail-on-cache-miss: true | |
| path: front-end/release/linux-unpacked | |
| key: build-artifacts-${{ github.sha }} | |
| - name: Install xvfb | |
| run: sudo apt-get update && sudo apt-get install -y xvfb | |
| - name: Install Runtime Shared Libraries | |
| run: sudo apt-get install --no-install-recommends -y xorg openbox libnss3 libasound2 libatk-adaptor libgtk-3-0 | |
| - name: Run Automation | |
| env: | |
| EXECUTABLE_PATH: ../front-end/release/linux-unpacked/hedera-transaction-tool | |
| ENVIRONMENT: LOCALNET | |
| PLAYWRIGHT_SHARED_ENV: ${{ matrix.test-suite.sharedEnvironment && 'true' || 'false' }} | |
| PLAYWRIGHT_WORKERS: ${{ matrix.test-suite.workers }} | |
| ORGANIZATION_URL: ${{ matrix.test-suite.backendRequired && 'https://localhost:3001' || '' }} | |
| POSTGRES_HOST: ${{ matrix.test-suite.backendRequired && 'localhost' || '' }} | |
| POSTGRES_PORT: ${{ matrix.test-suite.backendRequired && '5432' || '' }} | |
| POSTGRES_DATABASE: ${{ matrix.test-suite.backendRequired && 'postgres' || '' }} | |
| POSTGRES_USERNAME: ${{ matrix.test-suite.backendRequired && 'postgres' || '' }} | |
| POSTGRES_PASSWORD: ${{ matrix.test-suite.backendRequired && 'postgres' || '' }} | |
| run: xvfb-run -a npx playwright test --grep "${{ matrix.test-suite.grep }}" | |
| working-directory: automation | |
| - name: Upload Playwright Report | |
| if: ${{ always() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: playwright-report-${{ matrix.test-suite.name }} | |
| path: | | |
| automation/reports/playwright | |
| automation/reports/playwright-json | |
| if-no-files-found: warn | |
| retention-days: 3 | |
| - name: Upload Playwright Test Results | |
| if: ${{ always() }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: playwright-test-results-${{ matrix.test-suite.name }} | |
| path: automation/test-results | |
| if-no-files-found: warn | |
| retention-days: 3 | |
| - name: Show Back-end Logs | |
| if: ${{ failure() && matrix.test-suite.backendRequired }} | |
| env: | |
| BACKEND_COMPOSE_FILE: docker-compose.yaml | |
| run: docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=300 | |
| working-directory: back-end | |
| - name: Collect Solo Diagnostics | |
| if: ${{ always() && matrix.test-suite.soloRequired }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p solo-diagnostics | |
| kubectl get pods -A -o wide > solo-diagnostics/pods.txt || true | |
| kubectl get events -A --sort-by=.lastTimestamp > solo-diagnostics/events.txt || true | |
| kubectl describe pods -A > solo-diagnostics/pod-describe.txt || true | |
| kubectl logs -A --all-containers --tail=500 > solo-diagnostics/all-pod-logs.txt || true | |
| cp solo-deploy.log solo-diagnostics/solo-deploy.log || true | |
| cp /tmp/socat-*.log solo-diagnostics/ 2>/dev/null || true | |
| - name: Upload Solo Diagnostics | |
| if: ${{ always() && matrix.test-suite.soloRequired }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: solo-diagnostics-${{ matrix.test-suite.name }} | |
| path: solo-diagnostics | |
| if-no-files-found: warn | |
| retention-days: 3 | |
| - name: Export Kind Logs | |
| if: ${{ always() && matrix.test-suite.soloRequired }} | |
| run: kind export logs kind-logs --name solo || true | |
| - name: Upload Kind Logs | |
| if: ${{ failure() && matrix.test-suite.soloRequired }} | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: kind-logs-${{ matrix.test-suite.name }} | |
| path: kind-logs | |
| if-no-files-found: warn | |
| retention-days: 3 | |
| - name: Stop Back-end Services | |
| if: ${{ always() && matrix.test-suite.backendRequired }} | |
| env: | |
| BACKEND_COMPOSE_FILE: docker-compose.yaml | |
| run: docker compose -f "${BACKEND_COMPOSE_FILE}" down --volumes --remove-orphans || true | |
| working-directory: back-end | |
| cleanup: | |
| name: Cleanup | Build Cache | |
| runs-on: transaction-tools-linux-medium | |
| needs: [test] | |
| permissions: | |
| actions: write | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 | |
| with: | |
| egress-policy: audit | |
| - name: Delete Build Artifacts Cache | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| if ! gh api --method DELETE \ | |
| "/repos/${{ github.repository }}/actions/caches?key=build-artifacts-${{ github.sha }}&ref=${{ github.ref }}"; then | |
| echo "::warning::Failed to delete Actions cache build-artifacts-${{ github.sha }}" | |
| fi | |
| unit-test: | |
| name: Unit Tests | ${{ matrix.test-suite.name }} | |
| runs-on: transaction-tools-linux-medium | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| test-suite: | |
| - name: Main Process | |
| command: main:coverage | |
| - name: Renderer Process | |
| command: renderer | |
| - name: Shared | |
| command: shared | |
| steps: | |
| - name: Harden Runner | |
| uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout Code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| - name: Install pnpm | |
| uses: step-security/action-setup@7734f5522f614a4cf2820ad19aa26717c7abfd96 # v6.0.9 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 | |
| with: | |
| cache: pnpm | |
| cache-dependency-path: pnpm-lock.yaml | |
| node-version-file: package.json | |
| - name: Install Dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Run Unit Tests | |
| run: pnpm run test:${{ matrix.test-suite.command }} | |
| working-directory: front-end | |
| - name: Upload coverage to Codecov | |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 | |
| with: | |
| token: ${{ secrets.CODECOV_TOKEN }} |