From a866ecc3b31b1bd4a98c4e66a291a87d42b7efb1 Mon Sep 17 00:00:00 2001 From: Gaubee Date: Mon, 22 Dec 2025 13:14:45 +0800 Subject: [PATCH] feat: add e2e entry script and i18n CI check - Add scripts/e2e.ts as entry point requiring spec file selection - Rename e2e to e2e:all for running all tests - Add i18n check job to CI workflow --- .github/workflows/ci.yml | 31 ++++++++++- package.json | 3 +- scripts/e2e.ts | 117 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 scripts/e2e.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 140264683..2d1b53348 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,6 +85,31 @@ jobs: - name: Unit tests run: pnpm test + # ==================== i18n 检查 ==================== + i18n: + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: ${{ env.PNPM_VERSION }} + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Check i18n keys + run: pnpm i18n:check + # ==================== 构建检查 ==================== build: runs-on: ubuntu-latest @@ -155,7 +180,7 @@ jobs: # ==================== 最终状态检查 ==================== checks: runs-on: ubuntu-latest - needs: [typecheck, test, build, e2e] + needs: [typecheck, test, i18n, build, e2e] if: always() steps: - name: Check results @@ -168,6 +193,10 @@ jobs: echo "Tests failed" exit 1 fi + if [[ "${{ needs.i18n.result }}" == "failure" ]]; then + echo "i18n check failed" + exit 1 + fi if [[ "${{ needs.build.result }}" == "failure" ]]; then echo "Build failed" exit 1 diff --git a/package.json b/package.json index 3d1dc3414..e2ba69fc6 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "storybook": "storybook dev -p 6006", "build-storybook": "storybook build", "typecheck": "tsc --build --noEmit", - "e2e": "playwright test", + "e2e": "bun scripts/e2e.ts", + "e2e:all": "playwright test", "e2e:ui": "playwright test --ui", "e2e:headed": "playwright test --headed", "e2e:update": "playwright test --update-snapshots", diff --git a/scripts/e2e.ts b/scripts/e2e.ts new file mode 100644 index 000000000..dfe1b703a --- /dev/null +++ b/scripts/e2e.ts @@ -0,0 +1,117 @@ +#!/usr/bin/env bun +/** + * E2E 测试入口脚本 + * + * 要求必须传递至少一个 spec 文件,以控制测试时间成本。 + * + * Usage: + * bun scripts/e2e.ts # 运行指定 spec(不需要 .spec.ts 后缀) + * bun scripts/e2e.ts pages guide # 运行多个 spec + * bun scripts/e2e.ts --all # 运行所有测试(等同于 pnpm e2e:all) + * + * Examples: + * bun scripts/e2e.ts pages + * bun scripts/e2e.ts wallet-create wallet-import + * bun scripts/e2e.ts i18n-boot --headed + */ + +import { readdirSync } from 'node:fs' +import { join, resolve, basename } from 'node:path' +import { execSync } from 'node:child_process' + +const ROOT = resolve(import.meta.dirname, '..') +const E2E_DIR = join(ROOT, 'e2e') + +const colors = { + reset: '\x1b[0m', + bold: '\x1b[1m', + dim: '\x1b[2m', + red: '\x1b[31m', + green: '\x1b[32m', + yellow: '\x1b[33m', + cyan: '\x1b[36m', +} + +function getAvailableSpecs(): string[] { + return readdirSync(E2E_DIR) + .filter((f) => f.endsWith('.spec.ts')) + .map((f) => f.replace('.spec.ts', '')) + .sort() +} + +function printUsage() { + const specs = getAvailableSpecs() + + console.log(` +${colors.bold}${colors.cyan}E2E 测试入口${colors.reset} + +${colors.yellow}用法:${colors.reset} + pnpm e2e [spec-name...] 运行指定 spec + pnpm e2e --all 运行所有测试 + pnpm e2e --headed 带界面运行 + pnpm e2e --ui 使用 Playwright UI 模式 + +${colors.yellow}可用的 spec 文件 (${specs.length}):${colors.reset} +${specs.map((s) => ` ${colors.dim}•${colors.reset} ${s}`).join('\n')} + +${colors.yellow}示例:${colors.reset} + pnpm e2e pages + pnpm e2e wallet-create wallet-import + pnpm e2e i18n-boot --headed + +${colors.dim}提示: 为了控制测试时间,请只运行需要的 spec 文件。 +运行全部测试请使用: pnpm e2e:all${colors.reset} +`) +} + +function main() { + const args = process.argv.slice(2) + + // 如果有 --all 参数,运行所有测试 + if (args.includes('--all')) { + const otherArgs = args.filter((a) => a !== '--all') + const cmd = ['playwright', 'test', ...otherArgs].join(' ') + console.log(`${colors.cyan}运行所有 E2E 测试...${colors.reset}\n`) + execSync(cmd, { stdio: 'inherit', cwd: ROOT }) + return + } + + // 分离 spec 名称和 playwright 参数 + const specNames: string[] = [] + const playwrightArgs: string[] = [] + + for (const arg of args) { + if (arg.startsWith('-')) { + playwrightArgs.push(arg) + } else { + specNames.push(arg) + } + } + + // 如果没有传递 spec 名称,显示用法 + if (specNames.length === 0) { + printUsage() + process.exit(1) + } + + const availableSpecs = getAvailableSpecs() + + // 验证 spec 名称 + const invalidSpecs = specNames.filter((s) => !availableSpecs.includes(s)) + if (invalidSpecs.length > 0) { + console.error( + `${colors.red}错误: 未找到以下 spec 文件:${colors.reset} ${invalidSpecs.join(', ')}`, + ) + console.error(`${colors.dim}可用的 spec: ${availableSpecs.join(', ')}${colors.reset}`) + process.exit(1) + } + + // 构建 playwright 命令 + const specFiles = specNames.map((s) => `e2e/${s}.spec.ts`) + const cmd = ['playwright', 'test', ...specFiles, ...playwrightArgs].join(' ') + + console.log(`${colors.cyan}运行 E2E 测试: ${specNames.join(', ')}${colors.reset}\n`) + execSync(cmd, { stdio: 'inherit', cwd: ROOT }) +} + +main()