Skip to content

TS End-to-end Tests #10

TS End-to-end Tests

TS End-to-end Tests #10

Workflow file for this run

name: TS End-to-end Tests
on:
workflow_dispatch:
inputs:
isNightly:
description: Nightly (runs all groups)
type: boolean
default: false
isRpc:
description: RPC
type: boolean
default: false
isExplorer:
description: Explorer
type: boolean
default: false
isAppsBackend:
description: Apps Backend
type: boolean
default: false
isTypescriptSDK:
description: TypeScript SDK
type: boolean
default: false
isWallet:
description: Wallet
type: boolean
default: false
isGraphQlTransport:
description: GraphQL Transport
type: boolean
default: false
isWalletDashboard:
description: Wallet Dashboard
type: boolean
default: false
isEvmBridge:
description: EVM Bridge
type: boolean
default: false
isIscSDK:
description: ISC SDK
type: boolean
default: false
isKiosk:
description: Kiosk
type: boolean
default: false
isNamesDapp:
description: Names Dapp
type: boolean
default: false
isNamesDisplay:
description: Names Display
type: boolean
default: false
isNamesSDK:
description: Names SDK
type: boolean
default: false
workflow_call:
inputs:
isNightly:
type: boolean
required: true
isRpc:
type: boolean
required: true
isExplorer:
type: boolean
required: true
isAppsBackend:
type: boolean
required: true
isTypescriptSDK:
type: boolean
required: true
isWallet:
type: boolean
required: true
isGraphQlTransport:
type: boolean
required: true
isWalletDashboard:
type: boolean
required: true
isEvmBridge:
type: boolean
required: true
isIscSDK:
type: boolean
required: true
isKiosk:
type: boolean
required: true
isNamesDapp:
type: boolean
required: true
isNamesDisplay:
type: boolean
required: true
isNamesSDK:
type: boolean
required: true
secrets:
EVM_BRIDGE_PREVIEW_DEFAULT_NETWORK:
required: true
EVM_BRIDGE_CONFIG:
required: true
concurrency:
group: e2e-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/develop' }}
env:
RUST_LOG: warn
RUST_BACKTRACE: 1
VITE_EVM_BRIDGE_DEFAULT_NETWORK: ${{ secrets.EVM_BRIDGE_PREVIEW_DEFAULT_NETWORK }}
VITE_EVM_BRIDGE_CONFIG: ${{ secrets.EVM_BRIDGE_CONFIG }}
jobs:
localnet:
name: Localnet
if: inputs.isNightly || inputs.isRpc || inputs.isExplorer || inputs.isAppsBackend || inputs.isTypescriptSDK || inputs.isWallet || inputs.isGraphQlTransport || inputs.isWalletDashboard || inputs.isEvmBridge || inputs.isIscSDK || inputs.isNamesDapp || inputs.isNamesDisplay || inputs.isNamesSDK
runs-on: self-hosted-x64
services:
postgres:
image: postgres:15
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgrespw
POSTGRES_DB: iota_indexer
POSTGRES_HOST_AUTH_METHOD: trust
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
command: postgres -c max_connections=1000
steps:
- uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
with:
submodules: true
- uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
# ─── Foundational setup ──────────────────────────────────────────────────
# These steps run without continue-on-error, and none of the subsequent steps have "if failure()" or "if always()"
# so any failure sets the job to "failure" state, rendering all subsequent steps to skip.
- name: Calculate common conditions
id: conditions
run: |
echo "runExplorer=${{ inputs.isNightly || inputs.isTypescriptSDK || inputs.isExplorer || inputs.isRpc }}" >> $GITHUB_OUTPUT
echo "runDashboard=${{ inputs.isNightly || inputs.isWalletDashboard || inputs.isRpc || inputs.isTypescriptSDK }}" >> $GITHUB_OUTPUT
echo "runWallet=${{ inputs.isNightly || inputs.isWallet || inputs.isRpc || inputs.isTypescriptSDK }}" >> $GITHUB_OUTPUT
echo "runEvmBridge=${{ inputs.isNightly || inputs.isEvmBridge || inputs.isIscSDK || inputs.isTypescriptSDK }}" >> $GITHUB_OUTPUT
echo "runKiosk=${{ inputs.isNightly || inputs.isRpc || inputs.isKiosk || inputs.isTypescriptSDK }}" >> $GITHUB_OUTPUT
echo "runAppsBackend=${{ inputs.isNightly || inputs.isAppsBackend }}" >> $GITHUB_OUTPUT
echo "runGraphql=${{ inputs.isNightly || inputs.isGraphQlTransport || inputs.isRpc }}" >> $GITHUB_OUTPUT
echo "runTsSdk=${{ inputs.isNightly || inputs.isTypescriptSDK || inputs.isRpc }}" >> $GITHUB_OUTPUT
echo "runNamesDapp=${{ inputs.isNightly || inputs.isNamesDapp }}" >> $GITHUB_OUTPUT
echo "runNamesDisplay=${{ inputs.isNightly || inputs.isNamesDisplay }}" >> $GITHUB_OUTPUT
echo "runNamesSDK=${{ inputs.isNightly || inputs.isNamesSDK || inputs.isNamesDapp }}" >> $GITHUB_OUTPUT
- name: Download IOTA Binary
run: |
.github/scripts/download-iota-binary.sh
- name: Install Nodejs
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24"
cache: "pnpm"
- name: Install dependencies
id: pnpm-install
run: pnpm install --frozen-lockfile
- name: Set environment variables
run: |
echo "VITE_IOTA_BIN=iota" >> $GITHUB_ENV
echo "E2E_RUN_LOCAL_NET_CMD=iota-localnet start --with-faucet --force-regenesis --with-indexer --with-graphql" >> $GITHUB_ENV
# ─── Test groups ─────────────────────────────────────────────────────────
# All steps below use: if: (!cancelled() && !failure()) && <group-condition>
#
# Fail-fast behavior is controlled by isNightly via continue-on-error:
# - Non-nightly: continue-on-error is false, so any group failure sets the
# job to "failure" state, making !failure() false for subsequent groups —
# the job stops early on the first failure to preserve CI resources.
# - Nightly: continue-on-error is true, so group failures don't affect the
# job state, allowing all other groups to run independently.
#
# Within each group, "Run tests" also checks that its prerequisite steps
# (build, browser install) succeeded. This is necessary for nightly mode,
# where those steps can fail silently via continue-on-error without
# triggering !failure().
#
# Artifact uploads intentionally omit !failure() so failure reports are
# still collected when tests fail. They gate on pnpm-install success as a
# proxy for "foundational setup completed", skipping only if setup never
# finished (in which case there are no reports to collect anyway).
# ─── Wallet build (shared) ───────────────────────────────────────────────
- name: Build Wallet
id: build-wallet
if: (!cancelled() && !failure()) && (steps.conditions.outputs.runDashboard == 'true' || steps.conditions.outputs.runWallet == 'true' || steps.conditions.outputs.runEvmBridge == 'true' || steps.conditions.outputs.runNamesDapp == 'true')
continue-on-error: ${{ inputs.isNightly }}
run: pnpm wallet build:rc
# ─── TS SDK ──────────────────────────────────────────────────────────────
- name: Run TS SDK e2e tests
id: run-ts-sdk
if: (!cancelled() && !failure()) && steps.conditions.outputs.runTsSdk == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @iota/iota-sdk test:e2e'
# ─── Playwright browsers (shared) ────────────────────────────────────────
- name: Install Playwright Browsers
id: install-playwright
if: >-
(!cancelled() && !failure()) && (
steps.conditions.outputs.runExplorer == 'true' ||
steps.conditions.outputs.runDashboard == 'true' ||
steps.conditions.outputs.runWallet == 'true' ||
steps.conditions.outputs.runEvmBridge == 'true' ||
steps.conditions.outputs.runNamesDapp == 'true'
)
continue-on-error: ${{ inputs.isNightly }}
# Any app works, they use the same playwright version
run: pnpm --filter iota-explorer playwright install --with-deps chromium
# ─── RPC / GraphQL ───────────────────────────────────────────────────────
- name: Run RPC/GraphQL compatibility e2e tests
id: run-graphql
if: (!cancelled() && !failure()) && steps.conditions.outputs.runGraphql == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter @iota/graphql-transport test:e2e'
# ─── Apps Backend ────────────────────────────────────────────────────────
- name: Build apps-backend
id: build-apps-backend
if: (!cancelled() && !failure()) && (steps.conditions.outputs.runAppsBackend == 'true' || steps.conditions.outputs.runDashboard == 'true' || steps.conditions.outputs.runWallet == 'true' || steps.conditions.outputs.runNamesDapp == 'true')
continue-on-error: ${{ inputs.isNightly }}
run: pnpm turbo --filter apps-backend build
- name: Run apps-backend e2e tests
id: run-apps-backend
if: (!cancelled() && !failure()) && steps.build-apps-backend.outcome == 'success' && steps.conditions.outputs.runAppsBackend == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm --filter apps-backend test:e2e
# ─── Explorer ────────────────────────────────────────────────────────────
- name: Build explorer
id: build-explorer
if: (!cancelled() && !failure()) && steps.conditions.outputs.runExplorer == 'true'
continue-on-error: ${{ inputs.isNightly }}
env:
IOTA_NETWORKS: '{ "localnet": { "id": "localnet", "name": "Localnet", "url": "http://localhost:9124", "explorer": "http://localhost:3000", "chain": "iota:local", "faucet": "http://localhost:9123" } }'
run: pnpm turbo --filter=iota-explorer build
- name: Run Explorer e2e tests
id: run-explorer
if: (!cancelled() && !failure()) && steps.install-playwright.outcome == 'success' && steps.build-explorer.outcome == 'success' && steps.conditions.outputs.runExplorer == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm --filter iota-explorer playwright test
- name: Upload Explorer test reports
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
if: (!cancelled()) && steps.pnpm-install.outcome == 'success' && steps.conditions.outputs.runExplorer == 'true'
continue-on-error: true
with:
name: playwright-report-explorer
path: apps/explorer/playwright-report/
retention-days: 30
# ─── Dashboard ───────────────────────────────────────────────────────────
- name: Build Dashboard
id: build-dashboard
if: (!cancelled() && !failure()) && steps.conditions.outputs.runDashboard == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm turbo --filter wallet-dashboard build
- name: Run Dashboard e2e tests
id: run-dashboard
if: (!cancelled() && !failure()) && steps.install-playwright.outcome == 'success' && steps.build-dashboard.outcome == 'success' && steps.conditions.outputs.runDashboard == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- pnpm --filter wallet-dashboard playwright test
- name: Upload Dashboard test reports
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
if: (!cancelled()) && steps.pnpm-install.outcome == 'success' && steps.conditions.outputs.runDashboard == 'true'
continue-on-error: true
with:
name: playwright-report-wallet-dashboard
path: apps/wallet-dashboard/playwright-report/
retention-days: 30
# ─── Kiosk ───────────────────────────────────────────────────────────────
- name: Build Kiosk
id: build-kiosk
if: (!cancelled() && !failure()) && steps.conditions.outputs.runKiosk == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm turbo --filter=@iota/kiosk build
- name: Run Kiosk e2e tests
id: run-kiosk
if: (!cancelled() && !failure()) && steps.build-kiosk.outcome == 'success' && steps.conditions.outputs.runKiosk == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm dlx concurrently --kill-others --success command-1 "$E2E_RUN_LOCAL_NET_CMD" 'pnpm --filter=@iota/kiosk test:e2e'
# ─── Wallet ──────────────────────────────────────────────────────────────
- name: Run Local net
if: (!cancelled() && !failure()) && (steps.conditions.outputs.runWallet == 'true' || steps.conditions.outputs.runEvmBridge == 'true')
run: iota-localnet start --force-regenesis --with-faucet --epoch-duration-ms 10000 &
- name: Run Wallet e2e tests
id: run-wallet
if: (!cancelled() && !failure()) && steps.install-playwright.outcome == 'success' && steps.build-wallet.outcome == 'success' && steps.conditions.outputs.runWallet == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- pnpm --filter iota-wallet playwright test
- name: Upload Wallet test reports
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
if: (!cancelled()) && steps.pnpm-install.outcome == 'success' && steps.conditions.outputs.runWallet == 'true'
continue-on-error: true
with:
name: playwright-report-wallet
path: apps/wallet/playwright-report/
retention-days: 30
# ─── EVM Bridge ──────────────────────────────────────────────────────────
- name: Build Evm-bridge
id: build-evm-bridge
if: (!cancelled() && !failure()) && steps.conditions.outputs.runEvmBridge == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm evm-bridge build
- name: Prepare evm-bridge e2e tests (downloads wallet artifact dist)
id: prepare-evm-bridge
if: (!cancelled() && !failure()) && steps.conditions.outputs.runEvmBridge == 'true'
continue-on-error: ${{ inputs.isNightly }}
working-directory: ./apps/evm-bridge
run: pnpm run test:prepare
- name: Run evm-bridge tests
id: run-evm-bridge
if: (!cancelled() && !failure()) && steps.build-evm-bridge.outcome == 'success' && steps.install-playwright.outcome == 'success' && steps.prepare-evm-bridge.outcome == 'success' && steps.conditions.outputs.runEvmBridge == 'true'
continue-on-error: ${{ inputs.isNightly }}
working-directory: ./apps/evm-bridge
run: DEBUG=pw:webserver xvfb-run --auto-servernum pnpm run test:e2e
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
if: (!cancelled()) && steps.pnpm-install.outcome == 'success' && steps.conditions.outputs.runEvmBridge == 'true'
continue-on-error: true
with:
name: playwright-report-evm-bridge
path: apps/evm-bridge/playwright-report/
retention-days: 30
# ─── Names Dapp ──────────────────────────────────────────────────────────
- name: Setup E2E Network for Names
id: setup-names-network
if: (!cancelled() && !failure()) && (steps.conditions.outputs.runNamesDapp == 'true' || steps.conditions.outputs.runNamesSDK == 'true')
continue-on-error: ${{ inputs.isNightly }}
env:
DB_URL: "postgres://postgres:postgrespw@localhost:5432/iota_indexer"
ADMIN_MNEMONIC: "say credit unknown distance behave outside convince embrace planet ginger phrase isolate"
run: .github/scripts/setup-names-network.sh
- name: Inject localnet config and start Names indexer
id: start-names-indexer
if: (!cancelled() && !failure()) && steps.conditions.outputs.runNamesDapp == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: |
pnpm --filter names-dapp inject-localnet
INDEXER_ENV_FILE="$(mktemp)"
pnpm --dir external/names/scripts run envsForIndexer localnet bash "$INDEXER_ENV_FILE"
sed -i 's/^export //' "$INDEXER_ENV_FILE"
docker pull ghcr.io/iotaledger/iota-names/iota-names-indexer:latest
docker rm -f localnet-iota-names-indexer || true
docker run -d \
--name localnet-iota-names-indexer \
--env-file "$INDEXER_ENV_FILE" \
--add-host host.docker.internal:host-gateway \
-p 3030:3030 -p 9189:9189 \
ghcr.io/iotaledger/iota-names/iota-names-indexer:latest \
start \
--node-url=http://host.docker.internal:9000 \
--checkpoint-url=http://host.docker.internal:9000 \
--reset-metrics
- name: Run Names Dapp e2e tests
id: run-names-dapp
if: (!cancelled() && !failure()) && steps.install-playwright.outcome == 'success' && steps.start-names-indexer.outcome == 'success' && steps.build-wallet.outcome == 'success' && steps.build-apps-backend.outcome == 'success' && steps.conditions.outputs.runNamesDapp == 'true'
continue-on-error: ${{ inputs.isNightly }}
env:
ADMIN_MNEMONIC: "say credit unknown distance behave outside convince embrace planet ginger phrase isolate"
NEXT_PUBLIC_NAMES_DAPP_DEFAULT_NETWORK: localnet
NEXT_PUBLIC_NAMES_DAPP_CONFIG: |
{
"localnet": {
"network": "localnet",
"namesDisplayUrl": "http://localhost",
"indexerUrl": "http://localhost:3030",
"baseUrl": "http://localhost:3005"
}
}
run: |
pnpm names-dapp build
cd apps/names
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- env ADMIN_MNEMONIC="$ADMIN_MNEMONIC" pnpm run test:e2e
- name: Upload Names Dapp Playwright report
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
if: (!cancelled()) && steps.pnpm-install.outcome == 'success' && steps.conditions.outputs.runNamesDapp == 'true'
continue-on-error: true
with:
name: playwright-report-names-dapp
path: apps/names/playwright-report/
retention-days: 30
# ─── Names Display ────────────────────────────────────────────────────────
- name: Build Names SDK for display
id: build-names-sdk-display
if: (!cancelled() && !failure()) && steps.conditions.outputs.runNamesDisplay == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm --filter @iota/iota-names-sdk run build
- name: Build and test names-display service
id: run-names-display
if: (!cancelled() && !failure()) && steps.build-names-sdk-display.outcome == 'success' && steps.conditions.outputs.runNamesDisplay == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: |
pnpm --filter names-display run build
pnpm --filter names-display run test
# ─── Names SDK ─────────────────────────────────────────────────────────
- name: Build Names SDK
id: build-names-sdk
if: (!cancelled() && !failure()) && steps.conditions.outputs.runNamesSDK == 'true'
continue-on-error: ${{ inputs.isNightly }}
run: pnpm names-sdk build
- name: Prepare local network & run Names SDK e2e tests
id: run-names-sdk
if: (!cancelled() && !failure()) && steps.build-names-sdk.outcome == 'success' && steps.conditions.outputs.runNamesSDK == 'true'
continue-on-error: ${{ inputs.isNightly }}
env:
IS_CI_JOB: true
NETWORK: localnet
run: |
VITE_IOTA_BIN="iota" pnpm --filter=@iota/iota-names-sdk test:e2e
# ─── Nightly failure report ───────────────────────────────────────────────
- name: Fail job if any nightly test group failed
if: always() && inputs.isNightly
run: |
failed=()
[[ "${{ steps.run-ts-sdk.outcome }}" == 'failure' ]] && failed+=('TS SDK')
[[ "${{ steps.run-graphql.outcome }}" == 'failure' ]] && failed+=('RPC/GraphQL')
[[ "${{ steps.run-apps-backend.outcome }}" == 'failure' ]] && failed+=('Apps Backend')
[[ "${{ steps.run-explorer.outcome }}" == 'failure' ]] && failed+=('Explorer')
[[ "${{ steps.run-dashboard.outcome }}" == 'failure' ]] && failed+=('Dashboard')
[[ "${{ steps.run-kiosk.outcome }}" == 'failure' ]] && failed+=('Kiosk')
[[ "${{ steps.run-wallet.outcome }}" == 'failure' ]] && failed+=('Wallet')
[[ "${{ steps.run-evm-bridge.outcome }}" == 'failure' ]] && failed+=('EVM Bridge')
[[ "${{ steps.run-names-dapp.outcome }}" == 'failure' ]] && failed+=('Names Dapp')
[[ "${{ steps.run-names-display.outcome }}" == 'failure' ]] && failed+=('Names Display')
[[ "${{ steps.run-names-sdk.outcome }}" == 'failure' ]] && failed+=('Names SDK')
if [[ ${#failed[@]} -gt 0 ]]; then
echo "Failed test groups: ${failed[*]}"
exit 1
fi
localnet-cancel:
needs: [localnet]
if: failure() && !inputs.isNightly
runs-on: ubuntu-latest
steps:
- name: Cancel workflow run
run: gh run cancel ${{ github.run_id }} --repo ${{ github.repository }}
env:
GH_TOKEN: ${{ github.token }}