Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/check-code-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ jobs:
*/*/node_modules
key: 2022-05-07-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}}

- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y libudev-dev libusb-1.0-0-dev

- name: Bootstrap
run: |
yarn
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/check_storybook.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
node:
- lts/*
- 22
os:
- macos-latest
- ubuntu-latest
Expand Down
79 changes: 79 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: E2E Tests

on:
push:
pull_request:
workflow_dispatch:

jobs:
playwright-electron:
name: Playwright Electron E2E
runs-on: ubuntu-latest

steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf

- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: "yarn"

- name: Restore
uses: actions/cache@v4
with:
path: |
node_modules
*/*/node_modules
key: 2022-12-21-${{ runner.os }}-${{ hashFiles('**/yarn.lock')}}

- name: Install Linux Dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
libasound2t64 \
libgbm1 \
libgtk-3-0 \
libnss3 \
libudev-dev \
libusb-1.0-0-dev \
libx11-xcb1 \
libxss1 \
xvfb

- name: Install Lerna
run: yarn global add lerna

- name: Bootstrap
run: yarn
env:
CI: false

- name: Run Playwright Electron E2E
run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" yarn test:e2e
env:
CI: true

- name: Upload Playwright report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: playwright-report
if-no-files-found: ignore
retention-days: 14

- name: Upload Playwright test results
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-test-results
path: test-results/e2e
if-no-files-found: ignore
retention-days: 14
2 changes: 1 addition & 1 deletion .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
strategy:
matrix:
node:
- lts/*
- 22
os:
- macos-latest
- ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ node_modules
# testing
/**/coverage
*.snap
playwright-report
test-results

# production
build
Expand Down
41 changes: 41 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Neuron Playwright E2E

This directory contains the release-regression E2E harness for the Electron wallet.

## Run

```bash
yarn install
yarn test:e2e
```

Useful variants:

```bash
yarn test:e2e:headed
yarn test:e2e:ui
```

The root Playwright config starts the Vite UI server on `http://127.0.0.1:3000`. The fixture then launches the Electron main process from `packages/neuron-wallet` after `yarn build:main` has compiled it.

## CI and Release Machines

These tests are intended to run on developer machines and on CI/release workers that provide a graphical session. On Linux CI, run them under Xvfb:

```bash
xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" yarn test:e2e
```

The test config is CI-aware: it uses one worker, enables retries when `CI=true`, keeps reports closed in non-interactive runs, and stores traces, screenshots, and videos in Playwright output directories. A plain headless Node environment without a GUI session or Xvfb is not enough because Playwright launches the Electron app window.

GitHub Actions runs this through `.github/workflows/e2e.yml`. When a run fails, download the `playwright-report` and `playwright-test-results` artifacts from the workflow run. The HTML report shows the failed step, and `test-results/e2e` contains retained screenshots, videos, and Playwright trace files that can be replayed with Playwright trace viewer.

## Test Data

Each test run isolates Electron user data through `XDG_CONFIG_HOME` under the Playwright test output directory. Keep tests independent and avoid relying on a developer's local Neuron profile.

For cases that need wallet, chain, or RPC state, prefer explicit setup helpers in `e2e/fixtures` over manually prepared local data. This keeps release regression repeatable in CI.

## Selector Guidance

Prefer stable user-facing locators (`getByRole`, `getByText`, labels) when the UI exposes them. If a flow needs precision that current markup cannot support, add explicit `data-testid` attributes near the component being tested instead of depending on generated class names or translated copy.
39 changes: 39 additions & 0 deletions e2e/fixtures/electron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import path from 'path'
import { test as base, expect, type Page } from '@playwright/test'
import { _electron as electron, type ElectronApplication } from 'playwright'

type NeuronFixtures = {
electronApp: ElectronApplication
page: Page
}

const repoRoot = path.resolve(__dirname, '../..')
const walletPackageDir = path.join(repoRoot, 'packages/neuron-wallet')

export const test = base.extend<NeuronFixtures>({
electronApp: async ({}, use, testInfo) => {
const xdgConfigHome = testInfo.outputPath('xdg-config-home')
const electronApp = await electron.launch({
args: [walletPackageDir],
cwd: repoRoot,
env: {
...process.env,
BROWSER: 'none',
DISABLE_ESLINT_PLUGIN: 'true',
NODE_ENV: 'development',
XDG_CONFIG_HOME: xdgConfigHome,
},
})

await use(electronApp)
await electronApp.close()
},

page: async ({ electronApp }, use) => {
const page = await electronApp.firstWindow({ timeout: 60_000 })
await page.waitForLoadState('domcontentloaded')
await use(page)
},
})

export { expect }
65 changes: 65 additions & 0 deletions e2e/neuron-functional-regression-plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Neuron 功能测试与发版回归清单

本清单结合现有 Jest/Vitest 覆盖、附件中的历史测试用例,以及 Neuron 当前功能模块整理。目标是把发版回归拆成稳定可自动化的 Playwright E2E、需要链上数据的集成用例、以及仍需人工设备验证的专项用例。

## 覆盖分层

| 层级 | 目标 | 现状 | 建议执行 |
| --- | --- | --- | --- |
| 单元测试 | 纯函数、校验器、格式化、模型计算 | `packages/neuron-ui/src/tests`、`packages/neuron-wallet/tests` 已覆盖大量工具、控制器、服务 | 每次 PR 执行 `yarn test` |
| 集成测试 | wallet service、同步、交易生成、数据库迁移、RPC mock | wallet 包已有 `.intg.test.ts` 和 controller/service 测试 | 每次发版候选执行 `yarn test:ci` |
| E2E 冒烟 | Electron 启动、主窗口、路由、核心页面不崩溃 | 本次新增 Playwright 框架 | 每次发版候选执行 `yarn test:e2e` |
| E2E 业务回归 | 钱包创建/导入、同步、转账、历史、DAO、多签、UDT | 需要补充稳定测试数据和选择器 | 按下面 P0/P1 分批自动化 |
| 人工专项 | Ledger/硬件钱包、系统托盘、安装包升级、平台差异 | 依赖设备或 OS 行为 | 发版前人工验收 |

## P0 自动化回归

| 模块 | 场景 | 断言 |
| --- | --- | --- |
| 启动 | 启动 Electron 并连接本地 Vite UI | 主窗口标题为 Neuron,`#root` 渲染,renderer 无明显崩溃文本 |
| 钱包入口 | 无本地钱包数据时进入钱包向导 | 展示新建、助记词导入、keystore 导入、硬件钱包入口 |
| 新建钱包 | 新建钱包,记录助记词,设置名称和密码 | 钱包创建成功,进入概览,默认网络和同步状态可见 |
| 导入钱包 | 使用固定测试助记词导入钱包 | 导入成功,地址派生结果与固定期望一致 |
| 地址派生 | receiving 第 17 个地址被使用后触发扩展 | receiving 地址补齐到 20 个可用地址,change 地址保持 10 个基础窗口 |
| 同步正确性 | 轻节点测试网/远程测试节点同步同一测试钱包 | 余额、交易数、交易状态与测试链浏览器或 mock RPC 账本一致 |
| 转账表单 | 合法/非法收款地址、金额、小于最小 cell 容量、余额不足 | 合法输入可进入确认页;非法输入展示对应错误 |
| Max 转账 | 单收款地址 Max、多个收款地址中一个 Max | Max 金额等于余额减手续费,Max 字段不可手动改写 |
| 发送交易 | 正确密码发送、错误密码发送 | 正确密码广播成功进入历史;错误密码展示密码错误且不广播 |
| 历史 | 搜索地址/hash、排序、分页、展开详情 | 展示钱包名称、类型、金额、时间、状态,input/output 金额正确 |

## P1 自动化回归

| 模块 | 场景 | 断言 |
| --- | --- | --- |
| 网络 | 轻节点测试网、远程测试节点、主网/测试网切换 | 网络切换后同步任务和钱包监听脚本正确刷新 |
| 数据路径 | 修改 CKB 数据目录、首次同步、已有数据目录 | 提示文案正确,配置文件和数据目录落到预期位置 |
| 资产账户 | 创建 CKB asset account、创建 SUDT account | 创建成功,账户列表和余额展示正确 |
| SUDT | SUDT 收款、转账、迁移、回收 | token id、金额精度、手续费和历史记录正确 |
| Cell 管理 | 选择 cell、合并、消耗、锁定 cell 展示 | input/output 和手续费计算正确 |
| DAO | 存入、可解锁、提取、收益展示 | DAO 阶段、APC、可提取金额、历史详情正确 |
| 多签 | 新建多签地址、导入配置、发送多签交易 | 多签地址同步、签名阈值、交易状态正确 |
| 离线签名 | 导出待签名交易、导入签名、广播 | 文件内容合法,广播成功后历史状态正确 |
| 签名验证 | 普通钱包签名/验签 | 签名结果和验证结果正确 |
| Debug 导出 | 导出 debug 信息 | 归档包含必要日志且不包含敏感私钥 |

## P2/人工专项

| 模块 | 场景 | 说明 |
| --- | --- | --- |
| 硬件钱包 | Ledger 连接、导入、签名、断开重连 | 需要硬件设备和不同 OS 权限 |
| 安装包 | macOS/Windows/Linux 安装、升级、卸载 | 需要平台安装包和系统级校验 |
| 自动更新 | 检查更新、下载、安装 | 需要 release server 或 mock update feed |
| 进程管理 | bundled CKB/light client 启停、异常恢复 | 需要真实二进制和端口冲突场景 |
| 大数据量 | 几千个地址、大量交易历史 | 建议使用固定 mock RPC 或预置链数据 |

## Playwright 自动化路线

1. 保持 `e2e/specs/app-smoke.spec.ts` 作为最小发版门禁,验证 Electron + UI 的启动链路。
2. 在 UI 关键按钮、输入框、列表行补充稳定 `data-testid`,优先覆盖钱包向导、发送页、历史页。
3. 新增 `e2e/fixtures/wallet-data.ts`,提供固定助记词、钱包名称、密码和期望地址。
4. 新增 mock RPC/light-client fixture,先自动化同步、余额、历史展示,再接真实测试网 nightly。
5. 把链上依赖用例拆成 `@mock-chain` 和 `@testnet` 标签,CI 默认跑 mock,发版候选跑 testnet。

## 发版建议

P0 必须通过后再进入候选包验证;P1 覆盖主要资产和交易风险,建议每个 minor 版本完整执行;P2 由发布负责人根据平台和设备资源安排人工验收。
17 changes: 17 additions & 0 deletions e2e/specs/app-smoke.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { test, expect } from '../fixtures/electron'

test.describe('Neuron release smoke', () => {
test('opens the Electron shell against the local UI server', async ({ page }) => {
await expect(page).toHaveTitle(/Neuron/)
await expect(page.locator('#root')).toBeVisible()
await expect(page.locator('body')).not.toContainText('ResizeObserver loop completed with undelivered notifications')
})

test('renders the wallet entry route without a renderer crash', async ({ page }) => {
await page.goto('http://127.0.0.1:3000/#/wizard/')

await expect(page.locator('#root')).toBeVisible()
await expect(page.locator('body')).not.toContainText('Unhandled Runtime Error')
await expect(page.locator('body')).not.toContainText('Cannot read properties of undefined')
})
})
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
"package:test": "yarn build && ./scripts/copy-ui-files.sh && ./scripts/package-for-test.sh",
"test": "cross-env NODE_OPTIONS=--openssl-legacy-provider lerna run --parallel --load-env-files=false test",
"test:ci": "yarn build:main && yarn test",
"test:e2e": "yarn build:main && playwright test",
"test:e2e:headed": "yarn build:main && playwright test --headed",
"test:e2e:ui": "yarn build:main && playwright test --ui",
"lint": "lerna run --stream lint",
"postinstall": "husky install",
"db:chain": "node ./node_modules/.bin/typeorm",
Expand All @@ -42,6 +45,7 @@
},
"devDependencies": {
"@babel/core": "7.27.1",
"@playwright/test": "1.60.0",
"@types/jest": "27.5.2",
"@types/node": "20.10.5",
"@types/npmlog": "7.0.0",
Expand Down
31 changes: 31 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineConfig } from '@playwright/test'

const isCI = !!process.env.CI

export default defineConfig({
testDir: './e2e/specs',
outputDir: './test-results/e2e',
fullyParallel: false,
workers: 1,
timeout: 120_000,
expect: {
timeout: 15_000,
},
forbidOnly: isCI,
retries: isCI ? 2 : 0,
reporter: [
['list'],
['html', { open: 'never', outputFolder: 'playwright-report' }],
],
use: {
trace: 'on-first-retry',
screenshot: 'only-on-failure',
video: 'retain-on-failure',
},
webServer: {
command: 'yarn start:ui',
url: 'http://127.0.0.1:3000',
reuseExistingServer: !isCI,
timeout: 120_000,
},
})
Loading